import subprocess res = subprocess.Popen( ' dir ' ,shell=True,stdout=subprocess.PIPE,stderr= subprocess.PIPE) print ( ' Stdout: ' ,res.stdout.read().decode( ' gbk ' )) print ( ' Stderr: ' ,res.stderr.read().decode( ' gbk ' ))
PIPE把輸出的東西裝到一個(gè)'水管'里,如果在windows中的編碼格式是gbk,執(zhí)行結(jié)果:
Stdout: 驅(qū)動(dòng)器 C 中的卷是 系統(tǒng) 卷的序列號(hào)是 85C0 - 669A C:\Users\Administrator\PycharmProjects\Internet_program 的目錄 2019/09/16 13:48. 2019/09/16 13:48 .. 2019/09/16 13:47 .idea 2019/09/16 13:46 21 Client1.py 2019/09/16 13:42 0 Client2.py 2019/09/16 13:48 207 Sever1.py 2019/09/16 01:41 70 time_test.py 2019/09/14 23:51 venv 4 個(gè)文件 298 字節(jié) 4 個(gè)目錄 45,863,636,992 可用字節(jié) Stderr:
在這里也可以使用os.popen()但是它會(huì)不管正確和錯(cuò)誤的結(jié)果都放在一起,而用subprocess能夠分別拿到正確和錯(cuò)誤的信息
?
基于TCP實(shí)現(xiàn)的黏包
Sever:
import socket sk = socket.socket() sk.bind(( ' 127.0.0.1 ' ,8092 )) sk.listen() conn,addr = sk.accept() while True: cmd = input( ' <<< ' ) conn.send(cmd.encode( ' gbk ' )) ret = conn.recv(1024).decode( ' gbk ' ) print (ret) conn.close() sk.close()
Client:
import socket import subprocess sk = socket.socket() sk.connect(( ' 127.0.0.1 ' ,8092 )) while True: cmd = sk.recv(1024).decode( ' gbk ' ) ret = subprocess.Popen(cmd,shell=True,stdout=subprocess.PIPE,stderr= subprocess.PIPE) std_out = ' stdout: ' + (ret.stdout.read()).decode( ' gbk ' ) std_err = ' stderr: ' + (ret.stderr.read()).decode( ' gbk ' ) print (std_out) print (std_err) sk.send(std_out.encode( ' gbk ' )) sk.send(std_err.encode( ' gbk ' )) sk.close()
執(zhí)行結(jié)果:
Sever:
<<< dir;ls stdout: 驅(qū)動(dòng)器 C 中的卷是 系統(tǒng) 卷的序列號(hào)是 85C0 - 669A C:\Users\Administrator\PycharmProjects\Internet_program 的目錄 <<< ipconfig stderr:找不到文件 <<<
Client:
stdout: 驅(qū)動(dòng)器 C 中的卷是 系統(tǒng) 卷的序列號(hào)是 85C0 - 669A C:\Users\Administrator\PycharmProjects\Internet_program 的目錄 stderr:找不到文件 stdout: Windows IP 配置 以太網(wǎng)適配器 Bluetooth 網(wǎng)絡(luò)連接 2 : 媒體狀態(tài) . . . . . . . . . . . . : 媒體已斷開(kāi) 連接特定的 DNS 后綴 . . . . . . . : 以太網(wǎng)適配器 本地連接: 媒體狀態(tài) . . . . . . . . . . . . : 媒體已斷開(kāi) 連接特定的 DNS 后綴 . . . . . . . : 無(wú)線(xiàn)局域網(wǎng)適配器 無(wú)線(xiàn)網(wǎng)絡(luò)連接: 連接特定的 DNS 后綴 . . . . . . . : 本地鏈接 IPv6 地址. . . . . . . . : fe80::8c6:36a9:6fa6: 8018%14 IPv4 地址 . . . . . . . . . . . . : 192.168.43.216 子網(wǎng)掩碼 . . . . . . . . . . . . : 255.255.255.0 默認(rèn)網(wǎng)關(guān). . . . . . . . . . . . . : 192.168.43.1 隧道適配器 本地連接 * 3 : 媒體狀態(tài) . . . . . . . . . . . . : 媒體已斷開(kāi) 連接特定的 DNS 后綴 . . . . . . . : stderr:
當(dāng)我們?cè)趕ever端輸入dir;ls命令時(shí),只有stdout的結(jié)果跑出來(lái),而當(dāng)我們輸入ipconfig這個(gè)命令時(shí),系統(tǒng)將上一次dir;ls未執(zhí)行完的stderr的結(jié)果給跑出來(lái)。像這樣沒(méi)有接受完全或者接受多了的就是黏包現(xiàn)象。
TCP會(huì)有黏包現(xiàn)象但是它不丟包。
?
基于UDP實(shí)現(xiàn)的黏包
Sever:
import socket sk = socket.socket(type= socket.SOCK_DGRAM) sk.bind(( ' 127.0.0.1 ' ,8092 )) msg,addr = sk.recvfrom(10240 ) while 1 : cmd = input( ' <<< ' ) if cmd == ' q ' : break sk.sendto(cmd.encode( ' gbk ' ),addr) msg,addr = sk.recvfrom(10240 ) print (msg.decode( ' gbk ' )) sk.close()
Client:
import socket import subprocess sk = socket.socket(type= socket.SOCK_DGRAM) addr = ( ' 127.0.0.1 ' ,8092 ) sk.sendto( ' Start ' .encode( ' utf-8 ' ),addr) while 1 : cmd,addr = sk.recvfrom(10240 ) ret = subprocess.Popen(cmd.decode( ' gbk ' ),shell=True,stderr=subprocess.PIPE,stdout= subprocess.PIPE) std_out = ' Stdout: ' + (ret.stdout.read()).decode( ' gbk ' ) std_err = ' Stderr: ' + (ret.stderr.read()).decode( ' gbk ' ) print (std_out) print (std_err) sk.sendto(std_out.encode( ' gbk ' ),addr) sk.sendto(std_err.encode( ' gbk ' ),addr) sk.close()
執(zhí)行結(jié)果:
Sever:
<<< dir;ls Stdout: 驅(qū)動(dòng)器 C 中的卷是 系統(tǒng) 卷的序列號(hào)是 85C0 - 669A C:\Users\Administrator\PycharmProjects\Internet_program 的目錄 <<< dir Stderr:找不到文件 <<< ipconfig Stdout: 驅(qū)動(dòng)器 C 中的卷是 系統(tǒng) 卷的序列號(hào)是 85C0 - 669A C:\Users\Administrator\PycharmProjects\Internet_program 的目錄 2019/09/16 14:43. 2019/09/16 14:43 .. 2019/09/16 14:37 .idea 2019/09/16 14:43 553 Client1.py 2019/09/16 13:42 0 Client2.py 2019/09/16 14:43 306 Sever1.py 2019/09/16 01:41 70 time_test.py 2019/09/14 23:51 venv 4 個(gè)文件 929 字節(jié) 4 個(gè)目錄 45,855,449,088 可用字節(jié) <<< pwd Stderr: <<< ip Stdout: Windows IP 配置 以太網(wǎng)適配器 Bluetooth 網(wǎng)絡(luò)連接 2 : 媒體狀態(tài) . . . . . . . . . . . . : 媒體已斷開(kāi) 連接特定的 DNS 后綴 . . . . . . . : 以太網(wǎng)適配器 本地連接: 媒體狀態(tài) . . . . . . . . . . . . : 媒體已斷開(kāi) 連接特定的 DNS 后綴 . . . . . . . : 無(wú)線(xiàn)局域網(wǎng)適配器 無(wú)線(xiàn)網(wǎng)絡(luò)連接: 連接特定的 DNS 后綴 . . . . . . . : 本地鏈接 IPv6 地址. . . . . . . . : fe80::8c6:36a9:6fa6: 8018%14 IPv4 地址 . . . . . . . . . . . . : 192.168.43.216 子網(wǎng)掩碼 . . . . . . . . . . . . : 255.255.255.0 默認(rèn)網(wǎng)關(guān). . . . . . . . . . . . . : 192.168.43.1 隧道適配器 本地連接 * 3 : 媒體狀態(tài) . . . . . . . . . . . . : 媒體已斷開(kāi) 連接特定的 DNS 后綴 . . . . . . . : <<<
Client:
Stdout: 驅(qū)動(dòng)器 C 中的卷是 系統(tǒng) 卷的序列號(hào)是 85C0 - 669A C:\Users\Administrator\PycharmProjects\Internet_program 的目錄 Stderr:找不到文件 Stdout: 驅(qū)動(dòng)器 C 中的卷是 系統(tǒng) 卷的序列號(hào)是 85C0 - 669A C:\Users\Administrator\PycharmProjects\Internet_program 的目錄 2019/09/16 14:43. 2019/09/16 14:43 .. 2019/09/16 14:37 .idea 2019/09/16 14:43 553 Client1.py 2019/09/16 13:42 0 Client2.py 2019/09/16 14:43 306 Sever1.py 2019/09/16 01:41 70 time_test.py 2019/09/14 23:51 venv 4 個(gè)文件 929 字節(jié) 4 個(gè)目錄 45,855,449,088 可用字節(jié) Stderr: Stdout: Windows IP 配置 以太網(wǎng)適配器 Bluetooth 網(wǎng)絡(luò)連接 2 : 媒體狀態(tài) . . . . . . . . . . . . : 媒體已斷開(kāi) 連接特定的 DNS 后綴 . . . . . . . : 以太網(wǎng)適配器 本地連接: 媒體狀態(tài) . . . . . . . . . . . . : 媒體已斷開(kāi) 連接特定的 DNS 后綴 . . . . . . . : 無(wú)線(xiàn)局域網(wǎng)適配器 無(wú)線(xiàn)網(wǎng)絡(luò)連接: 連接特定的 DNS 后綴 . . . . . . . : 本地鏈接 IPv6 地址. . . . . . . . : fe80::8c6:36a9:6fa6: 8018%14 IPv4 地址 . . . . . . . . . . . . : 192.168.43.216 子網(wǎng)掩碼 . . . . . . . . . . . . : 255.255.255.0 默認(rèn)網(wǎng)關(guān). . . . . . . . . . . . . : 192.168.43.1 隧道適配器 本地連接 * 3 : 媒體狀態(tài) . . . . . . . . . . . . : 媒體已斷開(kāi) 連接特定的 DNS 后綴 . . . . . . . : Stderr: Stdout: Stderr: ' pwd ' 不是內(nèi)部或外部命令,也不是可運(yùn)行的程序 或批處理文件。 Stdout: Stderr: ' ip ' 不是內(nèi)部或外部命令,也不是可運(yùn)行的程序 或批處理文件。
可以看出UDP不會(huì)有黏包現(xiàn)象,會(huì)產(chǎn)生丟包現(xiàn)象,沒(méi)發(fā)完就不發(fā)了,不完整也不可靠。
?
黏包成因
TCP協(xié)議的數(shù)據(jù)傳送
拆包機(jī)制
當(dāng)發(fā)送端緩沖區(qū)的長(zhǎng)度大于網(wǎng)卡的MTU時(shí),TCP會(huì)將這次發(fā)送的數(shù)據(jù)拆成幾個(gè)數(shù)據(jù)包發(fā)送出去。MTU是Maximum Transmission Unit的縮寫(xiě),意思是網(wǎng)絡(luò)上傳送最大數(shù)據(jù)包,MTU是字節(jié)單位,大部分網(wǎng)絡(luò)設(shè)備的MTU都是1500. 如果本機(jī)的MTU比網(wǎng)關(guān)的MTU大,大的數(shù)據(jù)包就會(huì)被拆開(kāi)來(lái)傳送,這樣會(huì)產(chǎn)生很多數(shù)據(jù)包碎片,增加丟包率,降低網(wǎng)絡(luò)速度。
在正常情況下它的拆包可理解為:
面向流的通信特點(diǎn)和Nagle算法
TCP(transport control protocol,傳輸控制協(xié)議),是面向連接的,面向流的,提供高可靠性的服務(wù)。收發(fā)兩端(客戶(hù)端和服務(wù)端)都要有一一成對(duì)的socket,因此發(fā)送端為了將多個(gè)發(fā)往接收端的包,更有效地發(fā)往對(duì)方,使用了優(yōu)化算法(Nagle算法),將多次間隔較小且數(shù)據(jù)量小的數(shù)據(jù),合并成一個(gè)大的數(shù)據(jù)塊,然后進(jìn)行封包。這樣接收端就難于分辨出來(lái)了,必須提供科學(xué)的拆包機(jī)制。即面向流的通信是無(wú)消息保護(hù)邊界的。
對(duì)于空消息:TCP是基于數(shù)據(jù)流的,于是收發(fā)消息不能為空,這就需要在客戶(hù)端和服務(wù)端都添加空消息的處理機(jī)制,防止程序卡住,而UDP協(xié)議是基于數(shù)據(jù)報(bào)的,即便是你輸入的是空內(nèi)容(直接回車(chē)),也可以被發(fā)送,UDP協(xié)議會(huì)幫你封裝上消息然后發(fā)出去。
可靠黏包的TCP協(xié)議:TCP協(xié)議數(shù)據(jù)不會(huì)丟,沒(méi)有收完包,就會(huì)下次接收,會(huì)繼續(xù)上次繼續(xù)接受。
基于tcp協(xié)議特點(diǎn)的黏包現(xiàn)象成因
當(dāng)我們?cè)趕ocket服務(wù)端發(fā)送值1、2,然后根據(jù)優(yōu)化算法,它會(huì)把1先放到這個(gè)緩存當(dāng)中等一等,然后再把2一起封裝起來(lái),然后再發(fā)出去,因此我們看到的就是黏包現(xiàn)象
這種現(xiàn)象的表面現(xiàn)象是兩個(gè)send太近且發(fā)送的消息太短
發(fā)送端可以使1K1K地發(fā)送數(shù)據(jù),而接收端的應(yīng)用程序可以?xún)蒏兩K地提走數(shù)據(jù),當(dāng)然也有可能一次提走3K或6K數(shù)據(jù),或者一次只提走幾個(gè)字節(jié)的數(shù)據(jù)。也就是說(shuō),應(yīng)用程序所看到的數(shù)據(jù)是一個(gè)整體,或者說(shuō)是一個(gè)流(stream),一條消息有多少字節(jié)對(duì)應(yīng)程序是不可見(jiàn)的,因此TCP協(xié)議是面向流的協(xié)議,這也是容易出現(xiàn)黏包問(wèn)題的原因。而UDP協(xié)議是面向消息的協(xié)議,每個(gè)UDP段都是一條消息,應(yīng)用程序必須以消息單位提取數(shù)據(jù),不能一次提取任意字節(jié)的數(shù)據(jù),這一點(diǎn)和TCP是很不同的。
?
解決黏包的方法
解決方案一:
Sever:
import socket sk = socket.socket() sk.bind(( ' 127.0.0.1 ' ,8080 )) sk.listen() conn,addr = sk.accept() while True: cmd = input( ' <<< ' ) if cmd == ' q ' : conn.send(b ' q ' ) break conn.send(cmd.encode( ' gbk ' )) num = conn.recv(1024).decode( ' utf-8 ' ) conn.send(b ' ok ' ) res = conn.recv(int(num)).decode( ' gbk ' ) print (res) conn.close() sk.close()
Client:
import socket import subprocess sk = socket.socket() sk.connect(( ' 127.0.0.1 ' ,8080 )) while True: cmd = sk.recv(1024).decode( ' gbk ' ) if cmd == ' q ' : break res = subprocess.Popen(cmd,shell= True, stdout = subprocess.PIPE, stderr = subprocess.PIPE) std_out = res.stdout.read() std_err = res.stderr.read() sk.send(str(len(std_out) + len(std_err)).encode( ' utf-8 ' )) sk.recv( 1024 ) print ( ' Stdout: ' + std_out.decode( ' gbk ' )) print ( ' Stderr: ' + std_err.decode( ' gbk ' )) sk.send(std_out) sk.send(std_err) sk.close()
執(zhí)行結(jié)果:
Sever:
<<< dir 驅(qū)動(dòng)器 C 中的卷是 系統(tǒng) 卷的序列號(hào)是 85C0 - 669A C:\Users\Administrator\PycharmProjects\Internet_program 的目錄 2019/09/17 15:33. 2019/09/17 15:33 .. 2019/09/17 15:31 .idea 2019/09/17 15:33 623 Client1.py 2019/09/16 13:42 0 Client2.py 2019/09/17 15:21 389 Sever1.py 2019/09/16 01:41 70 time_test.py 2019/09/14 23:51 venv 4 個(gè)文件 1,082 字節(jié) 4 個(gè)目錄 45,031,833,600 可用字節(jié) <<< ls ' ls ' 不是內(nèi)部或外部命令,也不是可運(yùn)行的程序 或批處理文件。 <<< ipconfig Windows IP 配置 以太網(wǎng)適配器 Bluetooth 網(wǎng)絡(luò)連接 2 : 媒體狀態(tài) . . . . . . . . . . . . : 媒體已斷開(kāi) 連接特定的 DNS 后綴 . . . . . . . : 以太網(wǎng)適配器 本地連接: 媒體狀態(tài) . . . . . . . . . . . . : 媒體已斷開(kāi) 連接特定的 DNS 后綴 . . . . . . . : 無(wú)線(xiàn)局域網(wǎng)適配器 無(wú)線(xiàn)網(wǎng)絡(luò)連接: 連接特定的 DNS 后綴 . . . . . . . : 本地鏈接 IPv6 地址. . . . . . . . : fe80::8c6:36a9:6fa6: 8018%14 IPv4 地址 . . . . . . . . . . . . : 192.168.43.216 子網(wǎng)掩碼 . . . . . . . . . . . . : 255.255.255.0 默認(rèn)網(wǎng)關(guān). . . . . . . . . . . . . : 192.168.43.1 隧道適配器 本地連接 * 3 : 媒體狀態(tài) . . . . . . . . . . . . : 媒體已斷開(kāi) 連接特定的 DNS 后綴 . . . . . . . : <<<
Client:
Stdout: 驅(qū)動(dòng)器 C 中的卷是 系統(tǒng) 卷的序列號(hào)是 85C0 - 669A C:\Users\Administrator\PycharmProjects\Internet_program 的目錄 2019/09/17 15:33. 2019/09/17 15:33 .. 2019/09/17 15:31 .idea 2019/09/17 15:33 623 Client1.py 2019/09/16 13:42 0 Client2.py 2019/09/17 15:21 389 Sever1.py 2019/09/16 01:41 70 time_test.py 2019/09/14 23:51 venv 4 個(gè)文件 1,082 字節(jié) 4 個(gè)目錄 45,031,833,600 可用字節(jié) Stderr: Stdout: Stderr: ' ls ' 不是內(nèi)部或外部命令,也不是可運(yùn)行的程序 或批處理文件。 Stdout: Windows IP 配置 以太網(wǎng)適配器 Bluetooth 網(wǎng)絡(luò)連接 2 : 媒體狀態(tài) . . . . . . . . . . . . : 媒體已斷開(kāi) 連接特定的 DNS 后綴 . . . . . . . : 以太網(wǎng)適配器 本地連接: 媒體狀態(tài) . . . . . . . . . . . . : 媒體已斷開(kāi) 連接特定的 DNS 后綴 . . . . . . . : 無(wú)線(xiàn)局域網(wǎng)適配器 無(wú)線(xiàn)網(wǎng)絡(luò)連接: 連接特定的 DNS 后綴 . . . . . . . : 本地鏈接 IPv6 地址. . . . . . . . : fe80::8c6:36a9:6fa6: 8018%14 IPv4 地址 . . . . . . . . . . . . : 192.168.43.216 子網(wǎng)掩碼 . . . . . . . . . . . . : 255.255.255.0 默認(rèn)網(wǎng)關(guān). . . . . . . . . . . . . : 192.168.43.1 隧道適配器 本地連接 * 3 : 媒體狀態(tài) . . . . . . . . . . . . : 媒體已斷開(kāi) 連接特定的 DNS 后綴 . . . . . . . : Stderr:
這種寫(xiě)法的好處就是能確定Sever到底要接受多少數(shù)據(jù),不好的地方就是多了一次交互
解決方案二: 使用struct模塊
用struct模塊我們能把一個(gè)數(shù)據(jù)類(lèi)型轉(zhuǎn)換成固定長(zhǎng)度的bytes
這里以數(shù)字類(lèi)型舉例,'i'代表int類(lèi)型:
import struct print (struct.pack( ' i ' ,2048),len(struct.pack( ' i ' ,2048))) # b'\x00\x08\x00\x00' 4 print (struct.pack( ' i ' ,204800),len(struct.pack( ' i ' ,204800))) # b'\x00 \x03\x00' 4 print (struct.pack( ' i ' ,2048000),len(struct.pack( ' i ' ,2048000))) # b'\x00@\x1f\x00' 4
當(dāng)后面的數(shù)值戳過(guò)一定范圍的時(shí)候程序就會(huì)報(bào)錯(cuò)
Sever:
import socket import struct sk = socket.socket() sk.bind(( ' 127.0.0.1 ' ,8080 )) sk.listen() conn,addr = sk.accept() while True: cmd = input( ' <<< ' ) if cmd == ' q ' : conn.send(b ' q ' ) break conn.send(cmd.encode( ' gbk ' )) num = conn.recv(4 ) num = struct.unpack( ' i ' ,num)[0] res = conn.recv(int(num)).decode( ' gbk ' ) print (res) conn.close() sk.close()
Client:
import socket import subprocess import struct sk = socket.socket() sk.connect(( ' 127.0.0.1 ' ,8080 )) while True: cmd = sk.recv(1024).decode( ' gbk ' ) if cmd == ' q ' : break res = subprocess.Popen(cmd,shell= True, stdout = subprocess.PIPE, stderr = subprocess.PIPE) std_out = res.stdout.read() std_err = res.stderr.read() len_num = len(std_out) + len(std_err) num_by = struct.pack( ' i ' ,len_num) print ( ' Stdout: ' + std_out.decode( ' gbk ' )) print ( ' Stderr: ' + std_err.decode( ' gbk ' )) sk.send(num_by) sk.send(std_out) sk.send(std_err) sk.close()
執(zhí)行結(jié)果:

