1. 程式人生 > >Day29--Python--緩衝區, 粘包

Day29--Python--緩衝區, 粘包

tcp: 屬於長連線,與一個客戶端進行連線了以後,其他的客戶端要等待.要想連線另外一個客戶端,需要優雅地斷開當前客戶端的連線

允許地址重用:
server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
在bind IP地址和埠之前,寫這句話,防止埠被佔用無法使用.


緩衝區:
輸入緩衝區 # recv
輸出緩衝區 # send

什麼是緩衝區,為什麼會有緩衝區?
每個socket物件被建立後,都會分配兩個緩衝區,輸入緩衝區和輸出緩衝區. 當傳送訊息的時候,
先將資料寫入輸出緩衝區中,再由TCP/UDP協議將資料從緩衝區傳送到目標機器. 一旦資料寫入到緩衝區,
無論是否傳送到目標機器,程式都可以執行下一步操作,這樣可以防止網路不暢通造成的程式阻塞.
當接收資料的時候,會從輸入緩衝區中讀取資料,而不是直接從網路中讀取,這樣cpu可以處理完當前任務後從輸入緩衝區讀取資訊.


粘包(tcp的兩種粘包現象)
1. 連續傳送小的資料,並且每次傳送之間的時間間隔很短. (兩個訊息在輸出緩衝區粘連到一起)
原因是tcp為了傳輸效率,做了一個優化演算法(Nagle),減少連續的小包傳送.因為每一個訊息被包裹以後都會有兩個過程:
1. 組包 2. 拆包0, 會降低效率
2. 第一次服務端傳送的資料比客戶端設定的一次接收訊息的size要大, 一次接收不完,第二次接收的時候就會把第一次剩餘的訊息接收到.

粘包的根本原因: 雙方不知道對方傳送訊息的大小.
解決方案1:
傳送訊息之前,先計算要傳送訊息的長度,然後先將訊息長度傳送過去,對方回覆確認收到,
然後根據接收到的訊息長度來修改自己一次接收訊息的大小.
這個過程多了一次互動

解決方案2:
第一種粘包情況可以增加發送訊息的時間間隔,等緩衝區的訊息傳送成功後再發送後續訊息

解決方案3:



# 粘包現象1 服務端

import socket

server = socket.socket()
ip_port = ('192.168.15.87', 8001)
server.bind(ip_port)

server.listen()

conn,addr = server.accept()

from_client_msg1 = conn.recv(1024).decode('utf-8')
#2000B -- 1024  976B  + 1000B
from_client_msg2 = conn.recv(1024).decode('utf-8')
#976+48 = 1024
print
('msg1:',from_client_msg1) print('msg2:',from_client_msg2) conn.close() server.close()
# 粘包現象1 客戶端
import socket

client = socket.socket()
server_ip_port = ('192.168.15.87', 8001)
client.connect(server_ip_port)
client.send('你好!'.encode('utf-8'))
client.send('天氣真好~'.encode('utf-8'))

client.close()

 

# 粘包現象2 服務端

import socket
import subprocess

server = socket.socket()
ip_port = ('192.168.15.87', 8001)
server.bind(ip_port)
server.listen(3)
while 1:
    print('等待連線中...')
    tube, addr = server.accept()
    print('連線成功!')
    while 1:
        print('等待接收訊息...')
        command = tube.recv(1024).decode('utf-8')
        print('命令:', command)
        if command == 'exit':
            print('連線已斷開!')
            break
        else:
            sub_obj = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
            ret = sub_obj.stdout.read().decode('gbk')  #接受到的返回資訊是bytes型別的,並且windows系統的預設編碼為gbk
            len_of_msg = str(len(ret.encode('utf-8')))
            if len_of_msg == '0':
                msg_of_error = sub_obj.stderr.read().decode('gbk').encode('utf-8')
                len_of_error = str(len(msg_of_error))
                print('即將傳送訊息長度為%s' % len_of_error)
                tube.send(len_of_error.encode('utf-8'))
                tube.send(msg_of_error)
            else:
                print('即將傳送訊息長度為%s' % len_of_msg)
                print(ret)
                tube.send(len_of_msg.encode('utf-8'))
                tube.send(ret.encode('utf-8'))

    tube.close()
View Code 粘包現象_2_服務端
# 粘包現象2 客戶端

import socket

client = socket.socket()
server_ip_port = ('192.168.15.87', 8001)
client.connect(server_ip_port)

while 1:
    cmd = input('請輸入要執行的指令>>>')
    if cmd == 'exit':
        client.send(cmd.encode('utf-8'))
        break
    client.send(cmd.encode('utf-8'))
    len_of_msg = int(client.recv(1024).decode('utf-8'))
    print('接收的訊息長度為%s' % len_of_msg)
    msg = client.recv(len_of_msg)
    real_len = len(msg)
    print('實際訊息長度是%s' % real_len)
    print(msg.decode('utf-8'))

client.close()
View Code 粘包現象_2_客戶端