recv原理、高階版黏包解決方案、基于UDP的socket通信
recv原理
源碼解釋:
Receive up to buffersize bytes from the socket.
接收來自socket緩沖區的字節數據,
For the optional flags argument, see the Unix manual.
對于這些設置的參數,可以查看Unix手冊。
When no data is available, block untilat least one byte is available or until the remote end is closed.
當緩沖區沒有數據可取時,recv會一直處于阻塞狀態,直到緩沖區至少有一個字節數據可取,或者遠程端關閉。
When the remote end is closed and all data is read, return the empty string.
關閉遠程端并讀取所有數據后,返回空字符串。
# 1,驗證服務端緩沖區數據沒有取完,又執行了recv執行,recv會繼續取值。
import socket
phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
phone.connect(('127.0.0.1',8080))
phone.send('hello'.encode('utf-8'))
phone.close()# 2,驗證服務端緩沖區取完了,又執行了recv執行,此時客戶端20秒內不關閉的前提下,recv處于阻塞狀態。
import socket
import time
phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
phone.connect(('127.0.0.1',8080))
phone.send('hello'.encode('utf-8'))
time.sleep(20)phone.close()# 3,驗證服務端緩沖區取完了,又執行了recv執行,此時客戶端處于關閉狀態,則recv會取到空字符串。
import socket
import time
phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
phone.connect(('127.0.0.1',8080))
phone.send('hello'.encode('utf-8'))
phone.close()
# 1,驗證服務端緩沖區數據沒有取完,又執行了recv執行,recv會繼續取值。import socketphone =socket.socket(socket.AF_INET,socket.SOCK_STREAM)phone.bind(('127.0.0.1',8080))phone.listen(5)conn, client_addr = phone.accept()
from_client_data1 = conn.recv(2)
print(from_client_data1)
from_client_data2 = conn.recv(2)
print(from_client_data2)
from_client_data3 = conn.recv(1)
print(from_client_data3)
conn.close()
phone.close()# 2,驗證服務端緩沖區取完了,又執行了recv執行,此時客戶端20秒內不關閉的前提下,recv處于阻塞狀態。import socketphone =socket.socket(socket.AF_INET,socket.SOCK_STREAM)phone.bind(('127.0.0.1',8080))phone.listen(5)conn, client_addr = phone.accept()
from_client_data = conn.recv(1024)
print(from_client_data)
print(111)
conn.recv(1024) # 此時程序阻塞20秒左右,因為緩沖區的數據取完了,并且20秒內,客戶端沒有關閉。
print(222)conn.close()
phone.close()# 3 驗證服務端緩沖區取完了,又執行了recv執行,此時客戶端處于關閉狀態,則recv會取到空字符串。import socketphone =socket.socket(socket.AF_INET,socket.SOCK_STREAM)phone.bind(('127.0.0.1',8080))phone.listen(5)conn, client_addr = phone.accept()
from_client_data1 = conn.recv(1024)
print(from_client_data1)
from_client_data2 = conn.recv(1024)
print(from_client_data2)
from_client_data3 = conn.recv(1024)
print(from_client_data3)
conn.close()
phone.close()# recv空字符串: 對方客戶端關閉了,且服務端的緩沖區沒有數據了,我再recv取到空bytes.
高階版黏包解決方案
服務端:
import socket
import subprocess
import struct
import jsonphone = socket.socket()
phone.bind(('127.0.0.1',8897))phone.listen(3)
print("等待接入")
while 1:conn, addr = phone.accept()print(conn, addr)try:while 1:from_client_data = conn.recv(1024)if from_client_data.decode('utf-8').upper() == 'Q':print('對方中斷鏈接')breakobj = subprocess.Popen(from_client_data.decode('utf-8'),shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE,)to_client_data = obj.stdout.read() + obj.stderr.read()total_size = len(to_client_data)dic = {'filename':'text1','MD5':120045318563413485631,'total_size':total_size}head1 = json.dumps(dic).encode('utf-8')len_head1 = len(head1)head_bytes = struct.pack('i',len_head1)conn.send(head_bytes)conn.send(head1)conn.send(to_client_data)except ConnectionError:print('對方中斷網絡鏈接')breakconn.close()
phone.close()
客戶端:
import socket
import struct
import json
phone = socket.socket()phone.connect(('127.0.0.1',8897))
while 1:to_server_data = input('請輸入內容')phone.send(to_server_data.encode('utf-8'))if to_server_data.upper() == "Q":print('主動退出')breakif not to_server_data.strip():continuehead = phone.recv(4)num = struct.unpack('i',head)[0]dic_head = phone.recv(num).decode('utf-8')dic = json.loads(dic_head)s = b''while len(s) < dic['total_size']:from_server_data = phone.recv(1024)s += from_server_dataprint(s.decode('gbk'))
phone.close()
基于UDP的socket通信
服務端:
import socket
udp_sk = socket.socket(type=socket.SOCK_DGRAM) #創建一個服務器的套接字
udp_sk.bind(('127.0.0.1',9000)) #綁定服務器套接字
msg,addr = udp_sk.recvfrom(1024)
print(msg)
udp_sk.sendto(b'hi',addr) # 對話(接收與發送)
udp_sk.close() # 關閉服務器套接字
客戶端:
import socket
ip_port=('127.0.0.1',9000)
udp_sk=socket.socket(type=socket.SOCK_DGRAM)
udp_sk.sendto(b'hello',ip_port)
back_msg,addr=udp_sk.recvfrom(1024)
print(back_msg.decode('utf-8'),addr)