日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程语言 > python >内容正文

python

python之路-网络编程

發(fā)布時(shí)間:2025/5/22 python 28 豆豆
生活随笔 收集整理的這篇文章主要介紹了 python之路-网络编程 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

一. 楔子

  兩個(gè)程序之間想要傳遞一個(gè)數(shù)據(jù),需要用到網(wǎng)絡(luò)通信.

二. 軟件開(kāi)發(fā)的架構(gòu):

  第一種: 應(yīng)用類: qq,微信,網(wǎng)盤,優(yōu)酷 這一類是屬于需要安裝的桌面應(yīng)用.

  第二種: web類: 百度,知乎,博客園,等使用瀏覽器訪問(wèn)就可以直接使用的應(yīng)用.

?這些應(yīng)用的本質(zhì)其實(shí)就是兩個(gè)程序之間的通訊,而這兩個(gè)分類又對(duì)應(yīng)了兩個(gè)軟件開(kāi)發(fā)的架構(gòu).

  1.C/S 架構(gòu)

C/S即: client與server,中文意思: 客戶端與服務(wù)器端架構(gòu),這種架構(gòu)也是從用戶層面,(也是物理層面劃分的).

  2.B/S 架構(gòu)

B/S即:browser和server,中文意思:瀏覽器端與服務(wù)器端架構(gòu),這種架構(gòu)是從用戶層面來(lái)劃分.?

browser瀏覽器,其實(shí)也是一種client客戶端,只是這個(gè)客戶端不需要大家去安裝什么應(yīng)用程序,只是需要通過(guò)http請(qǐng)求服務(wù)器端相關(guān)的資源(網(wǎng)頁(yè)資源),客戶端browser瀏覽器就能進(jìn)行增刪改查.

三. 網(wǎng)絡(luò)基礎(chǔ).

  1. osi七層模型.

  2.socket概念

  socket是應(yīng)用層與TCP/IP協(xié)議族通信的中間軟件抽象層,他是一組接口.在設(shè)計(jì)模式中,socket其實(shí)就是一個(gè)門面模式,它把復(fù)雜的TCP/IP協(xié)議族隱藏在socket接口后面,對(duì)用戶來(lái)說(shuō),一組簡(jiǎn)單的接口就是全部,讓socket去組織數(shù)據(jù),以符合指定的協(xié)議.

其實(shí)站在你的角度上看,socket就是一個(gè)模塊.我們通過(guò)調(diào)用模塊中實(shí)現(xiàn)的方法
建立兩個(gè)進(jìn)程之間的連接和通信. 也有人將socket說(shuō)成ip
+port,因?yàn)閕p是用來(lái)標(biāo)識(shí)互聯(lián)網(wǎng)中的一臺(tái)主機(jī)的位置,
而port是用來(lái)標(biāo)識(shí)這臺(tái)機(jī)器上的一個(gè)應(yīng)用程序,
所以我們只要確立了ip和port就能找到一個(gè)應(yīng)用程序,并且使用socket模塊來(lái)與之通信.

  3. tcp協(xié)議和udp協(xié)議

TCP 可靠的.面向連接的協(xié)議,傳輸效率低全雙工通信(發(fā)送緩存,接收緩存),面向字節(jié)流.

使用TCP的應(yīng)用: web瀏覽器;電子郵件;文件傳輸程序.

UDP 不可靠的,無(wú)連接的服務(wù),傳輸效率高(發(fā)送錢延遲小),一對(duì)一,一對(duì)多,多對(duì)多,面向報(bào)文,盡最大努力服務(wù),無(wú)擁塞控制,使用UDP的應(yīng)用: 域名系統(tǒng)(DNS); 視頻流,IP語(yǔ)音.

?

?

四.套接字(socket)初使用.

1. 基于TCP協(xié)議的socket

?tcp是基于連接的,必須先啟動(dòng)服務(wù)器,然后再啟動(dòng)客戶端去連接服務(wù)器.

?server端

import socket sk = socket.socket() sk.bind(('127.0.0.1',8898)) #把地址綁定到套接字 sk.listen() #監(jiān)聽(tīng)鏈接 conn,addr = sk.accept() #接受客戶端鏈接 ret = conn.recv(1024) #接收客戶端信息 print(ret) #打印客戶端信息 conn.send(b'hi') #向客戶端發(fā)送信息 conn.close() #關(guān)閉客戶端套接字 sk.close() #關(guān)閉服務(wù)器套接字(可選) View Code

