原理解析图:算法
1 socket通讯过程如图所示:首先客户端将发送内容经过send()方法将内容发送到客户端计算机的内核区,而后由操做系统将内容经过底层路径发送到服务器端的内核区,而后由服务器程序经过recv()方法从服务器端计算机内核区取出数据。
2 所以咱们能够了解到,send方法并非直接将内容发送到服务器端,recv方法也并非直接将从客户端发来的内容接收到服务器程序内存中,而是操做本身机器的内核区。
1 1:当连续发送数据时,因为tcp协议的nagle算法,会将较小的内容拼接成大的内容,一次性发送到服务器端,所以形成粘包 2 3 2:当发送内容较大时,因为服务器端的recv(buffer_size)方法中的buffer_size较小,不能一次性彻底接收所有内容,所以在下一次请求到达时,接收的内容依然是上一次没有彻底接收完的内容,所以形成粘包现象。
也就是说:接收方不知道该接收多大的数据才算接收完毕,形成粘包。shell
思路一:对于第一种粘包产生方式能够在两次send()直接使用recv()来阻止连续发送的状况发生。代码就不用展现了。服务器
思路二:因为产生粘包的缘由是接收方的无边界接收,所以发送端能够在发送数据以前向接收端告知发送内容的大小便可。代码示例以下:socket
方式一:分两次通信分别传递内容大小和内容tcp
服务器端代码:ui
1 # __author__:Kelvin 2 # date:2019/4/28 21:36 3 from socket import * 4 import subprocess 5 6 server = socket(AF_INET, SOCK_STREAM) 7 server.bind(("127.0.0.1", 8000)) 8 server.listen(5) 9 10 while True: 11 conn, addr = server.accept() 12 print("建立了一个新的链接!") 13 while True: 14 try: 15 data = conn.recv(1024) 16 if not data: break 17 res = subprocess.Popen(data.decode("utf-8"), shell=True, stdout=subprocess.PIPE, stdin=subprocess.PIPE, 18 stderr=subprocess.PIPE) 19 err = res.stderr.read() 20 if err: 21 cmd_msg = err 22 else: 23 cmd_msg = res.stdout.read() 24 if not cmd_msg: cmd_msg = "action success!".encode("gbk") 25 length = len(cmd_msg) 26 conn.send(str(length).encode("utf-8")) 27 conn.recv(1024) 28 conn.send(cmd_msg) 29 except Exception as e: 30 print(e) 31 break
客户端代码:spa
1 # __author__:Kelvin 2 # date:2019/4/28 21:36 3 from socket import * 4 5 client = socket(AF_INET, SOCK_STREAM) 6 client.connect(("127.0.0.1", 8000)) 7 while True: 8 inp = input(">>:") 9 if not inp: continue 10 if inp == "quit": break 11 client.send(inp.encode("utf-8")) 12 length = int(client.recv(1024).decode("utf-8")) 13 client.send("ready!".encode("utf-8")) 14 lengthed = 0 15 cmd_msg = b"" 16 while lengthed < length: 17 cmd_msg += client.recv(1024) 18 lengthed = len(cmd_msg) 19 print(cmd_msg.decode("gbk"))
方式二:一次通信直接传递内容大小和内容操作系统
服务器端:code
1 # __author__:Kelvin 2 # date:2019/4/28 21:36 3 from socket import * 4 import subprocess 5 import struct 6 7 server = socket(AF_INET, SOCK_STREAM) 8 server.bind(("127.0.0.1", 8000)) 9 server.listen(5) 10 11 while True: 12 conn, addr = server.accept() 13 print("建立了一个新的链接!") 14 while True: 15 try: 16 data = conn.recv(1024) 17 if not data: break 18 res = subprocess.Popen(data.decode("utf-8"), shell=True, stdout=subprocess.PIPE, stdin=subprocess.PIPE, 19 stderr=subprocess.PIPE) 20 err = res.stderr.read() 21 if err: 22 cmd_msg = err 23 else: 24 cmd_msg = res.stdout.read() 25 if not cmd_msg: cmd_msg = "action success!".encode("gbk") 26 length = len(cmd_msg) 27 conn.send(struct.pack("i", length)) 28 conn.send(cmd_msg) 29 except Exception as e: 30 print(e) 31 break
客户端:server
1 # __author__:Kelvin 2 # date:2019/4/28 21:36 3 from socket import * 4 import struct 5 6 client = socket(AF_INET, SOCK_STREAM) 7 client.connect(("127.0.0.1", 8000)) 8 while True: 9 inp = input(">>:") 10 if not inp: continue 11 if inp == "quit": break 12 client.send(inp.encode("utf-8")) 13 length = struct.unpack("i",client.recv(4))[0] 14 lengthed = 0 15 cmd_msg = b"" 16 while lengthed < length: 17 cmd_msg += client.recv(1024) 18 lengthed = len(cmd_msg) 19 print(cmd_msg.decode("gbk"))
上述两种方式都可以解决粘包问题。