python开发socket网络编程基础:粘包问题&udp套接字

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了python开发socket网络编程基础:粘包问题&udp套接字相关的知识,希望对你有一定的参考价值。

一,发生粘包

服务器端

 1 from socket import *
 2 phone=socket(AF_INET,SOCK_STREAM)        #套接字
 3 phone.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)   #解决端口占用
 4 phone.bind((127.0.0.1,8080))   #绑定端口和Ip到套接字
 5 phone.listen(5)
 6 conn,client_addr=phone.accept()   #等待tcp接受
 7 
 8 
 9 # data1=conn.recv(10)
10 # print(‘data1: ‘,data1)
11 # data2=conn.recv(4)
12 # print(‘data2:‘,data2)
13 #接收方不及时接收缓冲区的包,造成多个包接收(客户端发送了一段数据,服务端只收了一小部分,
14 #服务端下次再收的时候还是从缓冲区拿上次遗留的数据,产生粘包)

客户端

1 from socket import *
2 import time
3 phone=socket(AF_INET,SOCK_STREAM)
4 phone.connect((127.0.0.1,8080))
5 
6 
7 # phone.send(‘helloworld‘.encode(‘utf-8‘))
8 # phone.send(‘egon‘.encode(‘utf-8‘))
9 #发送端需要等缓冲区满才发送出去,造成粘包(发送数据时间间隔很短,数据了很小,会合到一起,产生粘包)

 

二,用struct模块解决粘包问题

为字节流加上自定义固定长度报头,报头中包含字节流长度,然后一次send到对端,对端在接收时,先从缓存中取出定长的报头,然后再取真实数据

技术分享

 1 #_*_coding:utf-8_*_
 2 #http://www.cnblogs.com/coser/archive/2011/12/17/2291160.html
 3 __author__ = Linhaifeng
 4 import struct
 5 import binascii
 6 import ctypes
 7 
 8 values1 = (1, abc.encode(utf-8), 2.7)
 9 values2 = (defg.encode(utf-8),101)
10 s1 = struct.Struct(I3sf)
11 s2 = struct.Struct(4sI)
12 
13 print(s1.size,s2.size)
14 prebuffer=ctypes.create_string_buffer(s1.size+s2.size)
15 print(Before : ,binascii.hexlify(prebuffer))
16 # t=binascii.hexlify(‘asdfaf‘.encode(‘utf-8‘))
17 # print(t)
18 
19 
20 s1.pack_into(prebuffer,0,*values1)
21 s2.pack_into(prebuffer,s1.size,*values2)
22 
23 print(After pack,binascii.hexlify(prebuffer))
24 print(s1.unpack_from(prebuffer,0))
25 print(s2.unpack_from(prebuffer,s1.size))
26 
27 s3=struct.Struct(ii)
28 s3.pack_into(prebuffer,0,123,123)
29 print(After pack,binascii.hexlify(prebuffer))
30 print(s3.unpack_from(prebuffer,0))
31 
32 关于struct的详细用法

 

服务器端

 1 import socket
 2 import subprocess
 3 import struct
 4 phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM) #买手机
 5 phone.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) #就是它,在bind前加
 6 phone.bind((127.0.0.1,8088)) #绑定手机卡
 7 phone.listen(5) #开机
 8 
 9 print(starting...)
10 while True: #链接循环
11     conn,client_addr=phone.accept() #等电话 (链接,客户的的ip和端口组成的元组)
12     print(-------->,conn,client_addr)
13 
14     #收,发消息
15     while True:#通信循环
16         try:
17             cmd=conn.recv(1024)
18             print(cmd)
19             if not cmd:break #针对linux
20             #执行cmd命令,拿到cmd的结果,结果应该是bytes类型
21             #。。。。
22             res = subprocess.Popen(cmd.decode(utf-8), shell=True,
23                                    stdout=subprocess.PIPE,
24                                    stderr=subprocess.PIPE)
25             stdout=res.stdout.read()
26             stderr=res.stderr.read()
27             print(stdout)
28 
29             #先发报头(转成固定长度的bytes类型)
30             header = struct.pack(i,len(stdout)+len(stderr))
31             print(header)
32             conn.send(header)
33             #再发送命令的结果
34             conn.send(stdout)
35             conn.send(stderr)
36         except Exception:
37             break
38     conn.close() #挂电话
39 phone.close() #关机

客户端

 1 import socket
 2 import struct
 3 phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM) #买手机
 4 phone.connect((127.0.0.1,8088)) #绑定手机卡
 5 
 6 #发,收消息
 7 while True:
 8     cmd=input(>>: ).strip()
 9     if not cmd:continue