?client端

import socket sk = socket.socket() # 創(chuàng)建客戶套接字 sk.connect(('127.0.0.1',8000)) # 嘗試連接服務(wù)器 sk.send(b'hello') ret = sk.recv(1024) # 對(duì)話(發(fā)送/接收) print(ret) sk.close() # 關(guān)閉客戶套接字 View Code

?

  問(wèn)題: 重啟服務(wù)端時(shí)可能會(huì)遇到,

?解決方法:

# 加入一條socket配置,重用ip和端口. import socket from socket import SOL_SOCKET,SO_REUSEADDR sk = socket.socket() sk.setsockopt(SQL_SOCKET,SO_REUSEADDR,1) # 就是它,在bind前加. sk.bind('127.0.0.1',8000) #把地址綁定到套接字 sk.listen() #監(jiān)聽(tīng)鏈接 conn,addr = sk.accept() #接收客戶端鏈接 ret = conn.recv(1024) #接收客戶端信息 print(ret) # 打印客戶端信息 conn.send(b'hi') # 向客戶端發(fā)送信息 conn.close() # 關(guān)閉客戶端套接字 sk.close() # 關(guān)閉服務(wù)器套接字(可選) View Code

2.基于UDP協(xié)議的socket

udp是無(wú)鏈接的,啟動(dòng)服務(wù)之后可以直接接收消息,不需要提前建立鏈接.

簡(jiǎn)單使用

server端

import socket udp_sk = socket.socket(type=socket.SOCK_DGRAM) # 創(chuàng)建一個(gè)服務(wù)器的套接字 udp_sk.bind(('127.0.0.1',9000)) # 綁定服務(wù)器套接字 msg,addr = udp_sk.recvfrom(1024) print(msg) udp_sk.sendto(b'hi',addr) #對(duì)話(接收與發(fā)送) udp_sk.close() #關(guān)閉服務(wù)器套接字 View Code

client端

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) View Code

3. socket參數(shù)的詳解

socket.socket(family=AF_INET,type=SOCK_STEAM,proto=0,fileno=None)

  創(chuàng)建socket對(duì)象的參數(shù)說(shuō)明:

family地址系列應(yīng)為AF_INET(默認(rèn)值),AF_INET6,AF_UNIX,AF_CAN或AF_RDS。
(AF_UNIX 域?qū)嶋H上是使用本地 socket 文件來(lái)通信)
type套接字類型應(yīng)為SOCK_STREAM(默認(rèn)值),SOCK_DGRAM,SOCK_RAW或其他SOCK_常量之一。
SOCK_STREAM?是基于TCP的,有保障的(即能保證數(shù)據(jù)正確傳送到對(duì)方)面向連接的SOCKET,多用于資料傳送。?
SOCK_DGRAM?是基于UDP的,無(wú)保障的面向消息的socket,多用于在網(wǎng)絡(luò)上發(fā)廣播信息
proto協(xié)議號(hào)通常為0,可以省略,或者在地址族為AF_CAN的情況下,協(xié)議應(yīng)為CAN_RAW或CAN_BCM之一.
fileno如果指定了fileno,則其他參數(shù)將被忽略,導(dǎo)致帶有指定文件描述符的套接字返回。
與socket.fromfd()不同,fileno將返回相同的套接字,而不是重復(fù)的。
這可能有助于使用socket.close()關(guān)閉一個(gè)獨(dú)立的插座

?

五.黏包

1.黏包現(xiàn)象

讓我們基于tcp先制作一個(gè)遠(yuǎn)程執(zhí)行命名的程序(命令ls -l; llllll,pwd)

res = subprocess.Popen(cmd.decode('utf-8'),shell=True,stderr=subprocess.PIPE,stdout=subprocess.PIPE)結(jié)果的編碼是以當(dāng)前所在的系統(tǒng)為準(zhǔn)的,如果是windows,那么res.stdout.read()讀出來(lái)的就是gbk編碼的,在接收端需要gbk解碼且只能從管道里讀一次結(jié)果 View Code

同時(shí)執(zhí)行多條命令之后,得到的結(jié)果很可能這樣一部分,在執(zhí)行其他命令的時(shí)候又接收之前執(zhí)行的另一部分結(jié)果,這種顯現(xiàn)就是黏包.