<<< dir 驅(qū)動(dòng)器 C 中的卷是 系統(tǒng) 卷的序列號(hào)是 85C0 - 669A C:\Users\Administrator\PycharmProjects\Internet_program 的目錄 2019/09/17 16:25. 2019/09/17 16:25 .. 2019/09/17 16:23 .idea 2019/09/17 16:25 659 Client1.py 2019/09/16 13:42 0 Client2.py 2019/09/17 16:22 400 Sever1.py 2019/09/17 16:08 288 time_test.py 2019/09/14 23:51 venv 4 個(gè)文件 1,347 字節(jié) 4 個(gè)目錄 45,025,951,744 可用字節(jié) <<< configip ' configip ' 不是內(nèi)部或外部命令,也不是可運(yùn)行的程序 或批處理文件。 <<< ipconfig Windows IP 配置 以太網(wǎng)適配器 Bluetooth 網(wǎng)絡(luò)連接 2 : 媒體狀態(tài) . . . . . . . . . . . . : 媒體已斷開(kāi) 連接特定的 DNS 后綴 . . . . . . . : 以太網(wǎng)適配器 本地連接: 媒體狀態(tài) . . . . . . . . . . . . : 媒體已斷開(kāi) 連接特定的 DNS 后綴 . . . . . . . : 無(wú)線(xiàn)局域網(wǎng)適配器 無(wú)線(xiàn)網(wǎng)絡(luò)連接: 連接特定的 DNS 后綴 . . . . . . . : 本地鏈接 IPv6 地址. . . . . . . . : fe80::8c6:36a9:6fa6: 8018%14 IPv4 地址 . . . . . . . . . . . . : 192.168.43.216 子網(wǎng)掩碼 . . . . . . . . . . . . : 255.255.255.0 默認(rèn)網(wǎng)關(guān). . . . . . . . . . . . . : 192.168.43.1 隧道適配器 本地連接 * 3 : 媒體狀態(tài) . . . . . . . . . . . . : 媒體已斷開(kāi) 連接特定的 DNS 后綴 . . . . . . . : <<<

