解决tcp粘包
粘包现象:只有tcp协议才会产生粘包,udp协议不会产生粘包
1、tcp协议下,发送端会采用一个优化算法(Nagle算法),把间隔时间短,数据比较小的包合并到一起,再一起发送过去,造成粘包
2、发送端从缓存区拿数据,但数据过大,只拿取一部分数据,下次再接收时,再把没有接收的数据再拿取过来,造成粘包
对于udp协议来说,是不会发生粘包,接收端设定recvfrom多少个字节,就会接收多少个字节,超过的部分就会舍弃
拆包:当send的数据大于网卡的MTU时,数据会被分片发送,所以一般一次send的数据大小尽量不超过8k
#通过tcp协议远程命令操作服务端
#服务端 import socket import subprocess import struct ip_port = ("127.0.0.1", 8000) back_log = 5 buffer_size = 1024 tcp_server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) tcp_server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) tcp_server.bind(ip_port) tcp_server.listen(back_log) print("--->") while True: conn, addr = tcp_server.accept( ) while True: try: cmd = conn.recv(buffer_size) if not cmd:break print("来自客户端数据",cmd) res = subprocess.Popen(cmd.decode("gbk"),shell=True, stdout=subprocess.PIPE, #stdout:标准输出 stdin=subprocess.PIPE, #stdin:标准输入 stderr=subprocess.PIPE) #stderr:标准错误 err = res.stderr.read() if err: cmd_res = err else: cmd_res = res.stdout.read() if not cmd: cmd_res = "执行成功".encode("gbk") #解决粘包方法1 # length = len(cmd_res) #计算传过来的长度 # conn.send(str(length).encode("utf-8")) #将长度转换成byte,再将长度传回去 # client_ready = conn.recv(buffer_size) # if client_ready == b"ready": # conn.send(cmd_res) #解决粘包方法2 length = len(cmd_res) data_length = struct.pack("i",length) #struct的pack方法直接将长度转换成byte,并且固定为4个字节 conn.send(data_length) conn.send(cmd_res) except Exception: break conn.close() tcp_server.close()
#客户端
import socket import struct from functools import partial ip_port = ("127.0.0.1",8000) back_log = 5 buffer_size = 1024 tcp_client = socket.socket(socket.AF_INET,socket.SOCK_STREAM) tcp_client.connect(ip_port) #链接服务端 while True: cmd = input(">>>") if not cmd:continue elif cmd == "quit":break tcp_client.send(cmd.encode("gbk")) #解决粘包方法1 # length = tcp_client.recv(buffer_size) # tcp_client.send(b"ready") # length = int(length.decode("utf-8")) # recv_size = 0 # recv_msg = b"" # while recv_size < length: #循环拿数据,直至数据长度超过传输回来的数据的长度 # recv_msg += tcp_client.recv(buffer_size) # recv_size = len(recv_msg) #解决粘包方法2 length_data = tcp_client.recv(4) #stuctd的pack方法将长度固定为4个字节 length = struct.unpack("i",length_data)[0] #stuct的unpack方法,再将长度转换回来,注意转换回来是一个元组 # recv_size = 0 # recv_msg = b"" # while recv_size < length: #循环拿数据,直至数据长度超过传输回来的数据的长度 # recv_msg += tcp_client.recv(buffer_size) # recv_size = len(recv_msg) #用来替换循环拿取数据方法 recv_msg = iter(partial(tcp_client.recv,buffer_size),b"") #partial偏移函数,第一个参数是函数,第二个参数是函数的第一个参数 #iter迭代协议,第一个参数是对象,第二参数设定是对象自动迭代,直至迭代到第二个参数结束 #与上面不同这个得到的是可迭代对象,需要先next()迭代数据后再decode解码 cmd_res = recv_msg print("收到服务端发来的消息:",cmd_res.__next__().decode("gbk")) tcp_client.close()
内容补充:
from functools import partial #partial偏移函数需要从functools中导入 def fun(a,b): return a**b f = partial(fun,2) #使用partial有两个参数,第一个是函数,第二个是函数的第一个参数,如例:a=2 print(f(3)) s = [1,2,3,4,5] def test(): return s.pop() s1 = iter(test,2) #iter迭代器协议,第一个参数是迭代对象,第二个是到哪里结束 for i in s1: #如例:这个对象for循环下,自动迭代到2时,迭代停止 print(i)

浙公网安备 33010602011771号