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

一,发生粘包

服务器端

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

五,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"))
原文地址:https://www.cnblogs.com/jokerbj/p/7422349.html