基於tcp協議的socket,客戶端一次接受不完,下一次繼續接受(若是間隔時間相對過長,後續的數據會與以前剩餘的數據黏在一塊兒),send數據時,連續的發送少許的數據(時間間隔很短),這些數據會積壓在一塊兒發送出去.python
每一個 socket 被建立後,都會分配兩個緩衝區,輸入緩衝區和輸出緩衝區。程序員
write()/send() 並不當即向網絡中傳輸數據,而是先將數據寫入緩衝區中,再由TCP協議將數據從緩衝區發送到目標機器。一旦將數據寫入到緩衝區,函數就能夠成功返回,無論它們有沒有到達目標機器,也無論它們什麼時候被髮送到網絡,這些都是TCP協議負責的事情。shell
TCP協議獨立於 write()/send() 函數,數據有可能剛被寫入緩衝區就發送到網絡,也可能在緩衝區中不斷積壓,屢次寫入的數據被一次性發送到網絡,這取決於當時的網絡狀況、當前線程是否空閒等諸多因素,不禁程序員控制。json
read()/recv() 函數也是如此,也從輸入緩衝區中讀取數據,而不是直接從網絡中讀取。網絡
這些I/O緩衝區特性可整理以下:socket
1.I/O緩衝區在每一個TCP套接字中單獨存在;
2.I/O緩衝區在建立套接字時自動生成;
3.即便關閉套接字也會繼續傳送輸出緩衝區中遺留的數據;
4.關閉套接字將丟失輸入緩衝區中的數據。tcp
服務端ide
import socket import subprocess import struct server_side = socket.socket() server_side.bind(("127.0.0.1", 8848)) server_side.listen(5) while 1: conn, addr = server_side.accept() while 1: try: cmd = conn.recv(1024) if cmd.decode("utf-8") == 'q': break obj = subprocess.Popen(cmd.decode("utf-8"), shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, ) result = obj.stdout.read() + obj.stderr.read() # 製做報頭 total_size = len(result) # 將不固定長度的int數據類型的報頭,轉化成固定長度的4bytes total_size_bytes = struct.pack("i", total_size) # 發送報頭 conn.send(total_size_bytes) # 發送數據 conn.send(result) except ConnectionResetError: break conn.close() server_side.close()
客戶端函數
import socket import struct client_side = socket.socket() client_side.connect(("127.0.0.1", 8848)) while 1: to_send = input("請輸入你要執行的命令:") if to_send.upper() == "Q": client_side.send("q".encode("utf-8")) break client_side.send(to_send.encode("utf-8")) # 接收報頭 head_bytes = client_side.recv(4) # 反解報頭 total_size = struct.unpack("i", head_bytes)[0] # 循環接收數據 data = b"" while len(data) < total_size: data += client_side.recv(1024) print(data.decode("gbk")) client_side.close()
服務端線程
import socket import subprocess import json import struct phone = socket.socket() phone.bind(("127.0.0.1", 8848)) phone.listen(5) conn, addr = phone.accept() while 1: try: cmd = conn.recv(1024) obj = subprocess.Popen(cmd.decode("utf-8"), shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE ) result = obj.stdout.read() + obj.stderr.read() result = result.decode("gbk").encode("utf-8") # 1.製做報頭 head_dict = {"md5": "df", "file_name": "新建文件夾", "file_size": len(result)} # 2. 將報頭字典轉化成json字符串 head_dict_json = json.dumps(head_dict) # 3. 將json字符串轉化成bytes head_dict_json_bytes = head_dict_json.encode("utf-8") # 4. 獲取報頭的長度 head_len = len(head_dict_json_bytes) # 5. 將報頭長度轉化成固定的4個bytes head_len_bytes = struct.pack("i", head_len) # 6. 發送固定的4個字節 conn.send(head_len_bytes) # 7. 發送報頭 conn.send(head_dict_json_bytes) # 8. 發送原數據 conn.send(result) except ConnectionResetError: break conn.close() phone.close()
客戶端
import socket import struct import json phone = socket.socket() phone.connect(("127.0.0.1", 8848)) while 1: cmd = input("請輸入指令") phone.send(cmd.encode("utf-8")) # 1. 接收報頭長度 head_len_bytes = phone.recv(4) # 2. 將報頭數字轉化成int類型 head_len = struct.unpack("i", head_len_bytes)[0] # 3. 接收bytes類型的報頭字典 head_dict_json_bytes = phone.recv(head_len) # 4. 將bytes類型的字典轉化成json字符串 head_dict_json = head_dict_json_bytes.decode("utf-8") # 5. 將json字符串轉化成字典 head_dict = json.loads(head_dict_json) # 6. 循環接收原數據 total_data = b'' while len(total_data) < head_dict["file_size"]: total_data += phone.recv(1024) print(total_data.decode("utf-8")) phone.close