让我们基于tcp先制作一个远程执行命令的程序
res=subprocess.Popen(cmd.decode('utf-8'),
shell=True,
stderr=subprocess.PIPE,
stdout=subprocess.PIPE)
的结果编码是以当前所在的系统为准的,如果是windows,那么res.stdout.read()独处的就是GBK编码的,在接收端需要用GBK编码
且只能从管道里读一次结果
只有TCP有粘包现象,udp永远你不会粘包, tcp协议是面向流的协议, udp是面向消息的协议
所谓粘包的问题主要还是因为接收方不知道消息之间的界限,不知道一次性提取多少个字节的数据所造成的
tcp为提高传输效率,tcp优化算法会把一些数据合成一个tcp段后一次发送出去,这样接收方就收到了粘包数据
tcp在数据传输时,发送端先把数据发送到自己的缓存中,然后协议控制将缓存中的数据发往对端,对端返回一个ack=1,发送端则清理缓存中的数据,对端返回ack=0,则重新发送数据,所以tcp是可靠的
而udp发送数据,对端是不会返回确认信息的,因此不可靠
使用tcp协议远程执行命令
from socket import *
import subprocess
ip_port=('127.0.0.1',8080)
BUFSIZE=1024
tcp_socket_server=socket(AF_INET,SOCK_STREAM)
tcp_socket_server.bind(ip_port)
tcp_socket_server.listen(5)
while True:
conn,addr=tcp_socket_server.accept()
print('客户端‘,addr)
while True:
cmd=conn.recv(BUFSIZE)
if len(cmd) == 0:break
res = subprocess.Popen(cmd.decode('utf-8'),shell=True,
stdout=subprocess.PIPE,
stdin=subprocess.PIPE,
stderr=subprocess.PIPE)
stderr=res.stderr.read()
stdout=res.stdout.read()
conn,send(stderr)
conn,send(stdout)
客户端
import socket
BUFSIZE=1024
ip_port=('127.0.0.1',8080)
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
res=s.connect_ex(ip_port)
while True:
msg=input('>>:').strip()
if len(msg) == 0:break
s.send(msg.encode('utf-8'))
act_res=s.recv(BUFSIZE)
print(act_res.decode('utf-8'),end=")
上述程序基于tcp的socket,在运行时会发生粘包
小面基于udp制作一个远程执行命令的程序
from socket import *
import subprocess
ip_port=('127.0.0.1',8080)
bufsize=1024
udp_server=socket(AF_INET,SOCK=DGRAM)
udp_server.bind(ip_port)
while True:
cmd,addr=udp_server.recvfrom(bufsize)
print('用户命令’,cmd)
res=subprocess.Popen(cmd.decode('utf-8'),shell=True,stderr=subpross.PIPE,stdout=subprocess.PIPE)
stderr=res.stderr.read()
stdout=res.stdout.read()
udp_server.sendto(stderr,addr)
udp_server.sendto(stdout,addr)
udp_server.close()
客户端
from socket import *
ip_port=('127.0.0.1',8080)
bufsize=1024
udp_client=socket(AF_INET,SOCK_DGRAM)
while True:
msg=input('>>: ').strip()
udp_client.sendto(msg.encode('utf-8'),ip_port)
data,addr=udp_client.recvfrom(bufsize)
print(data.decode('utf-8'),end=")
以上基于udp的socket,在运行时永远不会发生粘包
解决粘包的办法
问题的根源在于,接收端不知道发送端要传送的字节流的长度,所以解决粘包的办法就是围绕如何让发送端在发送数据前,把自己将要发送的字节流总大小让接收端知晓,然后接收端来一个死循环接受完所有数据
low版解决方法
from socket import *
import subprocess
ip_port=('127.0.0.1',8080)
s=socket(AF_INET,SOCK.STREAM)
s.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)
s.bind(ip_port)
s.listen(5)
while True:
conn,addr=s.accept()
print('客户端‘,addr)
while True:
msg=conn.recv(1024)
if not msg:break
res=subprocess.Popen(msg.decode('utf-8'),shell=True,
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
err=res.stderr.read()
if err:
ret=err
else:
ret=res.stdout.read()
data_length=len(ret)
conn.send(str(data_lenth).encode('utf-8'))
data=conn.recv(1024).decode('utf-8')
if data == 'recv_ready'
conn.sendall(ret)
conn.close()
客户端
import socket,time
s=scoket.socket(socket.AF_INET,socket.SOCK.STRAM)
res=s.connect_ex(('127.0.0.1',8080))
while True:
msg=input('>>:').strip()
if len(msg) ==0:break
if msg == 'quit':break
s.send(msg.encode('utf-8'))
length=int(s.recv(1024).decode('utf-8'))
s.send('recv_ready'.encode('utf-8'))
send_size=0
recv_size=0
data=b''
while recv_size <length:
data+=s.recv(1024)
recv_size+=len(data)
print(data.deode('utf-8'))
程序运行速度远快于网络传输速度,所以在发送一段字节前,先用send去发送该字节流长度,这种方式会放大网络延迟带来的性能损耗
海峰老师解决粘包的方法
为字节流加上自定义固定长度报头,报头中包含字节流长度,然后一次send到对端,对端在接受时,先从缓存中取出定长的报头,然后再取真实数据