2. 基于tcp協(xié)議實(shí)現(xiàn)的黏包

?tcp-server

tcp - serverfrom socket import * import subprocessip_port = ('127.0.0.1',8888) BUFSIZE = 1024tcp_socket_server = socket(AF_INET,SOCK_STREAM) tcp_socket_server.setsockopt(SOL_SOCKET,SO_REUSEADDR,1) tcp_socket_server.bind(ip_port) tcp_socket_server.listen(5)while True:cmd = conn.recv(BUFSIZE)if len(cmd) == 0:breakres = subprocess.Poen(cmd.decode('utf-8'),shell=True,stdout=subprocess.PIPE,stdin=subprocess.PIPE,stderr=subprocess.PIPE) stderr = res.stderr.read()stdout = res.stdout.read()conn.send(stderr)conn.send(stdout) View Code

tcp-client

# tcp-client import socket BUFSIZE = 1024 ip_port = ('127.0.0.1',8888)s = socket.socket(socket.AF_INET,socket.SOCK_STREAM) res = s.connect_ex(ip_port)while True:msg = input('>>:').strip()if len(msg) == 0:continueif msg == 'quit':breaks.send(msg.encode('utf-8'))act_res = s.recv(BUFSIZE)print(act_res.decode('utf-8'),end='') View Code

? 注意: 只要TCP有黏包現(xiàn)象.UDP永遠(yuǎn)不會(huì)黏包.

?3.黏包成因

? 1.TCP協(xié)議中的數(shù)據(jù)傳遞,

tcp協(xié)議的拆包機(jī)制.

當(dāng)發(fā)送端緩沖區(qū)的長(zhǎng)度大于網(wǎng)卡的MTU時(shí),tcp會(huì)將這次發(fā)送的數(shù)據(jù)拆成幾個(gè)數(shù)據(jù)包發(fā)送出去. MTU是Maximum Transmission Unit的縮寫. 意思是網(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)傳送,這樣會(huì)產(chǎn)生數(shù)據(jù)包碎片,增加丟包率,降低網(wǎng)絡(luò)速度.

面向流的通信特點(diǎn)和Nagle算法

TCP(transport control protocol,傳輸控制協(xié)議)是面向連接的,面向流的,提供高可靠性服務(wù)。 收發(fā)兩端(客戶端和服務(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ā)的消息不能為空,這就需要在客戶端和服務(wù)端都添加空消息的處理機(jī)制,防止程序卡住,而udp是基于數(shù)據(jù)報(bào)的,即便是你輸入的是空內(nèi)容(直接回車),也可以被發(fā)送,udp協(xié)議會(huì)幫你封裝上消息頭發(fā)送過(guò)去。 可靠黏包的tcp協(xié)議:tcp的協(xié)議數(shù)據(jù)不會(huì)丟,沒(méi)有收完包,下次接收,會(huì)繼續(xù)上次繼續(xù)接收,己端總是在收到ack時(shí)才會(huì)清除緩沖區(qū)內(nèi)容。數(shù)據(jù)是可靠的,但是會(huì)粘包。

?

?會(huì)發(fā)生黏包的兩種情況

情況一 , 發(fā)送方的緩存機(jī)制.

發(fā)送端需要等緩沖區(qū)滿才發(fā)送出去,造成黏包(發(fā)送數(shù)據(jù)時(shí)間間隔很短,數(shù)據(jù)了很小,會(huì)合到一起,產(chǎn)生黏包)

?服務(wù)端

from socket import * ip_port = ('127.0.0.1',8080)tcp_socket_server = socket(AF_INET,SOCK_STEARM) tcp_socket_server.bind(ip_port) tcp_socket_server.listen(5)conn,addr = tcp_socket_server.accept()data1 = conn.recv(10) data2 = conn.recv(10)print('----->',data1.decode('utf-8')) print('----->',data2.decode('utf-8'))conn.close() View Code

?客戶端

import socket BUFSIZE = 1024 ip_port = ('127.0.0.1',8080)s = socket.socket(socket.AF_INET,socket.SOCK_STREAM) res = s.connect_ex(ip_port)s.send('hello'.encode('utf-8')) s.send('egg'.encode('utf-8')) View Code

情況二,接收方的緩存機(jī)制

接收方不及時(shí)接收緩存區(qū)的包,造成多個(gè)包接收(客戶端發(fā)送了一段數(shù)據(jù),服務(wù)端只收了一小部分,服務(wù)端下次再收的時(shí)候還是從緩存區(qū)拿上次遺留的數(shù)據(jù),產(chǎn)生黏包)

?服務(wù)端

from socket import * ip_port = ('127.0.0.1',8080)tcp_socket_server = socket(AF_INET,SOCK_STREAM) tcp_socket_server.bind(ip_port) tcp_socket_server.listen(5)conn,addr = tcp_socket_server.accept()data1 = conn.recv(2) # 一次沒(méi)有收完整 data2 = conn,recv(10) # 下次收的時(shí)候,會(huì)先取舊的數(shù)據(jù),然后取新的.print('----->',data1.decode('utf-8')) print('----->',data2.decode('utf-8')) conn.close() View Code

?客戶端

import socket BUFSIZE = 1024 ip_port = ('127.0.0.1',8080)s = socket.socket(socket.AF_INET,socket.SOCK_STREAM) res = s.connect_ex(ip_port)s.send('hello egg'.encode('utf-8')) View Code

? 總結(jié):

  黏包現(xiàn)象只發(fā)生在tcp協(xié)議中:

  1.從表面上看,黏包問(wèn)題主要是因?yàn)榘l(fā)送方和接收方的緩存機(jī)制,tcp協(xié)議面向流通信的特點(diǎn).

  2. 實(shí)際上,主要還是因?yàn)榻邮辗讲恢老⒅g的界限,不知道一次性提取多少字節(jié)的數(shù)據(jù)造成的.