Stdout: 驅(qū)動(dòng)器 C 中的卷是 系統(tǒng) 卷的序列號(hào)是 85C0 - 669A C:\Users\Administrator\PycharmProjects\Internet_program 的目錄 2019/09/17 16:25. 2019/09/17 16:25 .. 2019/09/17 16:23 .idea 2019/09/17 16:25 659 Client1.py 2019/09/16 13:42 0 Client2.py 2019/09/17 16:22 400 Sever1.py 2019/09/17 16:08 288 time_test.py 2019/09/14 23:51 venv 4 個(gè)文件 1,347 字節(jié) 4 個(gè)目錄 45,025,951,744 可用字節(jié) Stderr: Stdout: Stderr: ' configip ' 不是內(nèi)部或外部命令,也不是可運(yùn)行的程序 或批處理文件。 Stdout: Windows IP 配置 以太網(wǎng)適配器 Bluetooth 網(wǎng)絡(luò)連接 2 : 媒體狀態(tài) . . . . . . . . . . . . : 媒體已斷開(kāi) 連接特定的 DNS 后綴 . . . . . . . : 以太網(wǎng)適配器 本地連接: 媒體狀態(tài) . . . . . . . . . . . . : 媒體已斷開(kāi) 連接特定的 DNS 后綴 . . . . . . . : 無(wú)線(xiàn)局域網(wǎng)適配器 無(wú)線(xiàn)網(wǎng)絡(luò)連接: 連接特定的 DNS 后綴 . . . . . . . : 本地鏈接 IPv6 地址. . . . . . . . : fe80::8c6:36a9:6fa6: 8018%14 IPv4 地址 . . . . . . . . . . . . : 192.168.43.216 子網(wǎng)掩碼 . . . . . . . . . . . . : 255.255.255.0 默認(rèn)網(wǎng)關(guān). . . . . . . . . . . . . : 192.168.43.1 隧道適配器 本地連接 * 3 : 媒體狀態(tài) . . . . . . . . . . . . : 媒體已斷開(kāi) 連接特定的 DNS 后綴 . . . . . . . : Stderr:
實(shí)現(xiàn)一個(gè)大文件的傳輸和下載
當(dāng)我們?cè)诰W(wǎng)絡(luò)上傳輸所有數(shù)據(jù)時(shí),這些數(shù)據(jù)都叫數(shù)據(jù)包,數(shù)據(jù)包里的所有數(shù)據(jù)都叫報(bào)文,報(bào)文里不止有你的數(shù)據(jù)還有IP地址、MAC地址、端口號(hào)等,所有的報(bào)文都有報(bào)頭,這是由協(xié)議規(guī)定的。所有在網(wǎng)絡(luò)上傳播數(shù)據(jù)包的協(xié)議里都有一個(gè)報(bào)頭。什么時(shí)候需要自己定制報(bào)文?比如說(shuō)復(fù)雜的應(yīng)用上就會(huì)應(yīng)用到、傳輸文件的時(shí)候(文件名、文件大小、文件類(lèi)型、存儲(chǔ)路徑等)...
其實(shí)在網(wǎng)絡(luò)傳輸?shù)倪^(guò)程當(dāng)中處處有協(xié)議,協(xié)議就是一堆報(bào)頭和報(bào)文(都由字節(jié)組成)。
Sever:
import socket import struct import json buffer = 1024 sk = socket.socket() sk.bind(( ' 127.0.0.1 ' ,8080 )) sk.listen() conn,addr = sk.accept() head_len = conn.recv(4 ) struct.unpack( ' i ' ,head_len)[0] json_head = conn.recv(head_len).decode( ' utf-8 ' ) head = json.loads(json_head) file_size = head[ ' fileSize ' ] with open(r ' dir\%s ' %head[ ' fileName ' ], ' wb ' ) as f: while file_size: if file_size >= buffer: content = conn.recv(buffer) f.write(content) file_size -= buffer else : content = conn.recv(buffer) f.write(content) break conn.close() sk.close()
Client:
import socket import struct import os import json sk = socket.socket() sk.connect(( ' 127.0.0.1 ' ,8080 )) buffer = 1024 head = { ' filePath ' : r ' C:\Users\Administrator\Desktop\專(zhuān)題\海報(bào)資料夾\專(zhuān)題海報(bào) ' , ' fileName ' : r ' 專(zhuān)題海報(bào) ' , ' fileSize ' : None} file_path = os.path.join(head[ ' filePath ' ],head[ ' fileName ' ]) file_size = os.path.getsize(file_path) head[ ' fileSize ' ] = file_size json_head = json.dumps(head) # 字典轉(zhuǎn)成字典 bytes_head = json_head.encode( ' utf-8 ' ) # 字符串轉(zhuǎn)bytes head_len = len(bytes_head) # 報(bào)頭的長(zhǎng)度 pack_len = struct.pack( ' i ' ,head_len) sk.send(pack_len) # 先發(fā)報(bào)頭的長(zhǎng)度 sk.send(bytes_head) # 再發(fā)bytes類(lèi)型的報(bào)頭 with open(r ' dir\%s ' %file_path, ' rb ' ) as f: while file_size: if file_size >= buffer: content = f.read(buffer) # 每次文件讀出的內(nèi)容 sk.send(content) file_size -= buffer else : content = f.read(file_size) sk.send(content) break sk.close()
更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號(hào)聯(lián)系: 360901061
您的支持是博主寫(xiě)作最大的動(dòng)力,如果您喜歡我的文章,感覺(jué)我的文章對(duì)您有幫助,請(qǐng)用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點(diǎn)擊下面給點(diǎn)支持吧,站長(zhǎng)非常感激您!手機(jī)微信長(zhǎng)按不能支付解決辦法:請(qǐng)將微信支付二維碼保存到相冊(cè),切換到微信,然后點(diǎn)擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對(duì)您有幫助就好】元
