粘包现象
Posted vivi0403
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了粘包现象相关的知识,希望对你有一定的参考价值。
套接字的类型
基于文件类型的套接字家族: AF_UNIX
基于文件的套接字调用的就是底层的文件系统来取数据,两个套接字进程运行在同一机器,可以通过访问同一个文件系统间接完成通信
基于网络类型的套接字家族; AF_INET
TCP socket 通信
服务端配置
import socket
server = socket.socket() #创建一个服务器对象
ip_port = (‘192.168.15.79‘,8080)#创建一个元组
server.bind(ip_port) #将服务器和ip地址进行绑定
server.listen(3) #设置服务器可以同时监听多少个客户端
con,addr =server.accept()#接收客户端发送过来的东西,如果协商成功,就建立相应的隧道
# print(con)#两者协商通信的协议,使用的ip地址和端口号
# #<socket.socket fd=96, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=(‘192.168.15.79‘, 8080), raddr=(‘192.168.15.79‘, 57945)>
print(addr)#(‘192.168.15.79‘, 57945) 连接的客户端的地址和端口号
Client = con.recv(1024)#设置接收的最大的文件的大小,并将接收到的信息进行赋值
print(‘客户端消息:‘,Client)
con.send(b‘you are over‘)#向客户端发送相关的信息,但只能发送呢bytes类型
con.close()
server.close()
客户端配置
import socket
client = socket.socket()#创建客户端
Server_ip = (‘192.168.15.79‘,8080) #设置连接服务器的ip地址和端口
client.connect(Server_ip) #连接服务器
client.send(b‘cisoc‘)#连接完成后向服务端发送信息
cc = client.recv(1024) #设置接收文件的大小
print(cc)
client.close()
使用TCP连接的时候,如果一个TCP服务端与多个客户端连接,但是服务端只能与第一客户端连接,
tcp属于长连接,长连接就是一直占用着这个链接,这个连接的端口被占用了,第二个客户端过来连接的时候,他是可以连接的,但是处于一个占线的状态,就只能等着去跟服务端建立连接,当服务端关闭与第一个客户端的连接之后,重新等待连接才能与第二个客户端进行连接
一对多通信
UDP socket通信
在UDP的配置中主要是两条命令
sendto >>>> 两个参数是1.要发送的信息 目标地址
recvfrom >>> 将接收到的两个东西进行解包,第一个是接收到的信息,发送这条信息的IP地址
type = sock.SOCK_DGRAM 指定为UDP的类型
粘包现象
每个 socket 被创建后,都会分配两个缓冲区,输入缓冲区和输出缓冲区。
每个 socket 被创建后,都会分配两个缓冲区,输入缓冲区和输出缓冲区。socket对象write()/send() 函数处理完数据后,都会数据往输出缓冲区中扔,写入到缓冲区后,socket对象就认为自己处理完数据,函数可以正常返回,再由TCP协议(传输层协议)将数据从输入缓冲区发送到目标机器。
由于TCP协议与 write()/send()是相关的两个个体,TCP协议按照自己的优化机制,对数据进行打包传输,这这就导致了多次写入的数据却被一次性发送,例如第一次发送的是12,第二次发送的是13, TCP在缓冲区读取的时候按照包的大小直接读取,并发送,在对端收到的就是1213连起来的数字
这些I/O缓冲区特性可整理如下:
1.I/O缓冲区在每个TCP套接字中单独存在;
2.I/O缓冲区在创建套接字时自动生成;
3.即使关闭套接字也会继续传送输出缓冲区中遗留的数据;
4.关闭套接字将丢失输入缓冲区中的数据
5.默认的大小是一般是8K
发生粘包的两种情况
1.发送端需要等缓冲区满才发送出去,造成粘包(发送数据时间间隔短,数据小,被当做一个包发出去)
2. 接收方不及时接收缓冲区的包,造成多个包接收(客户端发送了一段数据,服务端只收了一小部分,服务端下次再收的时候还是从缓冲区拿上次遗留的数据,产生粘包)
粘包现象第一种情况:
服务端配置
import socket server = socket.socket() server_ip = (‘192.168.11.70‘,8080) server.bind(server_ip) server.listen(3) conn,addr = server.accept() while 1: conn.send(‘12‘.encode(‘utf-8‘)) conn.send(‘99‘.encode(‘utf-8‘))
客户端配置
import socket client = socket.socket() server_ip = (‘192.168.11.70‘,8080) client.connect(server_ip) while 1: gg = client.recv(1024).decode(‘utf-8‘) ff = client.recv(1024).decode(‘utf-8‘) print(gg,ff)
粘包现象第二种情况:
服务端配置
import socket import subprocess server = socket.socket() ip_port = (‘192.168.11.70‘,8080) server.bind(ip_port) server.listen(2) con,addr = server.accept() # print(addr) while 1 : client_cmd = con.recv(1024).decode(‘utf-8‘) # print(‘*****‘) sub = subprocess.Popen( client_cmd, shell=True, stderr=subprocess.PIPE, stdout=subprocess.PIPE,) # print(‘********‘) cmd_res = sub.stdout.read() print(type(cmd_res)) con.send(cmd_res) print(‘结果长度>>>‘, len(cmd_res))
客户端配置 >>>输入输出比较大的系统命令的时候就会出现粘包的现象 eg:ipconfig /all
mport socket client = socket.socket() ip_port = (‘192.168.11.70‘,8080) client.connect(ip_port) while 1: client_cmd = client.send(input(‘请输入系统指令>>>‘).encode(‘utf-8‘)) cmd_result = client.recv(1024).decode(‘gbk‘) print(cmd_result)
解决粘包现象:
- 获取文件的传输大小,两边统一接收和传输的数据流大小
1.1利用struct模块,发送报头的方式来区分文件的传输
通过使用 struct.pack(‘i‘,len(文件长度)) 返回值是后面的值的bytes类型,恒为4字节
低端在处理的时候先接受4字节,然后通过struct.upack(‘i’,字节) 获得文件的长度
但是一般这样子处理的扩展性比较差,一般是通过在发送端定义一个字典,对所有的要发送的文件进行概括,之后使用json 对字典进行序列化并进行编码,之后再使用struct 进行处理,让对端能够正确接收到字典,接收到字典后,双方在按照商定的大小进行接收和发送 eg:下面的例子
1.2 在发送端直接使用len 计算长度后,直接将数字发送到对端,对端按照发送过来的数字直接设置,进行接收(存在问题是如果文件过大也会导致,缓冲区爆满而出错)
1.3 不管文件大还是小,直接对字节码进行按照商定好的大小进行切片发送()
对端则按照商定好的大小进行接收(扩展差点,)
最优解决方案
服务端配置:
import socket import os import json import struct server = socket.socket() server_ip = (‘192.168.11.70‘,8080) server.bind(server_ip) server.listen(3) conn,addr = server.accept() # print(os.path.getsize(‘D:python全栈第二阶段网络编程day28粘包服务端.py‘)) dic = {‘filename‘:‘粘包服务端‘,‘size‘:os.path.getsize(‘D:python全栈第二阶段网络编程day28粘包服务端.py‘)} dic_JSON = json.dumps(dic).encode(‘utf-8‘) Head_Byte = struct.pack(‘i‘,len(dic_JSON)) conn.send(Head_Byte+dic_JSON) with open(r‘D:python全栈第二阶段网络编程day28粘包服务端.py‘,‘rb‘) as f : while 1: file_Read = f.read(8192) if file_Read: conn.send(file_Read) else:break
客户端配置
import socket import struct import json client = socket.socket() server_ip = (‘192.168.11.70‘,8080) client.connect(server_ip) dic_size = struct.unpack(‘i‘,client.recv(4))[0] dic_json = client.recv(dic_size).decode(‘utf-8‘) dic = json.loads(dic_json) with open(‘py.py‘,‘wb‘) as f: if dic[‘size‘]: size_rev = client.recv(8192) f.write(size_rev) dic[‘size‘] -=len(size_rev)
以上是关于粘包现象的主要内容,如果未能解决你的问题,请参考以下文章
详解啥是 TCP 粘包和拆包现象并演示 Netty 是如何解决的