python开发socket套接字:粘包问题&udp套接字&socketserver

时间:2021-10-02 05:59:59

一,发生粘包

服务器端

 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到对端,对端在接收时,先从缓存中取出定长的报头,然后再取真实数据

python开发socket套接字:粘包问题&udp套接字&socketserver

 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))

 

五,socketserver套接字

封装了socket,而且解决了Io阻塞问题

服务端

 1 # socketserver模块多进程,多线程
2 # 无论你开什么都是开线程,线程就有IO,这个模块帮你解决这个IO问题
3
4 # 基础版本,遇到问题,不能无限开线程,而且没有解决IO
5 # 线程开启跟进程开启一样
6 from socket import *
7 from threading import Thread
8 # s=socket(AF_INET,SOCK_STREAM)
9 # s.setsockopt(SOL_SOCKET,SO_REUSEADDR,1) #就是它,在bind前加
10 # s.bind(('127.0.0.1',8090))
11 # s.listen(5)
12 # def talk(conn,addr):
13 # while True: #通信循环
14 # try:
15 # data=conn.recv(1024)
16 # if not data:break
17 # conn.send(data.upper())
18 # except Exception:
19 # break
20 # conn.close()
21 # if __name__ == '__main__':
22 # while True:#链接循环
23 # conn,addr=s.accept()
24 # p = Thread(target=talk,args=(conn,addr))
25 # p.start()
26 # s.close()
27
28 # socketserver模块套接字,自动处理IO问题
29 import socketserver
30 class MyTCPhandler(socketserver.BaseRequestHandler): #必须继承这个类
31 def handle(self):
32 # print(self.request) 打印出来的就是conn
33 # print(self.client_address) 打印出来的就是addr
34 while True:
35 try:
36 data=self.request.recv(1024)
37 if not data:break
38 self.request.send(data.upper())
39 except Exception:
40 break
41 self.request.close()
42 if __name__ == '__main__':
43 server=socketserver.ThreadingTCPServer(('127.0.0.1',8082),MyTCPhandler) #多线程
44 # 三个参数,IP,端口,类
45 # server=socketserver.ForkingTCPServer(('127.0.0.1',8082),MyTCPhandler) #多进程
46 server.allow_reuse_address=True #重用地址
47 server.serve_forever() #永远运行,就是一直开着,相当于while True

 

客户端

 1 from socket import *
2 client=socket(AF_INET,SOCK_STREAM)
3 client.connect(('127.0.0.1',8082))
4
5 while True:
6 msg=input('>>: ').strip()
7 if not msg:continue
8 client.send(msg.encode('utf-8'))
9 data=client.recv(1024)
10 print(data.decode("utf-8"))