?

4.黏包的解決方案

?解決方案一

 問(wèn)題的根源在于,接收端不知道發(fā)送端將要傳送的字節(jié)流的長(zhǎng)度,所以解決黏包的方法就是圍繞,如何讓發(fā)送端在發(fā)送數(shù)據(jù)前,把自己將要發(fā)送的字節(jié)流總大小讓接收端知曉,然后接收端來(lái)一個(gè)死循環(huán)接收完所有數(shù)據(jù).

? ?服務(wù)端

import socket,subprocess ip_port = ('127.0.0.1',8080) s = socket.socket(socket.AF_INET,socket.SOCK_STRMAM) s.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)s.bind(ip_port) s.listen(5)while True:conn,addr = s.accept()print('客戶端',addr)while True:msg = conn.recv(1024)if not msg:breakres = subprocess.Popen(msg.decode('utf-8'),shell=True,stdin=subprocess,stderr=subprocess.PIPE,stdout=subprocess.PIPE)err = res.stderr.read()if err:ret = errelse:ret = res.stdout.read()data_length = len(ret)conn.send(str(data_length).encode('utf-8'))data = conn.recv(1024).decode('utf-8')if data == 'recv_ready':conn.sendall(ret)conn.close() View Code

? ?客戶端

import socket,time s = socket.socket(socket.AF_INET,socket.SOCK_STREAM) res = s.connect_ex(('127.0.0.1',8080))while True:msg = input('>>:').strip()if len(msg) == 0:continueif msg == 'quit':breaks.send(msg.encode('utf-8'))length = int(s.recv(1024).decode('utf-8'))s.send('recv_ready'.encode('utf-8'))send_size = 0recv_size = 0data = b''while recv_size < length:data += s.recv(1024)recv_size += len(data)print(data.decode('utf-8')) View Code

?存在的問(wèn)題:

  程序的運(yùn)行速度遠(yuǎn)快于網(wǎng)絡(luò)傳輸速度,所以在發(fā)送一段字節(jié)前,先用send去發(fā)送該字節(jié)流長(zhǎng)度,這種方式會(huì)放大網(wǎng)絡(luò)延遲帶來(lái)的性能損耗.

? ?5. 解決方案進(jìn)階.

? 剛剛的方法,問(wèn)題在于我們?cè)诎l(fā)送. 我們可以借助一個(gè)模塊,這個(gè)模塊可以要發(fā)送的數(shù)據(jù)長(zhǎng)度轉(zhuǎn)換成固定長(zhǎng)度的字節(jié),這樣客戶端每次接收消息之前只要先接收這個(gè)固定長(zhǎng)度字節(jié)的內(nèi)容看一看接下來(lái)要接收的信息大小,那么最終接收的數(shù)據(jù)只要達(dá)到這個(gè)值就停止,就能剛好不多不少的接收完整的數(shù)據(jù)了.

