TCP协议
TCP(transport control protocol,传输控制协议)是面向连接的,面向流的,提供高可靠性服务。收发两端(客户端和服务器端)都要有一一成对的socket,因此,发送端为了将多个发往接收端的包,更有效的发到对方,使用了优化方法(Nagle算法),将多次间隔较小且数据量小的数据,合并成一个大的数据块,然后进行封包。这样,接收端,就难于分辨出来了,必须提供科学的拆包机制。 即面向流的通信是无消息保护边界的。
UDP协议
UDP(user datagram protocol,用户数据报协议)是无连接的,面向消息的,提供高效率服务。不会使用块的合并优化算法,, 由于UDP支持的是一对多的模式,所以接收端的skbuff(套接字缓冲区)采用了链式结构来记录每一个到达的UDP包,在每个UDP包中就有了消息头(消息来源地址,端口等信息),这样,对于接收端来说,就容易进行区分处理了。 即面向消息的通信是有消息保护边界的。
tcp是基于数据流的,于是收发的消息不能为空,这就需要在客户端和服务端都添加空消息的处理机制,防止程序卡住,而udp是基于数据报的,即便是你输入的是空内容(直接回车),那也不是空消息,udp协议会帮你封装上消息头
TCP粘包问题
出现的原因
1、tcp是流式协议,数据像水流一样粘在一起,没有任何边界区分
2、收数据没收干净,有残留,就会跟下次结果混淆在一起
解决粘包问题的核心法门
每次都收干净,不要有任何残留
解决粘包问题的思路
1、拿到数据的总大小
2、recv_size = 0,循环接收,每接收一次,recv_size+=接收长度
3、直到recv_size = total_size,结束循环
TCP客户端与服务端代码(解决粘包问题终极版)
客户端
import json
import struct
from socket import *
client = socket(AF_INET, SOCK_STREAM)
client.connect(('127.0.0.1', 8080))
while True:
cmd = input('请输入命令>>:').strip()
if len(cmd) == 0: continue
client.send(cmd.encode('utf-8'))
# 接收端
# 1、先收4个字节,从中提取接下来要收的头的长度
x = client.recv(4)
header_len = struct.unpack('i', x)[0]
# 2、接收头,并解析
json_str_bytes = client.recv(header_len)
json_str = json_str_bytes.decode('utf-8')
header_dic = json.loads(json_str)
print(header_dic)
total_size = header_dic["total_size"]
# 3、接收真实的数据
recv_size = 0
while recv_size < total_size:
recv_data = client.recv(1024)
recv_size += len(recv_data)
print(recv_data.decode('utf-8'), end='')
else:
print()
服务端
# 服务端应该满足两个特点:
# 1、一直对外提供服务
# 2、并发地服务多个客户端
import json
import struct
import subprocess
from socket import *
server = socket(AF_INET, SOCK_STREAM)
server.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1) # 就是它,在bind前加
server.bind(('127.0.0.1', 8080))
server.listen(5)
# 服务端应该做两件事
# 第一件事:循环地从板连接池中取出链接请求与其建立双向链接,拿到链接对象
while True:
conn, client_addr = server.accept()
# 第二件事:拿到链接对象,与其进行通信循环
while True:
try:
cmd = conn.recv(1024)
if len(cmd) == 0: break
obj = subprocess.Popen(cmd.decode('utf-8'),
shell=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE
)
stdout_res = obj.stdout.read()
stderr_res = obj.stderr.read()
total_size = len(stdout_res) + len(stderr_res)
# 1、制作头
header_dic = {
"filename": "a.txt",
"total_size": total_size,
"md5": "123123xi12ix12"
}
json_str = json.dumps(header_dic)
json_str_bytes = json_str.encode('utf-8')
# 2、先把头的长度发过去
x = struct.pack('i', len(json_str_bytes)) # i 类型储存的长度绝对够用
conn.send(x)
# 3、发头信息
conn.send(json_str_bytes)
# 4、再发真实的数据
conn.send(stdout_res)
conn.send(stderr_res)
except Exception:
break
conn.close()
标签:udp,网络,tcp,json,len,str,import,recv,size
From: https://www.cnblogs.com/piggthird/p/17779535.html