网络编程中的服务,客户端与服务端交互命令的时候,有一种现像是粘包,(接收1024个数据)而发送的太多,一次收不完,会收几次,造成信息不正确
粘包:根本原因是TCP协议是一种面向流的协议,
在客户端粘包,服务端一次发送过多的信息(如cat /etc/passwd),你客户端只接受1024个字符
在服务端粘包,连续两次发送比较短的数据,因为cpu的速度过快,所以,在缓存中直接连在一起,(因为比网络IO快)
就造成了粘包现像
http://www.cnblogs.com/linhaifeng/articles/6129246.html
subprocess模块 struct模块 参考
struct.pack包,
报头
一定要固定长度
包含对将要发送数据的描述信息
socket套接字只能发送bytes格式
编码只能对字符不能编数字所以就有了,struct模块是把数字转成固定长度的bytes
json struct模块解决粘包现像
步骤:
1,定义报头字典
2,json序列化然后,编码成二进制,计算字典的长度(数据的长度在字典中)
3,sturct发送固定长度,把数据字典的长度的发送,再发送数据
4,客户端sturct先收固定的i ,4个字节 ,,得到字典的长度,连续收完字典长度
5,反序列化,解码,然后得到字典,再得到字典中数据长度,开始收数据,
########################################
粘包现像代码实例 服务端 #买手机 import socket,time import subprocess phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM) #绑定电话卡 ip_port=('127.0.0.1',8080) phone.bind(ip_port) phone.listen(5) conn,addr=phone.accept() # data1=conn.recv(1024) # data2=conn.recv(1024) data1=conn.recv(1) #b'h' time.sleep(5) data2=conn.recv(1024) #b'elloworldSB' # print('第一个包',data1) print('第二个包',data2) 客户端 #买手机 import socket,time import subprocess phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM) #绑定电话卡 ip_port=('127.0.0.1',8080) phone.connect(ip_port) # phone.send('helloworld'.encode('utf-8')) time.sleep(3) phone.send('SB'.encode('utf-8'))
######################################################
#####################################################
json,struct, 解决粘包问题的服务端与客户端代码(执行显示比较大的命令)
服务端
#coding:utf-8 #买手机 import socket import struct import json import subprocess phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM) #绑定电话卡 ip_port=('192.168.16.114',8081) phone.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) phone.bind(ip_port) #开机 phone.listen(5) #等待电话 #链接循环 while True: conn,addr=phone.accept() print('client addr',addr) #通讯循环 while True: try: cmd=conn.recv(1024) res=subprocess.Popen(cmd.decode('utf-8'), shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) out_res=res.stdout.read() err_res=res.stderr.read() data_size=len(out_res)+len(err_res) head_dic={'data_size':data_size} head_json=json.dumps(head_dic) head_bytes=head_json.encode('utf-8') #part1:先发报头的长度 head_len=len(head_bytes) conn.send(struct.pack('i',head_len)) #part2:再发送报头 conn.send(head_bytes) #part3:最后发送数据部分 conn.send(out_res) conn.send(err_res) except Exception: break conn.close() phone.close()
客户端
# 买手机 import socket import struct import json phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 拨通电话 # ip_port = ('127.0.0.1', 8080) ip_port = ('192.168.16.114', 8081) phone.connect(ip_port) # 通信循环 while True: # 发消息 cmd = input('>>: ').strip() if not cmd: continue phone.send(bytes(cmd, encoding='utf-8')) # part1:先收报头的长度 head_struct=phone.recv(4) head_len=struct.unpack('i',head_struct)[0] # part2:再收报头 head_bytes=phone.recv(head_len) head_json=head_bytes.decode('utf-8') head_dic=json.loads(head_json) print(head_dic) data_size = head_dic['data_size'] #part3:收数据 recv_size = 0 recv_data = b'' while recv_size < data_size: data = phone.recv(1024) recv_size += len(data) recv_data += data print(recv_data.decode('utf-8')) phone.close()
#######################################################
#######################################################
ftp上传下载文件代码实例
服务端
import socket import struct import json import subprocess import os class MYTCPServer: address_family = socket.AF_INET socket_type = socket.SOCK_STREAM allow_reuse_address = False max_packet_size = 8192 coding='utf-8' request_queue_size = 5 server_dir='file_upload' def __init__(self, server_address, bind_and_activate=True): """Constructor. May be extended, do not override.""" self.server_address=server_address self.socket = socket.socket(self.address_family, self.socket_type) if bind_and_activate: try: self.server_bind() self.server_activate() except: self.server_close() raise def server_bind(self): """Called by constructor to bind the socket. """ if self.allow_reuse_address: self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) self.socket.bind(self.server_address) self.server_address = self.socket.getsockname() def server_activate(self): """Called by constructor to activate the server. """ self.socket.listen(self.request_queue_size) def server_close(self): """Called to clean-up the server. """ self.socket.close() def get_request(self): """Get the request and client address from the socket. """ return self.socket.accept() def close_request(self, request): """Called to clean up an individual request.""" request.close() def run(self): while True: self.conn,self.client_addr=self.get_request() print('from client ',self.client_addr) while True: try: head_struct = self.conn.recv(4) if not head_struct:break head_len = struct.unpack('i', head_struct)[0] head_json = self.conn.recv(head_len).decode(self.coding) head_dic = json.loads(head_json) print(head_dic) #head_dic={'cmd':'put','filename':'a.txt','filesize':123123} cmd=head_dic['cmd'] if hasattr(self,cmd): func=getattr(self,cmd) func(head_dic) except Exception: break def put(self,args): file_path=os.path.normpath(os.path.join( self.server_dir, args['filename'] )) filesize=args['filesize'] recv_size=0 print('----->',file_path) with open(file_path,'wb') as f: while recv_size < filesize: recv_data=self.conn.recv(self.max_packet_size) f.write(recv_data) recv_size+=len(recv_data) print('recvsize:%s filesize:%s' %(recv_size,filesize)) tcpserver1=MYTCPServer(('127.0.0.1',8080)) tcpserver1.run()
客户端
import socket import struct import json import os class MYTCPClient: address_family = socket.AF_INET socket_type = socket.SOCK_STREAM allow_reuse_address = False max_packet_size = 8192 coding='utf-8' request_queue_size = 5 def __init__(self, server_address, connect=True): self.server_address=server_address self.socket = socket.socket(self.address_family, self.socket_type) if connect: try: self.client_connect() except: self.client_close() raise def client_connect(self): self.socket.connect(self.server_address) def client_close(self): self.socket.close() def run(self): while True: inp=input(">>: ").strip() if not inp:continue l=inp.split() cmd=l[0] if hasattr(self,cmd): func=getattr(self,cmd) func(l) def put(self,args): cmd=args[0] filename=args[1] if not os.path.isfile(filename): print('file:%s is not exists' %filename) return else: filesize=os.path.getsize(filename) head_dic={'cmd':cmd,'filename':os.path.basename(filename),'filesize':filesize} print(head_dic) head_json=json.dumps(head_dic) head_json_bytes=bytes(head_json,encoding=self.coding) head_struct=struct.pack('i',len(head_json_bytes)) self.socket.send(head_struct) self.socket.send(head_json_bytes) send_size=0 with open(filename,'rb') as f: for line in f: self.socket.send(line) send_size+=len(line) print(send_size) else: print('upload successful') client=MYTCPClient(('192.168.16.114',8080)) client.run()