在使用TCP协议进行数据传输的时候,会有以下问题出现。
client:
import socket
sk = socket.socket()
sk.connect(("127.0.0.1", 8101))
# 连续发送数据
s = "我爱你"
sk.send(s.encode("utf-8"))
sk.send(s.encode("utf-8"))
print("发送完毕")
sk.close()
server:
import socket
sk = socket.socket()
sk.bind(("127.0.0.1", 8101))
sk.listen()
conn, addr = sk.accept()
msg1 = conn.recv(1024)
print(msg1.decode("utf-8"))
msg2 = conn.recv(1024)
print(msg2.decode("utf-8"))
sk.close()
运行结果
我们发现,打印来的效果是两个数据包合在一起了。为什么会这样呢? 在数据传输的时候客户端发送两次数据。这两个数据并不是直接发送出去的。首先会存放在缓冲区。等缓冲区数据装满或者经过一段时间后。会把缓冲区中的数据一起发送出去。 这就导致了一个很坑的现象。明明是两次发送的数据。被合在了一起。这就是典型的黏包现象。
注意,黏包现象只有TCP才会出现。UDP是不会出现黏包的。因为UDP的不连续性。每次发送的数据都会立刻打包成数据包然后发出去。数据包与数据包之间是有边界隔离的。你可以认为是一个sendto对应一个recvfrom。因此UDP不会出现黏包.
很简单。之所以出现黏包就是因为数据没有边界,直接把两个包混合成了一个包。那么我可以在发送数据的时候,指定边界,告诉对方,我接下来这个数据包有多大。 对面接收数据的时候呢,先读取该数据包的大小,然后再读取数据,就不会产生黏包了。
通俗的说,发送数据的时候制定数据的格式:长度+数据。 接收的时候就知道有多少是当前这个数据包的大小了。也就相当于定义了分隔边界了。
client:
import socket
sk = socket.socket()
sk.connect(("127.0.0.1", 8101))
# 连续发送数据
s = "我爱你"
bs = s.encode("utf-8")
# 计算数据长度. 格式化成4位数字
bs_len = format(len(bs), "04d").encode("utf-8")
# 发送数据之前. 先发送长度
# 整个数据包: 0009\x\x\x\x\x\x...
sk.send(bs_len)
sk.send(bs)
sk.send(bs_len)
sk.send(bs)
print("发送完毕")
sk.close()
server:
import socket
sk = socket.socket()
sk.bind(("127.0.0.1", 8101))
sk.listen()
conn, addr = sk.accept()
# 整个数据包: 0009\x\x\x\x\x\x...
# 接收4个字节. 转换成数字
bs_len = int(conn.recv(4).decode(‘Utf-8‘))
# 读取数据
msg1 = conn.recv(bs_len)
print(msg1.decode("utf-8"))
bs_len = int(conn.recv(4).decode(‘Utf-8‘))
msg2 = conn.recv(bs_len)
print(msg2.decode("utf-8"))
sk.close()
如果每次发送数据都要经过这么一次,属实有点儿累。没关系,python提供了一个很好用的模块来帮我们解决这个恶心的问题
ret = struct.pack("i", 123456789)
print(ret)
print(len(ret)) # 4 不论数字大小, 定死了4个字节
# 把字节还原回数字
bs = b‘\x15\xcd[\x07‘
num = struct.unpack("i", bs)[0]
print(num)
client:
import socket
import struct
sk = socket.socket()
sk.connect(("127.0.0.1", 8123))
msg_bs = "我爱你".encode("utf-8")
msg_struct_len = struct.pack("i", len(msg_bs))
# 发一次
sk.send(msg_struct_len)
sk.send(msg_bs)
# 发两次
sk.send(msg_struct_len)
sk.send(msg_bs)
server:
import socket
import struct
sk = socket.socket()
sk.bind(("127.0.0.1", 8123))
sk.listen()
conn, addr = sk.accept()
# 接收一个数据包
msg_struct_len = conn.recv(4)
msg_len = struct.unpack("i", msg_struct_len)[0]
data = conn.recv(msg_len)
print(data.decode(‘utf-8‘))
# 接收第二个数据包
msg_struct_len = conn.recv(4)
msg_len = struct.unpack("i", msg_struct_len)[0]
data = conn.recv(msg_len)
print(data.decode(‘utf-8‘))
看着还是别扭?提取一个模块试试看
my_socket_util
import struct
def my_send(sk, msg):
msg_bs = msg.encode("utf-8")
msg_struct_len = struct.pack("i", len(msg_bs))
sk.send(msg_struct_len)
sk.send(msg_bs)
def my_recv(sk):
# 接收一个数据包
msg_struct_len = sk.recv(4)
msg_len = struct.unpack("i", msg_struct_len)[0]
data = sk.recv(msg_len)
return data.decode("utf-8")
client:
import socket
import my_socket_util as msu
sk = socket.socket()
sk.connect(("127.0.0.1", 8123))
msu.my_send(sk, "我爱你")
msu.my_send(sk, "我爱你")
server:
import socket
import my_socket_util as msu
sk = socket.socket()
sk.bind(("127.0.0.1", 8123))
sk.listen()
conn, addr = sk.accept()
print(msu.my_recv(conn))
print(msu.my_recv(conn))
原文:https://www.cnblogs.com/pure3417/p/14698434.html