? struct模塊

?該模塊可以把一個(gè)類型,如數(shù)字,轉(zhuǎn)成固定長(zhǎng)度的bytes

>>> struct.pack('i',11111111) struct.error: 'i' format requires -214748348 <= number <= 2147483647 # 這個(gè)是范圍

?

# struct模塊 import json,struct # 假設(shè)通過(guò)客戶端上傳1T:10737418000的文件a.txt 為避免黏包,必須自定制報(bào)頭 header = {'file_size':1073741824000,'file_name':'/a/b/c/d/e/a.txt','md5':'8f6fbf8347faa4924a76856701edb0f3'} # 1T數(shù)據(jù),文件路徑和md5值.#為了該報(bào)頭能傳送,需要序列化并且轉(zhuǎn)為bytes head_bytes = bytes(json.dumps(header),encoding='utf-8') # 序列化并轉(zhuǎn)成bytes,用于傳輸# 為了讓客戶端知道報(bào)頭的長(zhǎng)度,用struck將報(bào)頭長(zhǎng)度這個(gè)數(shù)字轉(zhuǎn)成固定長(zhǎng)度:4個(gè)字節(jié) head_len_bytes = struct.pack('i',len(head_bytes)) # 這4個(gè)字節(jié)里只包含了一個(gè)數(shù)字,該數(shù)字是報(bào)頭的長(zhǎng)度#客戶端開(kāi)始發(fā)送 conn.send(head_len_bytes) #先發(fā)報(bào)頭的長(zhǎng)度,4個(gè)bytes conn.send(head_bytes) # 先發(fā)報(bào)頭的字節(jié)格式 conn.sendall(文件內(nèi)容) # 然后發(fā)真實(shí)內(nèi)容的字節(jié)格式#服務(wù)端開(kāi)始接收 head_len_bytes = s.recv(4) #先收?qǐng)?bào)頭4個(gè)bytes,得到報(bào)頭長(zhǎng)度的字節(jié)格式 x = struct.unpack('i',head_len_bytes)[0] #提取報(bào)頭的長(zhǎng)度 head_bytes = s.recv(x) #按照?qǐng)?bào)頭長(zhǎng)度x,收取報(bào)頭的bytes格式 header = json.loads(json.dumps(header)) #提取報(bào)頭#最后根據(jù)報(bào)頭的內(nèi)容提取真實(shí)的數(shù)據(jù),比如: real_data_len = s.recv(header['file_size']) s.recv(real_data_len)

? ?關(guān)于struct的詳細(xì)用法

# 關(guān)于struct的詳細(xì)用法 #http://www.cnblogs.com/coser/archive/2011/12/17/2291160.html __author__ = 'Linhaifeng' import struct import binascii import ctypesvalues1 = (1,'abc'.encode('utf-8',2.7)) values2 = ('defg'.encode('utf-8'),101) s1 = struct.Struct('I3sf') s2 = struct.Struct('4sI')print(s1.size,s2.size) prebuffer = ctypes.create_string_buffer(s1.size+s2.size) print('Before : ',binascii.hexlify(prebuffer)) # t = binascii.hexlify('asdfaf'.encode('utf-8')) # print(t) s1.pack_into(prebuffer,0,*values1) s2.pack_into(prebuffer,s1.size,*values2)print('After pack',binascii.hexlify(prebuffer)) print(s1.unpack_from(prebuffer,0)) print(s2.unpack_from(prebuffer,s1.size))s3 = struct.Struct('ii') s3.pack_into(prebuffer,0,123,123) print('After pack',binascii.hexlify(prebuffer)) print(s3.unpack_from(prebuffer,0)) View Code

? ?使用struct解決黏包

?借助struct模塊,我們知道長(zhǎng)度數(shù)字可以被轉(zhuǎn)換成一個(gè)標(biāo)準(zhǔn)大小的4字節(jié)數(shù)字,因此可以利用這個(gè)特點(diǎn)來(lái)預(yù)先發(fā)送數(shù)據(jù)長(zhǎng)度.

發(fā)送時(shí)接收時(shí)
先發(fā)送struct轉(zhuǎn)換好的數(shù)據(jù)長(zhǎng)度4字節(jié)先接收4個(gè)字節(jié)使用struct轉(zhuǎn)換成數(shù)字來(lái)獲取要接收的數(shù)據(jù)長(zhǎng)度
再發(fā)送數(shù)據(jù)再按照長(zhǎng)度接收數(shù)據(jù)

