粘包现象
讓我們基于tcp先制作一個遠程執(zhí)行命令的程序
res=subprocess.Popen(cmd.decode('utf-8'),
shell=True,
stderr=subprocess.PIPE,
stdout=subprocess.PIPE)
的結果編碼是以當前所在的系統(tǒng)為準的,如果是windows,那么res.stdout.read()獨處的就是GBK編碼的,在接收端需要用GBK編碼
且只能從管道里讀一次結果
?
只有TCP有粘包現(xiàn)象,udp永遠你不會粘包,? tcp協(xié)議是面向流的協(xié)議, udp是面向消息的協(xié)議
所謂粘包的問題主要還是因為接收方不知道消息之間的界限,不知道一次性提取多少個字節(jié)的數(shù)據(jù)所造成的
tcp為提高傳輸效率,tcp優(yōu)化算法會把一些數(shù)據(jù)合成一個tcp段后一次發(fā)送出去,這樣接收方就收到了粘包數(shù)據(jù)
?
tcp在數(shù)據(jù)傳輸時,發(fā)送端先把數(shù)據(jù)發(fā)送到自己的緩存中,然后協(xié)議控制將緩存中的數(shù)據(jù)發(fā)往對端,對端返回一個ack=1,發(fā)送端則清理緩存中的數(shù)據(jù),對端返回ack=0,則重新發(fā)送數(shù)據(jù),所以tcp是可靠的
而udp發(fā)送數(shù)據(jù),對端是不會返回確認信息的,因此不可靠
?
使用tcp協(xié)議遠程執(zhí)行命令
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,在運行時會發(fā)生粘包
?
小面基于udp制作一個遠程執(zhí)行命令的程序
?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,在運行時永遠不會發(fā)生粘包
?
?
?
?
?
?
解決粘包的辦法
問題的根源在于,接收端不知道發(fā)送端要傳送的字節(jié)流的長度,所以解決粘包的辦法就是圍繞如何讓發(fā)送端在發(fā)送數(shù)據(jù)前,把自己將要發(fā)送的字節(jié)流總大小讓接收端知曉,然后接收端來一個死循環(huán)接受完所有數(shù)據(jù)
?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'))
程序運行速度遠快于網(wǎng)絡傳輸速度,所以在發(fā)送一段字節(jié)前,先用send去發(fā)送該字節(jié)流長度,這種方式會放大網(wǎng)絡延遲帶來的性能損耗
?
海峰老師解決粘包的方法
為字節(jié)流加上自定義固定長度報頭,報頭中包含字節(jié)流長度,然后一次send到對端,對端在接受時,先從緩存中取出定長的報頭,然后再取真實數(shù)據(jù)
?
轉載于:https://www.cnblogs.com/mayicai/p/9220958.html
總結
- 上一篇: Android之布局RelativeLa
- 下一篇: 指数映射