一,发生粘包
服务器端
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"))