刘刚刚的个人博客

python基础-基于tcp/ip、udp的Scoket使用


操作系统封装了应用层下的OSI协议并抽象出了Sokcet,让应用层的通讯不用关心底层的实现。

Socket是应用层与TCP/IP协议族通信的中间软件抽象层,是操作系统封装完下层协议后封装出来的一组接口。

基于tcp/ip的Scoket使用

通过基于tcp/ip的socket可以进行internet上可靠的通讯

服务端代码:

import socket

# 1.调用socket方法创建一个socket对象
# socket.AF_INET 代表进行internet间的通讯
# socket.SOCK_STREAM 代表使用tcp/ip的流式协议
soc=socket.socket(socket.AF_INET,socket.SOCK_STREAM)

# 2.设置监听的端口
soc.bind(('127.0.0.1',8081)) # 0-65535, 1024以前的都被系统保留使用

# 3.设置半连接池的大小
soc.listen(5) 

# 4.阻塞在此,直到接受客户端的消息
# 接收到消息时,会获得消息的内容和发送方的链接对象
conn,client_addr=phone.accept()

# 5.设置最大接受数据的量
data=conn.recv(1024) # 最大接收的数据量为1024Bytes,收到的是bytes类型
# 字符编码需要根据客户端的编码进行解码
print("客户端发来的消息:",data.decode('utf-8'))

# 6.给客户端发送消息
conn.send(data.upper())

# 7.关闭对应客户端的链接
conn.close()

# 8.关闭socket
soc.close()

客户端代码:

在客户端

import socket

# 1.创建socket对象
soc=socket.socket(socket.AF_INET,socket.SOCK_STREAM) 

# 2.绑定服务端的地址
soc.connect(('127.0.0.1',8081))

# 3.发送消息
soc.send('hello 哈哈哈'.encode('utf-8'))

# 4.接受返回的消息
data=phone.recv(1024)
print(data.decode('utf-8'))

# 5.关闭连接(必选的回收资源的操作)
soc.close()

黏包问题

产生黏包问题的原因:

  1. 发送端发送的数据时间间隔很小,由于系统会使用nagle算法对tcp进行优化,会导致,多条数据一块发送给服务端
  2. 接受端没有及时接受缓冲区的包,导致读取时取出来的为上次没接受完的数据

解决方式:

增加数据的头信息:数据信息的长度,数据信息

# 服务端解决黏包问题

import struct
# 1、生成头信息
header_dic={
    "filename":"a.txt",
    # 真实数据的长度
    "total_size":total_size, 
    "md5":"xxxxx"
}

json_str = json.dumps(header_dic)
json_str_bytes = json_str.encode('utf-8')


# 2、获取头的长度,并发送
# 使用struct可以使用固定的4个字节存储头的长度
x=struct.pack('I',len(json_str_bytes)) 
conn.send(x)

# 3、发头信息
conn.send(json_str_bytes)
# 4、再发真实的数据
conn.send(stdout_res)

在客户端的处理步骤:1. 接收4个字节 2.根据四个自己的信息,读出头部信息 3.根据头部信息,读出相应长度的数据。

tip:

  1. 如果客户端没有正常关闭scoket(基于流传输)会发生什么?

    则连接会出现错误,则在unix系统中会不停的收到空数据,在windows中会报错,如果持续监听,需要进行相关处理。

  2. 关闭socket后端口不会马上释放,因为操作系统不会立即回收端口。

基于UDP的Socket使用

udp 协议不会保证数据的可靠传输,但拥有更快的传输速度。udp的数据在接收时,如果一次没有接收完,那么剩余的数据会被丢弃

服务端代码

import socket

# 1. 创建socket对象
# 使用数据报协议=》udp
server=socket.socket(socket.AF_INET,socket.SOCK_DGRAM) 

# 2. 绑定服务端口
server.bind(('127.0.0.1',8081))

# 3. 接受数据
data,client_addr=server.recvfrom(1024)

# 4. 给客户端发送消息
server.sendto(data.upper(),client_addr)
    
# 5. 关闭连接    
server.close()

客户端

import socket

# 1. 创建socket对象
client=socket.socket(socket.AF_INET,socket.SOCK_DGRAM) 

# 2. 发送数据
client.sendto('hello'.encode('utf-8'),('127.0.0.1',8081))

# 3. 接受数据
res=client.recvfrom(1024)

# 4. 关闭连接
client.close()

udp协议不存在黏包问题,如果服务端只接受了数据的一部分,那么剩余部分将被抛弃

tip:

  1. 如果客户端没有正常关闭scoket(基于包传输)会发生什么?

    不会影响服务端的接收数据,udp协议中,客户端和服务端是没有关联的

流传输协议、包传输协议:

流传输协议:

在流式协议中,如果A要给B发送数据,那么A可以分多次发,然后B一次接受或者多次接受,A中如果使用socket发送的为空数据,那么操作系统将不会发送该数据。

包传输协议:

在包传输协议中,如果为空数据,那么系统也会将空数据进行包装发送给接收端。

other

nagle算法:Nagle算法的基本定义是任意时刻,最多只能有一个未被确认的小段。 所谓“小段”,指的是小于MSS尺寸的数据块,所谓“未被确认”,是指一个数据块发送出去后,没有收到对方发送的ACK确认该数据已收到。这种机制减少了网络中传输的拥塞。

域名解析包括递归和迭代

scoket 封装了 传输层、网络层 数据链路层,

socket 由操作系统完成,

物理层 执行的是发送和接受

那么应用层程序结束后,链接资源是什么时候回收的呢?

我的名片

昵称:shuta

职业:后台开发(php)

邮箱:648949076@qq.com

站点信息

建站时间: 2020/2/19
网站程序: ANTD PRO VUE + TP6.0
晋ICP备18007778号