粘包现象

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属于长连接,长连接就是一直占用着这个链接,这个连接的端口被占用了,第二个客户端过来连接的时候,他是可以连接的,但是处于一个占线的状态,就只能等着去跟服务端建立连接,当服务端关闭与第一个客户端的连接之后,重新等待连接才能与第二个客户端进行连接

一对多通信

技术分享图片View Code

 

  

 

 

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))
View Code

 

客户端配置

技术分享图片
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)
View Code

 

粘包现象第二种情况:

服务端配置

技术分享图片
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))
View Code

 客户端配置  >>>输入输出比较大的系统命令的时候就会出现粘包的现象 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)
View Code

 

解决粘包现象:

  1. 获取文件的传输大小,两边统一接收和传输的数据流大小

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(rD:python全栈第二阶段网络编程day28粘包服务端.py,rb) as f :
    while 1:
        file_Read = f.read(8192)
        if file_Read:
            conn.send(file_Read)
        else:break
View Code

 

客户端配置

技术分享图片
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)
View Code

 

 
























以上是关于粘包现象的主要内容,如果未能解决你的问题,请参考以下文章

socket网络编程:粘包现象以及解决方法(代码完善)

网络编程基础之粘包现象

详解啥是 TCP 粘包和拆包现象并演示 Netty 是如何解决的

透过现象看本质,我找到了Netty粘包与半包的这几种解决方案。

Netty进阶——粘包与半包(现象分析)

Netty进阶——粘包与半包(现象分析)