緩衝區 subprocess 黏包

1、緩衝區
 
每一個socket被建立之後,都會分配兩個緩衝區,輸入緩衝區和輸出緩衝區,默認大小都爲8k,能夠經過getsocket()獲取,暫時存放傳輸數據,防止程序在發送數據的時候卡組,提升代碼運行效率
 
首先看python的系統交互subprocess
 
import subprocess

sub_obj = subprocess.Popen(
    'dir',                  # 系統指令:'dir','ipconfig'.等
    shell=True,             # 使用shell,就至關於使用cmd窗口
    stdout=subprocess.PIPE,  # 標準輸出PIPE管道,保存着指令的執行結果
    stderr=subprocess.PIPE   # 標準錯誤輸出
)
print('正確輸出',sub_obj.stdout.read().decode('gbk'))
print('錯誤輸出',sub_obj.stderr.read().decode('gbk'))

  

結果編碼是以當前所在系統爲準的,若爲windows,則用GBK解碼,且只能從管道里讀一次結果
 
2、黏包
 
  一、tcp兩種黏包現象:
   ①、發送端須要等緩衝區滿才發送出去,形成黏包(發送時間的時間間隔很短,數據也很小,會被優化算法合到一塊兒,產生黏包)
 
server 端的代碼示例以下
from socket import *
ip_port=('127.0.0.1',8080)

tcp_socket_server=socket(AF_INET,SOCK_STREAM)
tcp_socket_server.bind(ip_port)
tcp_socket_server.listen(5)
conn, addr = tcp_socket_server.accept()
# 服務端連續接受兩個信息
data1 = conn.recv(10)
data2 = conn.recv(10)

print('----->',data1.decode('utf-8'))
print('----->',data2.decode('utf-8'))

conn.close
nian_server01.py
client端的實例以下:
 
1 import socket
2 BUFSIZE=1024
3 ip_port=('127.0.0.1',8080)
4 s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
5 res=s.connect(ip_port)
6 
7 s.send('hello'.encode('utf-8'))
8 s.send('sigui'.encode('utf-8'))
nian_client01.py

結果:python

-----> hellosigui
----->

  

 ②、
接受方沒有及時接受緩衝區的包,致使多個包接收,(客戶端發送了一段數據,服務端只收了一小部分,服務區下次接收的時候仍是從緩衝區拿上次遺留的數據,產生黏包)
第一次若是發送的數據大小2000B,接收端一次性接受大小爲1024,這樣就致使剩下的內容會被下一次recv接收到,致使結果錯亂。
 
server代碼以下
 1 import socket
 2 import subprocess
 3 server = socket.socket()
 4 ip_port = ('127.0.0.1',8010)
 5 server.bind(ip_port)
 6 server.listen()
 7 conn,addr = server.accept()
 8 
 9 while 1:
10     from_client_cmd = conn.recv(1024)
11     print(from_client_cmd.decode('utf-8'))
12     sub_obj = subprocess.Popen(
13         from_client_cmd.decode('utf-8'),
14         shell=True,
15         stdout=subprocess.PIPE,
16         stderr=subprocess.PIPE
17     )
18     std_msg = sub_obj.stdout.read()
19     print('指令執行的長度>>>',len(std_msg))
20     conn.send(std_msg)
nian_server02.py
client代碼以下
 1 import socket
 2 
 3 client = socket.socket()
 4 client.connect(('127.0.0.1',8010))
 5 
 6 while 1:
 7     cmd = input('請輸入指令:')
 8     client.send(cmd.encode('utf-8'))
 9     server_cmd_result = client.recv(1025)
10     print(server_cmd_result.decode('gbk'))
nian_client02.py
二、解決tcp黏包的方案
 
  一、方案一:因爲接受方不知道發送端將要傳送的字節流的長蘇,致使接收的時候,可能接收不全,或者多接收另一次發送的內容,因此讓發送端在發送數據以前,把本身將要發送的字節流總大小讓接收方知曉,而後接收方發一個確認消息給發送端,而後發送端再發送過來後面的真實數據,接收方再來接收完全部數據。
 
server端代碼:
 1 import socket
 2 import subprocess
 3 # 建立socket對象
 4 server = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
 5 server.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) # 地址重用
 6 # 設置IP地址和端口號
 7 ip_port = ('127.0.0.1',8080)
 8 # 綁定IP地址和端口號
 9 server.bind(ip_port)
