socket补充:通信循环、链接循环、远程操作及黏包现象
socket補充:通信循環(huán)、鏈接循環(huán)、遠程操作及黏包現(xiàn)象
socket通信循環(huán)
server端:
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() print(conn, client_addr, sep='\n')while 1: # 循環(huán)收發(fā)消息try:from_client_data = conn.recv(1024)print(from_client_data.decode('utf-8'))conn.send(from_client_data + b'SB')except ConnectionResetError:breakconn.close() phone.close()client端:
import socketphone = socket.socket(socket.AF_INET,socket.SOCK_STREAM) # 買電話phone.connect(('127.0.0.1',8080)) # 與客戶端建立連接, 撥號while 1: # 循環(huán)收發(fā)消息client_data = input('>>>')phone.send(client_data.encode('utf-8'))from_server_data = phone.recv(1024)print(from_server_data.decode('utf-8'))phone.close() # 掛電話socket通信鏈接循環(huán)
server端:
import socketphone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)phone.bind(('127.0.0.1',8080))phone.listen(5)while 1 : # 循環(huán)連接客戶端conn, client_addr = phone.accept()print(client_addr)while 1:try:from_client_data = conn.recv(1024)print(from_client_data.decode('utf-8'))conn.send(from_client_data + b'SB')except ConnectionResetError:breakconn.close() phone.close()服務(wù)端:
import socketphone = socket.socket(socket.AF_INET,socket.SOCK_STREAM) # 買電話phone.connect(('127.0.0.1',8080)) # 與客戶端建立連接, 撥號while 1:client_data = input('>>>')phone.send(client_data.encode('utf-8'))from_server_data = phone.recv(1024)print(from_server_data.decode('utf-8'))phone.close() # 掛電話socket遠程操作
import subprocessobj = subprocess.Popen('dir1',shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE,)print(obj.stdout.read().decode('gbk')) # 正確命令 print(obj.stderr.read().decode('gbk')) # 錯誤命令服務(wù)端:
import socket import subprocess phone = socket.socket()phone.bind(('127.0.0.1',8848))phone.listen(2) # listen: 2 允許有兩個客戶端加到半鏈接池,超過兩個則會報錯while 1:conn,addr = phone.accept() # 等待客戶端鏈接我,阻塞狀態(tài)中print(f'鏈接來了: {conn,addr}')while 1:try:from_client_data = conn.recv(1024) # 最多接受1024字節(jié)if from_client_data.upper() == b'Q':print('客戶端正常退出聊天了')breakobj = subprocess.Popen(from_client_data.decode('utf-8'),shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE,)result = obj.stdout.read() + obj.stderr.read()conn.send(result)except ConnectionResetError:print('客戶端鏈接中斷了')breakconn.close() phone.close()# shell: 命令解釋器,相當(dāng)于調(diào)用cmd 執(zhí)行指定的命令。 # stdout:正確結(jié)果丟到管道中。 # stderr:錯了丟到另一個管道中。 # windows操作系統(tǒng)的默認編碼是gbk編碼。客戶端:
import socketphone = socket.socket()phone.connect(('127.0.0.1',8848)) while 1:to_server_data = input('>>>輸入q或者Q退出').strip().encode('utf-8')if not to_server_data:# 服務(wù)端如果接受到了空的內(nèi)容,服務(wù)端就會一直阻塞中,所以無論哪一端發(fā)送內(nèi)容時,都不能為空發(fā)送print('發(fā)送內(nèi)容不能為空')continuephone.send(to_server_data)if to_server_data.upper() == b'Q':breakfrom_server_data = phone.recv(1024) # 最多接受1024字節(jié)print(f'{from_server_data.decode("gbk")}')phone.close()什么叫做黏包現(xiàn)象?為什么會出現(xiàn)黏包現(xiàn)象?
socket收發(fā)消息的原理
應(yīng)用程序所看到的數(shù)據(jù)是一個整體,或說是一個流(stream),一條消息有多少字節(jié)對應(yīng)用程序是不可見的,因此TCP協(xié)議是面向流的協(xié)議,這也是容易出現(xiàn)粘包問題的原因。
而UDP是面向消息的協(xié)議,每個UDP段都是一條消息,應(yīng)用程序必須以消息為單位提取數(shù)據(jù),不能一次提取任意字節(jié)的數(shù)據(jù),這一點和TCP是很不同的。怎樣定義消息呢?
可以認為對方一次性write/send的數(shù)據(jù)為一個消息,需要明白的是當(dāng)對方send一條信息的時候,無論底層怎樣分段分片,TCP協(xié)議層會把構(gòu)成整條消息的數(shù)據(jù)段排序完成后才呈現(xiàn)在內(nèi)核緩沖區(qū)。
設(shè)置緩沖區(qū)的兩個好處:
所謂粘包問題主要還是因為接收方不知道消息之間的界限,不知道一次性提取多少字節(jié)的數(shù)據(jù)所造成的。
只有TCP有粘包現(xiàn)象,UDP永遠不會粘包
黏包的兩種情況:
1,接收方?jīng)]有及時接收緩沖區(qū)的包,造成多個包接收(客戶端發(fā)送了一段數(shù)據(jù),服務(wù)端只收了一小部分,服務(wù)端下次再收的時候還是從緩沖區(qū)拿上次遺留的數(shù)據(jù),產(chǎn)生粘包)
server端:
import socket import subprocessphone = socket.socket(socket.AF_INET, socket.SOCK_STREAM)phone.bind(('127.0.0.1', 8080))phone.listen(5)while 1: # 循環(huán)連接客戶端conn, client_addr = phone.accept()print(client_addr)while 1:try:cmd = conn.recv(1024)ret = subprocess.Popen(cmd.decode('utf-8'), shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)correct_msg = ret.stdout.read()error_msg = ret.stderr.read()conn.send(correct_msg + error_msg)except ConnectionResetError:breakconn.close() phone.close()client端:
import socketphone = socket.socket(socket.AF_INET,socket.SOCK_STREAM) # 買電話phone.connect(('127.0.0.1',8080)) # 與客戶端建立連接, 撥號while 1:cmd = input('>>>')phone.send(cmd.encode('utf-8'))from_server_data = phone.recv(1024)print(from_server_data.decode('gbk'))phone.close()2、發(fā)送端需要等緩沖區(qū)滿才發(fā)送出去,造成粘包(發(fā)送數(shù)據(jù)時間間隔很短,數(shù)據(jù)也很小,會合到一起,產(chǎn)生粘包)
server端:
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()frist_data = conn.recv(1024) print('1:',frist_data.decode('utf-8')) # 1: helloworld second_data = conn.recv(1024) print('2:',second_data.decode('utf-8'))conn.close() phone.close()客戶端:
import socketphone = socket.socket(socket.AF_INET, socket.SOCK_STREAM) phone.connect(('127.0.0.1', 8080)) phone.send(b'hello') phone.send(b'world')phone.close() # 兩次返送信息時間間隔太短,數(shù)據(jù)小,造成服務(wù)端一次收取如何解決黏包現(xiàn)象?
struct模塊
該模塊可以把一個類型,如數(shù)字,轉(zhuǎn)成固定長度的bytes
import struct # 將一個數(shù)字轉(zhuǎn)化成等長度的bytes類型。 ret = struct.pack('i', 183346) print(ret, type(ret), len(ret))# 通過unpack反解回來 ret1 = struct.unpack('i',ret)[0] print(ret1, type(ret1), len(ret1))# 但是通過struct 處理不能處理太大ret = struct.pack('l', 4323241232132324) print(ret, type(ret), len(ret)) # 報錯方案一:low版。
問題的根源在于,接收端不知道發(fā)送端將要傳送的字節(jié)流的長度,所以解決粘包的方法就是圍繞,如何讓發(fā)送端在發(fā)送數(shù)據(jù)前,把自己將要發(fā)送的字節(jié)流總數(shù)按照固定字節(jié)發(fā)送給接收端后面跟上總數(shù)據(jù),然后接收端先接收固定字節(jié)的總字節(jié)流,再來一個死循環(huán)接收完所有數(shù)據(jù)。
server端:
import socket import subprocess import struct phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM)phone.bind(('127.0.0.1', 8080))phone.listen(5)while 1:conn, client_addr = phone.accept()print(client_addr)while 1:try:cmd = conn.recv(1024)ret = subprocess.Popen(cmd.decode('utf-8'), shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)correct_msg = ret.stdout.read()error_msg = ret.stderr.read()# 1 制作固定報頭total_size = len(correct_msg) + len(error_msg)header = struct.pack('i', total_size)# 2 發(fā)送報頭conn.send(header)# 發(fā)送真實數(shù)據(jù):conn.send(correct_msg)conn.send(error_msg)except ConnectionResetError:breakconn.close() phone.close()client端:
import socket import struct phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)phone.connect(('127.0.0.1',8080))while 1:cmd = input('>>>').strip()if not cmd: continuephone.send(cmd.encode('utf-8'))# 1,接收固定報頭header = phone.recv(4)# 2,解析報頭total_size = struct.unpack('i', header)[0]# 3,根據(jù)報頭信息,接收真實數(shù)據(jù)recv_size = 0res = b''while recv_size < total_size:recv_data = phone.recv(1024)res += recv_datarecv_size += len(recv_data)print(res.decode('gbk'))phone.close()轉(zhuǎn)載于:https://www.cnblogs.com/lifangzheng/p/11364213.html
總結(jié)
以上是生活随笔為你收集整理的socket补充:通信循环、链接循环、远程操作及黏包现象的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 浦发信用卡倍富金可以提前还款吗?手续费怎
- 下一篇: recv原理、高阶版黏包解决方案、基于U