10 
11     phone.send(cmd.encode(utf-8))
12     #先收报头
13     header_struct=phone.recv(4)
14     print(header_struct)
15     unpack_res = struct.unpack(i, header_struct)
16     total_size=unpack_res[0]
17     print(total_size)
18 
19     #再收数据
20     recv_size=0 #10241=10240+1
21     total_data=b‘‘
22     while recv_size < total_size:
23         recv_data=phone.recv(1024)
24         print(recv_data)
25         recv_size+=len(recv_data)
26         print(recv_size)
27         total_data+=recv_data
28         print(total_data)
29     # else:
30     print(total_data.decode(gbk))
31 phone.close()

 

三,大文件粘包问题

服务器端

 1 import socket
 2 import subprocess
 3 import struct
 4 import json
 5 phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM) #买手机
 6 phone.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) #就是它,在bind前加
 7 phone.bind((127.0.0.1,8082)) #绑定手机卡
 8 phone.listen(5) #开机
 9 
10 print(starting...)
11 while True: #链接循环
12     conn,client_addr=phone.accept() #等电话 (链接,客户的的ip和端口组成的元组)
13     print(-------->,conn,client_addr)
14 
15     #收,发消息
16     while True:#通信循环
17         try:
18             cmd=conn.recv(1024)
19             if not cmd:break #针对linux
20             #执行cmd命令,拿到cmd的结果,结果应该是bytes类型
21             #。。。。
22             res = subprocess.Popen(cmd.decode(utf-8), shell=True,
23                                    stdout=subprocess.PIPE,
24                                    stderr=subprocess.PIPE)
25             stdout=res.stdout.read()
26             stderr=res.stderr.read()
27             #制作报头
28             header_dic = {
29                 total_size: len(stdout)+len(stderr),
30                 filename: None,
31                 md5: None}
32 
33             header_json = json.dumps(header_dic)
34             header_bytes = header_json.encode(utf-8)
35             #发送阶段
36             #先发报头长度
37             conn.send(struct.pack(i,len(header_bytes)))
38             #再发报头
39             conn.send(header_bytes)
40 
41             #最后发送命令的结果
42             conn.send(stdout)
43             conn.send(stderr)
44         except Exception:
45             break
46     conn.close() #挂电话
47 phone.close() #关机

客户端

 1 import socket
 2 import struct
 3 import json
 4 phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM) #买手机
 5 phone.connect((127.0.0.1,8082)) #绑定手机卡
 6 
 7 #发,收消息
 8 while True:
 9     cmd=input(>>: ).strip()
10     if not cmd:continue
11 
12     phone.send(cmd.encode(utf-8))
13     #先收报头的长度
14     header_len=struct.unpack(i,phone.recv(4))[0]
15 
16     #再收报头
17     header_bytes=phone.recv(header_len)
18     header_json=header_bytes.decode(utf-8)
19     header_dic=json.loads(header_json)
20     total_size=header_dic[total_size]
21 
22     #最后收数据
23     recv_size=0 #10241=10240+1
24     total_data=b‘‘
25     while recv_size < total_size:
26         recv_data=phone.recv(1024)
27         recv_size+=len(recv_data)
28         total_data+=recv_data
29     print(total_data.decode(gbk))
30 phone.close()

 

四,udp套接字

服务器端

 1 from socket import *
 2 udp_server=socket(AF_INET,SOCK_DGRAM)
 3 udp_server.bind((127.0.0.1,8088))
 4 
 5 while True:
 6     msg,client_addr=udp_server.recvfrom(1024)
 7     print(has recv %s %msg)
 8     udp_server.sendto(msg.upper(),client_addr)
 9     print(has send)
10 udp_server.close()

客户端

 1 from socket import *
 2 udp_client=socket(AF_INET,SOCK_DGRAM)
 3 
 4 while True:
 5     msg=input(>>: ).strip()
 6     udp_client.sendto(msg.encode(utf-8),(127.0.0.1,8088))
 7     print(has send)
 8     # res,server_addr=udp_client.recvfrom(1024)
 9     # print(‘====>‘,res.decode(‘utf-8‘))
10 
11 udp_client.close()

 

udp 套接字不会发生粘包

服务器端

1 from socket import *
2 udp_server=socket(AF_INET,SOCK_DGRAM)
3 udp_server.bind((127.0.0.1,8089))
4 
5 msg1,client_addr=udp_server.recvfrom(5)
6 print(msg1)
7 
8 msg2,client_addr=udp_server.recvfrom(5)
9 print(msg2)

客户端

1 from socket import *
2 udp_client=socket(AF_INET,SOCK_DGRAM)
3 
4 udp_client.sendto(hello.encode(utf-8),(127.0.0.1,8089))
5 udp_client.sendto(world.encode(utf-8),(127.0.0.1,8089))

 

以上是关于python开发socket网络编程基础:粘包问题&udp套接字的主要内容,如果未能解决你的问题,请参考以下文章

Python基础 - 第八天 - Socket编程进阶

python基础之socket编程part2---粘包和并发

python基础之socket编程

python基础之socket编程

python基础之socket编程

python基础之socket编程