10 # 監聽IP地址和端口號
11 server.listen()
12 # 等待創建鏈接,conn是鏈接通道,addr是客戶端地址
13 conn,addr = server.accept()
14 
15 while 1:
16     from_client_cmd = conn.recv(1024)
17     print(from_client_cmd.decode('utf-8'))
18     # 接收到客戶端發送的指令,服務端經過subprocess模塊到服務器本身的系統執行這條指令
19     sub_obj = subprocess.Popen(
20         from_client_cmd.decode('utf-8'),
21         shell=True,
22         stdout=subprocess.PIPE,
23         stderr=subprocess.PIPE
24     )
25     # 從管道中拿出結果,經過subprocess.Popen的實例化對象.stdout.read()方法來獲取管道中的結果
26     std_msg = sub_obj.stdout.read()
27     # 爲了解決黏包的現象,咱們統計了一下消息的長度,先將消息長度發給客戶端,客戶端經過這個長度來接收後面服務端要發送的真實數據
28     std_msg_len = len(std_msg)
29     # 首先將數據長度的數據類型轉化爲bytes類型
30     std_bytes_len = str(len(std_msg)).encode('utf-8')
31     print('指令的執行結果長度>>>',len(std_msg))
32     conn.send(std_bytes_len)
33 
34     status = conn.recv(1024)
35     if status.decode('utf-8') == 'ok':
36         conn.send(std_msg)
37     else:
38         pass
solve_server01.py
client端代碼:
 1 import socket
 2 
 3 client = socket.socket()
 4 client.connect(('127.0.0.1',8080))
 5 
 6 while 1:
 7     cmd = input('請輸入指令:')
 8     client.send(cmd.encode('utf-8'))
 9     server_res_len = client.recv(1024).decode('utf-8')
10     print('來自服務端的消息長度:',server_res_len)
11     client.send(b'ok')
12     server_cmd_result = client.recv(int(server_res_len))
13     print(server_cmd_result.decode('gbk'))
solve_client01.py
 
二、方案二:經過struck模塊將須要發送的內容長度進行打包,打包成一個4字節長度的數據發送到對端,對端只要取出前4個字節,而後對前4個字節的弧據進行解包,拿到發送內容的長度,而後經過這個長度來繼續接收咱們要發送的內容。
先說struct包
例:
 
import struct

num = 100

# num太大的話會報錯,
# struct.error: 'i' format requires -2147483648 <= number <= 2147483647 #這個是範圍
# 打包,將int類型的數據打包成4個長度的bytes類型的數據
byt = struct.pack('i',num)
print(byt)
# 解包,將bytes類型的數據,轉換爲對應的那個int類型的數據
# 注:unpack返回的是truple
int_num = struct.unpack('i',byt)[0]
print(int_num)

  

server端代碼02:
 1 import  socket
 2 import subprocess
 3 import struct
 4 
 5 server = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
 6 server.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)  # 地址重用
 7 ip_port = ('127.0.0.1',8090)
 8 
 9 server.bind(ip_port)
10 server.listen()
11 conn,addr = server.accept()
12 
13 while 1:
14     from_client_cmd = conn.recv(1024)
15 
16     print(from_client_cmd.decode('utf-8'))
17     # 接收到客戶端發送來的系統指令,服務端經過subprocess模塊到服務端本身的系統裏只能怪這條指令
18     sub_obj = subprocess.Popen(
19         from_client_cmd.decode('utf-8'),
20         shell=True,
21         stdout=subprocess.PIPE,  # 正確的結果存放位置
22         stderr=subprocess.PIPE   # 錯誤結果的存放位置
23     )
24     # 從管道中拿出結果,經過subproess.Popen的實例化對象.stdout.read()方法來獲取管道中的結果
25     std_msg = sub_obj.stdout.read()
26     # 爲了解決黏包的現象,首先統計一下消息的長度,先將消息的長度發給客戶端,客戶端銅鼓這個長度來接收後面服務端發送的真實數據
27     std_msg_len = len(std_msg)
28     print('指令的執行長度>>>',len(std_msg))
29     msg_lenint_struct = struct.pack('i',std_msg_len)
30     conn.send(msg_lenint_struct+std_msg)
solve_server01.py
client端代碼02:
 1 import socket
 2 import struct
 3 client = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
 4 client.connect(('127.0.0.1',8090))
 5 
 6 while 1:
 7     cmd = input('請輸入指令:')
 8     # 發送指令
 9     client.send(cmd.encode('utf-8'))
10     # 接收數據長度,首先接收4個字節長度的數據,由於這四個字節是後面數據的長度
11     server_res_len = client.recv(4)
12     msg_len = struct.unpack('i',server_res_len)[0]
13 
14     print('來自服務端的消息長度',msg_len)
15     # 經過解包出來的長度,來接收後面的真實數據
16     server_cmd_result = client.recv(msg_len)
17     print(server_cmd_result.decode('gbk'))
solve_client02.py
 
三、udp是面向包的,因此udp是不存在黏包的。
 
在udp代碼中,咱們在server端接受返回消息的時候,咱們設置的recvfrom(1024),那麼當咱們輸入的執行指令爲'dir'的時候,若dir在當前文件夾下輸出的內容大於1024,而後就報錯了,
解釋緣由:由於udp是面向報文的,每一個消息是一個包,接收端設置接受大小的時候,必需要比你發的這個 包要大,否則一次接受不了就會報錯,而tcp是不會報錯的,這也是爲何udp會丟包的緣由
相關文章
相關標籤/搜索