前面学习了基本的最原始的单线程的socket的原理,下面学习一个新的知识点-粘包。由于我们接受的命令是recv(1024),那么如果当另一端发送的数据大于1024个字节的时候,他就会出现粘包的问题。每次只能发送1024个字节,如果我们直接放在一个循环里面不断发送,不断接受,那么当数据发完以后,他就会卡住在那里,因为我们知道在正常连接状态里,socket的accept和recv都是会进入阻塞的状态(换句话说,没有客户连接或者客户发空包,那么就会卡住!)。如何处理这个问题呢?一个思路是发送之前,先打个招呼,告诉对方自己要发送的字节长度,这样对方可以根据长度判断什么时候终止接受。
下面看一个模拟SSH操作的实例,客户端发送命令,服务器端执行之后返回结果给客户端
Server.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | #!/usr/bin/env python # -*- coding:utf-8 -*- import socket import subprocess #导入执行命令模块 ip_port = ( '127.0.0.1' , 9999 ) #定义元祖 #买手机 s = socket.socket() #绑定协议,生成套接字 s.bind(ip_port) #绑定ip+协议+端口:用来唯一标识一个进程,ip_port必须是元组格式 s.listen( 5 ) #定义最大可以挂起胡链接数 while True : #用来重复接收新的链接 conn,addr = s.accept() #接收客户端胡链接请求,返回conn(相当于一个特定胡链接),addr是客户端ip+port #收消息 while True : #用来基于一个链接重复收发消息 try : #捕捉客户端异常关闭(ctrl+c) recv_data = conn.recv( 1024 ) #收消息,阻塞 if len (recv_data) = = 0 : break #客户端如果退出,服务端将收到空消息,退出 #发消息 p = subprocess.Popen( str (recv_data,encoding = 'utf8' ),shell = True ,stdout = subprocess.PIPE) #执行系统命令,windows平 # 台命令的标准输出是gbk编码,需要转换 res = p.stdout.read() #获取标准输出 print (res, type (res)) if len (res) = = 0 : #执行错误命令,标准输出为空, send_data = 'cmd err' else : send_data = str (res,encoding = 'gbk' ) #命令执行ok,字节gbk---->str---->字节utf-8 send_data = bytes(send_data,encoding = 'utf8' ) #解决粘包问题 ready_tag = 'Ready|%s' % len (send_data) conn.send(bytes(ready_tag,encoding = 'utf8' )) #发送数据长度 feedback = conn.recv( 1024 ) #接收确认信息 feedback = str (feedback,encoding = 'utf8' ) if feedback.startswith( 'Start' ): conn.send(send_data) #发送命令的执行结果 except Exception: break conn.close() |
client.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | #!/usr/bin/env python # -*- coding:utf-8 -*- import socket ip_port = ( '127.0.0.1' , 9999 ) s = socket.socket() s.connect(ip_port) #链接服务端,如果服务已经存在一个好的连接,那么挂起 while True : #基于connect建立的连接来循环发送消息 send_data = input ( ">>: " ).strip() if send_data = = 'exit' : break if len (send_data) = = 0 : continue s.send(bytes(send_data,encoding = 'utf8' )) #解决粘包问题 ready_tag = s.recv( 1024 ) #收取带数据长度的字节:Ready|9998 ready_tag = str (ready_tag,encoding = 'utf8' ) if ready_tag.startswith( 'Ready' ): #Ready|9998 msg_size = int (ready_tag.split( '|' )[ - 1 ]) #获取待接收数据长度 start_tag = 'Start' s.send(bytes(start_tag,encoding = 'utf8' )) #发送确认信息 #基于已经收到的待接收数据长度,循环接收数据 recv_size = 0 recv_msg = b'' while recv_size < msg_size: recv_data = s.recv( 1024 ) recv_msg + = recv_data recv_size + = len (recv_data) print ( 'MSG SIZE %s RECE SIZE %s' % (msg_size,recv_size)) print ( str (recv_msg,encoding = 'utf8' )) s.close() |
登录乐搏学院官网
或关注我们的官方微博,还有更多惊喜哦~
本文出自 “” 博客,请务必保留此出处
顶
0
踩