粘包现象
讓我們基于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到對端,對端在接受時,先從緩存中取出定長的報頭,然后再取真實數據
?
轉載于:https://www.cnblogs.com/mayicai/p/9220958.html
總結
- 上一篇: Android之布局RelativeLa
- 下一篇: 指数映射