? 服務(wù)端(自定制報(bào)頭)

import socket,struct,json import subprocess phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM) phone.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) #就是它,在bind前加. phone.bind(('127.0.0.1',8080))phone.listen(5)while True:conn,addr = phone.accept()while True:cmd = conn,recv(1024)if not cmd:breakprint('cmd: %s ' % cmd)res = subprocess.Popen(cmd.decode('utf-8'),shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE)err = res.stderr.read()print(err)if err:back_msg = errelse:back_msg = res.stdout.read()conn.send(struct.pack('i',len(back_msg))) # 先發(fā)back_msg的長(zhǎng)度conn.sendall(back_msg) # 在發(fā)真實(shí)的內(nèi)容conn.close() View Code

? 客戶端(自定制報(bào)頭)

import socket,time,structs = socket.socket(socket.AF_INET,socket.SOCK_STREAM) res = s.connect_ex(('127.0.0.1',8080))while True:msg = input('>>:').strip()if len(msg) == 0:continueif msg == 'quit':breaks.send(msg.encode('utf-8'))l = s.recv(4)x = struct.unpack('i',1)[0]print(type(x),x)# print(struct.unpack('I',1))r_s = 0data = b''while r_s < x:r_d = s.recv(1024)data += r_dr_s += len(r_d)#print(data.decode('utf-8'))print(data.decode('gbk')) #windows默認(rèn)gbk編碼 View Code

?我們還可以把報(bào)頭做成字典,字典里包含將要發(fā)送的真實(shí)數(shù)據(jù)的詳細(xì)信息,然后json序列化,然后用struck將序列化后的數(shù)據(jù)長(zhǎng)度打包成4個(gè)字節(jié).

發(fā)送時(shí)接收時(shí)
先發(fā)報(bào)頭長(zhǎng)度先收?qǐng)?bào)頭長(zhǎng)度,用struct取出來(lái)
再編碼報(bào)頭內(nèi)容然后發(fā)送根據(jù)取出的長(zhǎng)度收取報(bào)頭內(nèi)容,然后解碼,反序列化
最后發(fā)真實(shí)內(nèi)容從反序列化的結(jié)果中取出待數(shù)據(jù)的詳細(xì)信息,然后去取真實(shí)的數(shù)據(jù)內(nèi)容

? 服務(wù)端: 定制稍微復(fù)雜一點(diǎn)的報(bào)頭.

import socket,struct,json import subprocess phone = socket.socket(sock,AF_INET,socket.SOCK_STREAM) phone.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) #就是它,在bind前加. phone.bind(('127.0.0.1',8080))phone.listen(5)while True:conn,addr = phone.accept()while True:cmd = conn.recv(1024)if not cmd:breakprint('cmd: %s '% cmd)res = subprocess.Popen(cmd.decode('utf-8'),shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE)err = res.stderr.read()print(err)if err:back_msg = errelse:back_msg = res.stdout.read()headers = {'data_size':len(back_msg)}head_json = json.dumps(headers)head_json_bytes = bytes(head_json,encoding='utf-8')conn.send(struct.pack('i',len(head_json_bytes))) # 先發(fā)報(bào)頭的長(zhǎng)度conn.send(head_json_bytes) # 再發(fā)報(bào)頭conn.sendall(back_msg) # 再發(fā)真實(shí)的內(nèi)容 conn.close() View Code

? 客戶端

from socket import * import struct,jsonip_port = ('127.0.0.1',8080) client = socket(AF_INET,SOCK_STREAM) client.connect(ip_port)while True:cmd = input('>>:')if not cmd:continueclient.send(bytes(cmd,encoding='utf-8'))head = client.recv(4)head_json_len = struct.unpack('i',head)[0]head_json = json.loads(client.recv(head_json_len).decode('utf-8'))data_len = head_json['data_size']recv_size = 0recv_data = b''while recv_size < data_len:recv_data += client.recv(1024)recv_size += len(recv_data)print(recv_data.decode('utf-8'))#print(recv_data.decode('gbk')) # windows默認(rèn)gbk編碼 View Code

?

轉(zhuǎn)載于:https://www.cnblogs.com/marry215464/p/9588158.html

總結(jié)

以上是生活随笔為你收集整理的python之路-网络编程的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。