网络编程~~~~粘包

一 两种发生粘包的情况:

  1. 接收方没有及时接受缓存区的包,造成多个包接收(客户端发送了一段数据,服务端只收了一小部分,服务端下次再收的时候还是从缓冲区拿上次遗留的数据,产生粘包)
  2. 发送端需要等缓冲区满才发送出去,造成粘包 (发送数据时间间隔很短,数据也很小,会合到一起,造成粘包)

二 粘包的解决方案:

客户端循环接收,拿到接收数据的字节数的长度,用作判断结束循环的条件

调用struct模块,把一个类型变成固定长度的bytes类型

import struct
# 将一个数字转化成等长度的bytes类型。
ret = struct.pack('i', 183346)
print(ret, type(ret), len(ret))

# 通过unpack反解回来
ret1 = struct.unpack('i',ret)[0]
print(ret1, type(ret1))
# 但是通过struct 处理不能处理太大,数字太大会报错

2.1 low版解决粘包方案(远程执行命令)

服务器端:

import subprocess
import socket
import struct
phone = socket.socket()
phone.bind(('127.0.0.1',8848))
phone.listen()
print('已开机')
while 1:
    conn, addr = phone.accept()
    while 1:
        try:
            from_client_data = conn.recv(1024).decode("utf-8")
            if from_client_data.upper() == 'Q':
                print('客户端已经正常退出!')
                break
            else:
                print(f'来自客户端的信息:{from_client_data}')
                obj = subprocess.Popen(from_client_data,
                                       shell=True,                                     stdout=subprocess.PIPE,
                              stderr=subprocess.PIPE,
                                       )
                to_client_data = obj.stdout.read() + obj.stderr.read()
                data_size = len(to_client_data)
                print(data_size)
                ret = struct.pack('i',data_size)
                conn.send(ret)
                conn.send(to_client_data)
        except ConnectionResetError:
            print('客户端异常断开连接!')
            break
    conn.close()
phone.close()

客户端:

import socket
import struct
phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
phone.connect(('127.0.0.1',8848))
while 1:
    try:
        data = input('>>>')
        if not data:
            print('内容不能为空!')
            continue
        phone.send(data.encode('utf-8'))
        if data.upper() == "Q":
            break
        else:
            data_size = phone.recv(4)
            ret = struct.unpack('i',data_size)[0]
            data = b''
            while len(data) < ret:
                from_server_data = phone.recv(1024)
                data += from_server_data
            print(len(data))
            print(f'来自服务器的消息:{data.decode("gbk")}')
    except ConnectionResetError:
        print('服务器异常断开连接')
        break
phone.close()

2.2 自定制报头:

服务器端:

# 1. 自定义报头
head_dic = {
    'file_name': 'test1',
    'md5': 6567657678678,
    'total_size': total_size,
}
# 2. json形式的报头
head_dic_json = json.dumps(head_dic)

# 3. bytes形式报头
head_dic_json_bytes = head_dic_json.encode('utf-8')

# 4. 获取bytes形式的报头的总字节数
len_head_dic_json_bytes = len(head_dic_json_bytes)

# 5. 将不固定的int总字节数变成固定长度的4个字节
four_head_bytes = struct.pack('i', len_head_dic_json_bytes)

# 6. 发送固定的4个字节
conn.send(four_head_bytes)

# 7. 发送报头数据
conn.send(head_dic_json_bytes)

# 8. 发送总数据
conn.send(result)

客户端:

# 1. 接收固定长度的4个字节
head_bytes = phone.recv(4)

# 2. 获得bytes类型字典的总字节数
len_head_dic_json_bytes = struct.unpack('i',head_bytes)[0]

# 3. 接收bytes形式的dic数据
head_dic_json_bytes = phone.recv(len_head_dic_json_bytes)

# 4. 转化成json类型dic
head_dic_json = head_dic_json_bytes.decode('utf-8')

# 5. 转化成字典形式的报头
head_dic = json.loads(head_dic_json)

三 基于网络的UDP协议的socket

客户端:

import socket
server = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
server.bind(('192.168.14.198',9000))

while 1:
    from_client_data = server.recvfrom(1024)  # 阻塞,等待客户来消息
    print(f'33[1;35;0m来自客户端{from_client_data[1]}: {from_client_data[0].decode("utf-8")} 33[0m')
    to_client_data = input('>>>').strip()
    server.sendto(to_client_data.encode('utf-8'),from_client_data[1])
server.close()
# 1. 基于udp协议的socket无须建立管道,先开启服务端或者客户端都行.
# 2. 基于udp协议的socket接收一个消息,与发送一个消息都是无连接的.
# 3. 只要拿到我的ip地址和端口就都可以给我发消息,我按照顺序接收消息.

客户端:

import socket
client = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)

while 1:
    to_server_data = input('>>>:').strip()
    client.sendto(to_server_data.encode('utf-8'),('127.0.0.1',9000))
    data,addr = client.recvfrom(1024)
    print(f'来自服务端{addr}消息:{data.decode("utf-8")}')
原文地址:https://www.cnblogs.com/lav3nder/p/11802148.html