24、解決粘包問題的方法、UDP套接字簡單示例、多併發問題
阿新 • • 發佈:2021-01-19
一、解決粘包問題的方法(簡單版)
- 服務端
from socket import * from subprocess import PIPE, Popen import struct server = socket(AF_INET, SOCK_STREAM) server.bind(('127.0.0.1', 8000)) server.listen(5) while True: conn, client_addr = server.accept() print(client_addr) while True: # 通訊迴圈 try: cmd = conn.recv(8096) if len(cmd) == 0: # 針對於linux系統 break obj = Popen(cmd.decode('utf-8'), shell=True, stderr=PIPE, stdout=PIPE, ) res1 = obj.stdout.read() res2 = obj.stderr.read() total_size = len(res1) + len(res2) # 先把資料的長度給發過去,長度為4 header = struct.pack('i', total_size) conn.send(header) # 再發送真正的資料 conn.send(res2) # 利用TCP協議的特性 conn.send(res1) # nagle演算法規定,TCP協議會將資料量較小、 # 時間間隔短的資料合併為一條傳送給客戶端 except Exception: break conn.close() # 異常斷開後回收資源
- 客戶端
from socket import * from subprocess import PIPE, Popen import struct client = socket(AF_INET, SOCK_STREAM) client.connect(('127.0.0.1', 8000)) while True: cmd = input('>>>:').strip() if len(cmd) == 0: continue client.send(cmd.encode('utf-8')) # 先接收資料的長度 header = client.recv(4) total_size = struct.unpack('i', header)[0] # 提取出位元組長度 # 接收真正的資料 recv_size = 0 res = b'' while recv_size < total_size: data = client.recv(1024) recv_size += len(data) res += data print(res.decode('gbk'))
二、解決粘包問題的方法(優化版)
- 服務端
import json from socket import * import struct from subprocess import PIPE, Popen server = socket(AF_INET, SOCK_STREAM) server.bind(('127.0.0.1', 8000)) server.listen(5) while True: # 連結迴圈 conn, client_addr = server.accept() print(client_addr) while True: # 通訊迴圈 try: cmd = conn.recv(8096) if len(cmd) == 0: break obj = Popen(cmd.decode('utf-8'), shell=True, stdout=PIPE, stderr=PIPE) res1 = obj.stdout.read() res2 = obj.stderr.read() header_dic = { 'filename': 'a.txt', 'total_size': len(res1) + len(res2), 'md5': 'qwe165qwqwe65456qw5' } header_json = json.dumps(header_dic) header_bytes = header_json.encode('utf-8') # 先發四個位元組 conn.send(struct.pack('i', len(header_bytes))) # 再發報頭字典 conn.send(header_bytes) # 最後發真正的資料 conn.send(res1) conn.send(res2) except Exception: break conn.close() # 關閉視窗來回收資源
- 客戶端
from socket import *
import struct
import json
client = socket(AF_INET, SOCK_STREAM)
client.connect(('127.0.0.1', 8000))
while True:
cmd = input('>>>: ').strip()
if len(cmd) == 0:
continue
client.send(cmd.encode('utf-8'))
# 先接收四個位元組,提取header_bytes的長度
header_bytes_len = struct.unpack('i', client.recv(4)[0])
# 再收header_bytes,提取header_dic
header_bytes = client.recv(header_bytes_len)
header_json = header_bytes.decode('utf-8')
header_dic = json.loads(header_json)
print(header_json)
total_size = header_dic['total_size']
# 再接收真正的資料
recv_size = 0
res = b''
while recv_size < total_size:
data = client.recv(1024)
recv_size += len(data)
res += data
print(res.decode('gbk'))
三、基於UDP套接字的編寫
- 服務端
import socket
server = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # 造手機
server.bind(('127.0.0.1', 8000)) # 需要繫結,不許要監聽
while True: # 通訊迴圈
data, client_addr = server.recvfrom(1024)
# 解壓賦值(文字,傳送端的埠號)
print(data)
server.sendto(data.upper(), client_addr)
- 客戶端
from socket import *
client = socket(AF_INET, SOCK_DGRAM) # 造手機
while True: # 輸入迴圈
msg = input('輸入:').strip()
client.sendto(msg.encode('utf8'), ('127.0.0.1', 8000))
# sendto(傳送內容二進位制,接收埠)
data, client_addr = client.recvfrom(1024)
# 解壓賦值(接收的文字,埠)
print(data.decode('utf-8'))
- UDP協議一般不會用於大資料的傳輸
- UDP套接字雖然沒有粘包的問題,但是不能代替TCP套接字,因為UDP協議有一個缺陷,如果資料傳送的途中,資料丟失,則資料就丟失了,而TCP協議則不會有這種缺陷,因此一般的UDP套接字使用者無關緊要的資料傳送,例如QQ、微信聊天等.
四、socketserver模組
- 基本使用框架
import socketserver
class MyHandler(socketserver.BaseRequestHandler):
# 通訊迴圈
def handle(self): # 呼叫的話以上不變
# data = self.request.recv(1024) # self.recv(1024)
# print(data)
# self.request.send(data.upper())
if __name__ == '__main__':
server = socketserver.ThreadingTCPServer(('127.0.0.1', 8080), MyHandler, bind_and_activate=True)
server.serve_forever()
- 多併發——服務端
# 同一時刻有多個人在接聽
import socketserver
import json
from subprocess import Popen, PIPE
import struct
class MyHandler(socketserver.BaseRequestHandler):
# 通訊迴圈
def handle(self): # 呼叫的話以上不變
# data = self.request.recv(1024) # self.recv(1024)
# print(data)
# self.request.send(data.upper())
try:
cmd = self.request.recv(8096)
obj = Popen(cmd.decode('utf-8'),
shell=True,
stdout=PIPE,
stderr=PIPE,
)
res1 = obj.stdout.read()
res2 = obj.stderr.read()
header_dic = {
'filename': "a.txt",
'total_size': len(res1) + len(res2),
'md5': '123dfsfsaf123213'
}
header_json = json.dumps(header_dic)
header_bytes = header_json.encode('utf-8')
# 先發4個位元組
self.request.send(struct.pack('i', len(header_bytes)))
# 再發報頭字典
self.request.send(header_bytes)
# 最後傳送真正的資料
self.request.send(res1)
self.request.send(res2)
except Exception:
self.request.close()
# 使用socketserver的連線迴圈(併發),但是使用了自己的通訊迴圈
if __name__ == '__main__':
server = socketserver.ThreadingTCPServer(('127.0.0.1', 8080), MyHandler, bind_and_activate=True)
server.serve_forever()
# 呼叫的話只需要將conn改成self.request即可
- 多併發——客戶端1
from socket import *
import struct
import json
client = socket(AF_INET, SOCK_STREAM)
client.connect(('127.0.0.1', 8080))
while True:
cmd = input(">>>: ").strip()
if len(cmd) == 0:
continue
client.send(cmd.encode('utf-8'))
# 先收4個位元組,提取header_bytes的長度
header_bytes_len = struct.unpack('i', client.recv(4))[0]
# 再收header_bytes,提取header_dic
header_bytes = client.recv(header_bytes_len)
header_json = header_bytes.decode('utf-8')
header_dic = json.loads(header_json)
print(header_dic)
total_size = header_dic['total_size']
# 再接收真正的資料
recv_size = 0
res = b''
while recv_size < total_size:
data = client.recv(1024)
recv_size += len(data)
res += data
print(res.decode('gbk'))
- 多併發——客戶端2
from socket import *
import struct
import json
client = socket(AF_INET, SOCK_STREAM)
client.connect(('127.0.0.1', 8080))
while True:
cmd = input(">>>: ").strip()
if len(cmd) == 0:
continue
client.send(cmd.encode('utf-8'))
# 先收4個位元組,提取header_bytes的長度
header_bytes_len = struct.unpack('i', client.recv(4))[0]
# 再收header_bytes,提取header_dic
header_bytes = client.recv(header_bytes_len)
header_json = header_bytes.decode('utf-8')
header_dic = json.loads(header_json)
print(header_dic)
total_size = header_dic['total_size']
# 再接收真正的資料
recv_size = 0
res = b''
while recv_size < total_size:
data = client.recv(1024)
recv_size += len(data)
res += data
print(res.decode('gbk'))