スティッキーパケットの問題が発生するのはなぜですか?tcpだけにスティッキーパケットがありますが、udpにはスティッキーパケットはありません
。2つの場合、スティッキーパケットが発生します。
1.送信者は、送信する前にバッファがいっぱいになるのを待つ必要があり、スティッキーパケットが発生します(データを送信する時間間隔が非常に短く、データが非常に小さく、それらがマージされてスティッキーパケットになります)
2。受信者はバッファ内のパケットを時間内に受信しないため、複数のパケットを受信します(クライアントはデータの一部を送信し、サーバーはごく一部しか受信しません。サーバーが次に受信するときは、残りの最後のデータを取得します。スティッキーパケットを生成するためのバッファ)
メッセージの送受信の原理
ソケットクライアントはソケットサーバーにメッセージを送信します。最初に、クライアントはメッセージをオペレーティングシステムのキャッシュに送信します。オペレーティングシステムはネットワークカードを呼び出し、それを他のキャッシュに送信します。オペレーティング
システム。キャッシュ内のデータを受信します。データを送信する場合でも受信する場合でも、独自のオペレーティングシステムのキャッシュを処理します。
したがって、送信が受信に対応するわけではありません。
tcpプロトコルのNagleアルゴリズム: tcpプロトコルは、複数のデータを比較的少量のデータと比較的短い送信間隔で1つのデータブロックに結合し、一度にパケットを送信します。これにより、効率を向上させることができます。
このように、受信側を区別するのが難しいため、スティッキーパケットの問題が発生する可能性があります。
構造体モジュールの使用
import struct
header = struct.pack("i", 12312312) # struct.pack可以将一个整型打包成bytes类型,并且bytes固定长度为4个字节
print(header) # b'\xf8\xde\xbb\x00'
print(len(header)) # 4
total_size = struct.unpack("i", header)[0] # struct.unpack会将打包好的bytes类型,反解成打包前的整型数字(12312312,)
print(total_size) # 12312312
使用struct.pack中的“i”将int类型打包成bytes类型,数字有长度的限制,bytes的固定长度为4个字节,
使用struct.pack中的“q”将int类型打包成bytes类型,固定长度会增加一些,bytes的固定长度为8个字节
スティッキーパッケージコード表示の問題を解決するためのTcpカスタムプロトコル
int型をバイト型にパッケージ化
するための構造体モジュールのインポートバイト型をint型に分解するための構造体モジュールのインポート
クライアント
import socket
import struct
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client.connect(("127.0.0.1", 8080))
while True:
cmd = input("shu ru").strip()
if len(cmd) == 0:
continue
client.send(cmd.encode("utf-8"))
# 先接收数据的长度
header = client.recv(4)
total_size = struct.unpack("i", header)[0] # struct.unpack会将打包好的bytes类型,反解成打包前的整型数字
# 再接收真正的数据
recv_size = 0
res = b""
while recv_size < total_size: # 循环取值,直到把total_size取空为止
data = client.recv(1024) # 接收服务端传过来的数据bytes类型
recv_size += len(data) # 统计传过来的数据长度
res += data # 统计服务端过来的bytes类型的数据
print(res.decode("gbk"))
サーバ
import socket
import subprocess
import struct
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.bind(("127.0.0.1", 8080))
server.listen(5)
while True:
conn, client_addr = server.accept()
print(client_addr)
while True:
try:
data = conn.recv(1024)
if len(data) == 0:
break
cmd = data.decode("utf-8")
obj = subprocess.Popen(cmd, shell=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE
)
res1 = obj.stdout.read()
res2 = obj.stderr.read()
total_size = len(res1) + len(res2)
# 先把数据的长度发过去
header = struct.pack("i", total_size) # struct.pack可以将一个total_size打包成bytes类型,并且bytes固定长度为4个字节
conn.send(header) # 数据的长度
# 再发送真正的数据
conn.send(res1)
conn.send(res2)
except Exception:
break
conn.close()
tcpカスタムプロトコルはパッケージに固執する問題を解決し、アップグレードされたバージョンのコードは、
インポートヘッダーの説明を辞書の形式で表示し、データ型変換用のstructモジュールをインポートし、エンコードとデコード用にjsonモジュールをインポートします
クライアント
粘包问题升级版
import socket
import struct
import json
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client.connect(("127.0.0.1", 8080))
while True:
cmd = input("shu ru").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: # 循环取值,直到把total_size取空为止
data = client.recv(1024) # 接收服务端传过来的数据bytes类型
recv_size += len(data) # 统计传过来的数据长度
res += data # 统计服务端传过来的bytes类型的数据
print(res.decode("gbk"))
サーバ
粘包问题升级版
import socket
import subprocess
import struct
import json
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.bind(("127.0.0.1", 8080))
server.listen(5)
while True:
conn, client_addr = server.accept()
print(client_addr)
while True:
try:
data = conn.recv(1024)
if len(data) == 0:
break
cmd = data.decode("utf-8")
obj = subprocess.Popen(cmd, shell=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE
)
res1 = obj.stdout.read()
res2 = obj.stderr.read()
header_dic = {
"filename": "a.txt",
"total_size": len(res1) + len(res2),
"md5": "55lala55"
}
header_json = json.dumps(header_dic)
header_bytes = header_json.encode("utf-8")
# 先发4个字节
conn.send(struct.pack("i", len(header_bytes)))
# 再发报头字典
conn.send(header_bytes)
# 最后再发送真正的数据
conn.send(res1)
conn.send(res2)
except Exception:
break
conn.close()
udpプロトコルの使用
udpはリンクなしで、どちらの端が最初に開始されたかについてエラーは報告されません
。udpプロトコルはこの時間の最大範囲のみを受信し、余分なデータは直接失われます。tcpなどの機能はありません。プロトコルキャッシングとスティッキーパケット。udpプロトコル信頼性の低いプロトコル
クライアント
import socket
client = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # udp协议又称为数据报
while True:
msg = input("shuru").strip()
client.sendto(msg.encode("utf-8"), ("127.0.0.1", 8080))
data, client_addr = client.recvfrom(1024)
print(data.decode("utf-8"))
client.close()
サーバ
import socket
server = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # udp协议又称为数据报
server.bind(("127.0.0.1", 8080))
while True:
conn, client_addr = server.recvfrom(1024)
print(conn.decode("utf-8"))
server.sendto(conn.upper(), client_addr)
server.close()
並行性と並列
並行性:これは疑似並列です。つまり、同時に実行されているように見えます。単一のCPU +マルチチャネルテクノロジーで並行性を実現できます(並列処理も並行性に属します)
並列:同時に実行し、複数のCPUでのみ並列処理を実現できます
tcpプロトコルで同時機能を実装するためのコード
クライアント
import socket
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client.connect(("127.0.0.1", 8080))
while True:
msg = input("shu ru")
if len(msg) == 0:
continue
client.send(msg.encode("utf-8"))
data = client.recv(1024)
print(data.decode("utf-8"))
socket.close()
サーバ
import socketserver
class MyRequestHandler(socketserver.BaseRequestHandler):
def handle(self):
while True:
try:
data = self.request.recv(1024) # self.request代表conn,表示连接通道
print(data.decode("utf-8"))
if len(data) == 0:
break
self.request.send(data.upper())
except Exception:
break
self.request.close()
server = socketserver.ThreadingTCPServer(("127.0.0.1", 8080), MyRequestHandler)
server.serve_forever()
udpプロトコルで同時機能を実装するためのコード
クライアント
import socket
client = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
while True:
msg = input("shuru").strip()
client.sendto(msg.encode("utf-8"), ("127.0.0.1", 8080))
data, server_addr = client.recvfrom(1024)
print(data.decode("utf-8"))
サーバ
客户端每发来一条消息,都会起一个线程,会把客户端发来的消息封装到对象里面去,交给一个线程来运行handle方法
import socketserver
class MyRequestHanlder(socketserver.BaseRequestHandler):
def handle(self):
data, server = self.request # data代表客户端发来的数据,server是套接字对象
server.sendto(data.upper(), self.client_address) # 服务端给客户端回消息
server = socketserver.ThreadingUDPServer(("127.0.0.1", 8080), MyRequestHanlder) # 这行代码等同于定义了一个套接字对象,绑定了ip跟端口
server.serve_forever() # 这行代码代表了循环给客户端收发消息