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

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > python >内容正文

python

python的网络编程用途_python---网络编程

發布時間:2024/3/26 python 37 豆豆
生活随笔 收集整理的這篇文章主要介紹了 python的网络编程用途_python---网络编程 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

一、軟件開發的架構

1: C/S架構

Client與Server? 客戶端與服務器端,這里的客戶端一般泛指客戶端應用EXE,程序需要先安裝后,才能運行在用戶的電腦上,對用戶的電腦操作系統環境依賴較大。

2: B/S架構

Browser與Server? 瀏覽器端與服務器端。

Browser瀏覽器,其實也是一種Client客戶端,只是這個客戶端不需要去安裝什么應用程序,只需在瀏覽器上通過HTTP請求服務器端相關的資源。

二、網絡基礎

IP地址:指互聯網協議地址。

IP地址是IP協會提供的一種統一的地址格式,它為互聯網上的每一個網絡和每一臺主機分配一個邏輯地址,以此來屏蔽物理地址的差異。

IP地址是一個32位的二進制數,通常被分割為4個‘8位二進制數’。

IP地址通常用“點分十進制”表示(a,b,c,d)的形式,其中,a,b,c,d都是0~255之間的十進制整數。

端口可以認為是設備與外界通訊交流的出口。

因此IP地址可以精確到具體的一臺電腦,而端口精確到具體的程序。

通過子網掩碼,我們就能判斷,任意兩個IP地址是否處在同一個子網絡。方法是將兩個IP地址與子網掩碼分別進行AND運算(兩個位數都是1,則結果1,反之0),然后比較結果是否相同,如果是的話,就表示它們在同一個子網絡中,否則就不是。

總結一下,IP協議的作用主要有兩個:一個是為每一臺計算機分配IP地址,另一個是確定哪些地址在同一個子網絡。

TCP協議

TCP---傳輸控制協議,提供的是面向連接、可靠的字節流服務。當客戶與服務器彼此交換數據前,必須先在雙方之間建立一個TCP連接,之后才能傳輸數據。TCP提供超時重發,丟棄重復數據,檢驗數據,流量控制等功能,保證數據能從一端傳輸到另一端。

可靠的、面向連接的協議(eg:打電話)、傳輸效率低全雙工通信(發送緩存&接收緩存)、面向字節流。使用TCP的應用:Web瀏覽器;電子郵件、文件傳輸程序。

TCP是因特網中的傳輸層協議,使用三次握手協議建立連接。當主動方發出SYN連接請求后,等待對方回答SYN+ACK[1],并最終對對方的 SYN 執行 ACK 確認。這種建立連接的方法可以防止產生錯誤的連接。[1]

TCP三次握手的過程如下:

客戶端發送SYN(SEQ=x)報文給服務器端,進入SYN_SEND狀態。

服務器端收到SYN報文,回應一個SYN (SEQ=y)ACK(ACK=x+1)報文,進入SYN_RECV狀態。

客戶端收到服務器端的SYN報文,回應一個ACK(ACK=y+1)報文,進入Established狀態。

三次握手完成,TCP客戶端和服務器端成功地建立連接,可以開始傳輸數據了。

tcp的三次握手

建立一個連接需要三次握手,而終止一個連接要經過四次握手,這是由TCP的半關閉(half-close)造成的。

(1) 某個應用進程首先調用close,稱該端執行“主動關閉”(active close)。該端的TCP于是發送一個FIN分節,表示數據發送完畢。

(2) 接收到這個FIN的對端執行 “被動關閉”(passive close),這個FIN由TCP確認。

注意:FIN的接收也作為一個文件結束符(end-of-file)傳遞給接收端應用進程,放在已排隊等候該應用進程接收的任何其他數據之后,因為,FIN的接收意味著接收端應用進程在相應連接上再無額外數據可接收。

(3) 一段時間后,接收到這個文件結束符的應用進程將調用close關閉它的套接字。這導致它的TCP也發送一個FIN。

(4) 接收這個最終FIN的原發送端TCP(即執行主動關閉的那一端)確認這個FIN。[1]

既然每個方向都需要一個FIN和一個ACK,因此通常需要4個分節。

注意:

(1) “通常”是指,某些情況下,步驟1的FIN隨數據一起發送,另外,步驟2和步驟3發送的分節都出自執行被動關閉那一端,有可能被合并成一個分節。[2]

(2) 在步驟2與步驟3之間,從執行被動關閉一端到執行主動關閉一端流動數據是可能的,這稱為“半關閉”(half-close)。

(3) 當一個Unix進程無論自愿地(調用exit或從main函數返回)還是非自愿地(收到一個終止本進程的信號)終止時,所有打開的描述符都被關閉,這也導致仍然打開的任何TCP連接上也發出一個FIN。

無論是客戶還是服務器,任何一端都可以執行主動關閉。通常情況是,客戶執行主動關閉,但是某些協議,例如,HTTP/1.0卻由服務器執行主動關閉。[2]

tcp的四次揮手

UDP協議

UDP---用戶數據報協議,是一個簡單的面向數據報的運輸層協議。UDP不提供可靠性,它只是把應用程序傳給IP層的數據報發送出去,但是并不能保證它們能到達目的地。由于UDP在傳輸數據報前不用在客戶和服務器之間建立一個連接,且沒有超時重發等機制,故而傳輸速度很快。

不可靠的、無連接的服務,傳輸效率高(發送前時延小),一對一、一對多、多對一、多對多、面向報文,盡最大努力服務,無擁塞控制。使用UDP的應用:域名系統?(DNS);視頻流;IP語音(VoIP)。

互聯網協議按照功能不同分為osi七層或tcp/ip五層或tcp/ip四層

每層運行常見的物理設備

傳輸層——> 四層交換機、四層的路由器

網絡層——>路由器、三層交換機

數據鏈路層——>網橋、以太網交換機、網卡

物理層——>中繼器、集線器、雙絞線

每層運行常見的協議

應用層——>。。。

傳輸層——>TCP與UDP協議

網絡層——>ip協議

數據鏈路層——>arp協議 ? (通過ip找mac地址)

物理層——>。。。

交換機:廣播 單播 組播

ip協議:ip地址的格式

# ip地址 一臺機器在一個網絡內唯一的標識

# 子網掩碼? ip地址與子網掩碼做按位與運算,得到的結果是網段

# 網關ip 局域網內的機器訪問公網ip,就通過網關訪問

三、socket

Socket是應用層與TCP/IP協議族通信的中間軟件抽象層,它是一組接口。在設計模式中,Socket其實就是一個門面模式,它把復雜的TCP/IP協議族隱藏在Socket接口后面,對用戶來說,一組簡單的接口就是全部,讓Socket去組織數據,以符合指定的協議。

socket和file的區別:

1:file模塊是針對某個指定文件進行打開、讀寫、關閉

2:socket模塊是針對服務器端和客戶端socket進行打開、讀寫、關閉

基于文件類型的套接字家族

套接字家族的名字:AF_UNIX

unix一切皆文件,基于文件的套接字調用的就是底層的文件系統來取數據,兩個套接字進程運行在同一機器,可以通過訪問同一個文件系統間接完成通信

基于網絡類型的套接字家族

套接字家族的名字:AF_INET

(還有AF_INET6被用于ipv6,還有一些其他的地址家族,不過,他們要么是只用于某個平臺,要么就是已經被廢棄,或者是很少被使用,或者是根本沒有實現,所有地址家族中,AF_INET是使用最廣泛的一個,python支持很多種地址家族,但是由于我們只關心網絡編程,所以大部分時候我么只使用AF_INET)

基于TCP協議的socket

tcp是基于鏈接的,必須先啟動服務端,然后再啟動客戶端去鏈接服務端

importsocket

sk=socket.socket()

sk.bind(('127.0.0.1',8898)) #把地址綁定到套接字

sk.listen() #監聽鏈接

conn,addr = sk.accept() #接受客戶端鏈接

ret = conn.recv(1024) #接收客戶端信息

print(ret) #打印客戶端信息

conn.send(b'hi') #向客戶端發送信息

conn.close() #關閉客戶端套接字

sk.close() #關閉服務器套接字(可選)

服務端

importsocket

sk= socket.socket() #創建客戶套接字

sk.connect(('127.0.0.1',8898)) #嘗試連接服務器

sk.send(b'hello!')

ret= sk.recv(1024) #對話(發送/接收)

print(ret)

sk.close()#關閉客戶套接字

客戶端

tcp實現與多個客戶端通信,必須結束一個客戶端,才能到下一個客戶端。

#服務器端

importsocket#tcp協議

sk = socket.socket() #創建一個socket對象

sk.bind(('127.0.0.1',8080)) #給server端綁定一個ip和端口

sk.listen()whileTrue:

conn,addr=sk.accept()whileTrue:

msg= conn.recv(1024).decode('utf-8') #阻塞,直到收到一個客戶端發來的消息

print(msg)if msg == 'bye':breakinfo= input('>>>')if info == 'bye':

conn.send(b'bye')breakconn.send(info.encode('utf-8')) #發消息

conn.close() #關閉連接

sk.close() #關閉socket對象,如果不關閉,還能繼續接收

#客戶端1

importsocket

sk=socket.socket()

sk.connect(('127.0.0.1',8080))whileTrue:

msg= input('>>>')if msg == 'bye':

sk.send(b'bye')breaksk.send(msg.encode('utf-8'))

ret= sk.recv(1024).decode('utf-8')if ret == 'bye':break

print(ret)

sk.close()#客戶端2

importsocket

sk=socket.socket()

sk.connect(('127.0.0.1',8080))whileTrue:

msg= input('client2:>>>')if msg == 'bye':

sk.send(b'bye')breaksk.send(('client2 :'+msg).encode('utf-8'))

ret= sk.recv(1024).decode('utf-8')if ret == 'bye':break

print(ret)

sk.close()

tcp服務器與多個客戶端通信

基于udp協議的socket

#服務器端

importsocket

sk= socket.socket(type=socket.SOCK_DGRAM) #DGRAM datagram

sk.bind(('127.0.0.1',8080)) #只有服務端有的

msg,addr= sk.recvfrom(1024)print(msg.decode('utf-8'))

sk.sendto(b'bye',addr)

sk.close()#udp的server 不需要進行監聽也不需要建立連接#在啟動服務之后只能被動的等待客戶端發送消息過來#客戶端發送消息的同時還會 自帶地址信息#消息回復的時候 不僅需要發送消息,還需要把對方的地址填寫上

#客戶端

importsocket

sk= socket.socket(type=socket.SOCK_DGRAM)

ip_port= ('127.0.0.1',8080)

sk.sendto(b'hello',ip_port)

ret,addr= sk.recvfrom(1024)print(ret.decode('utf-8'))

sk.close()#client端不需要connect 因為UDP協議是不需要建立連接的#直接了解到對方的ip和端口信息就發送數據就行了#sendto和recvfrom的使用方法是完全和server端一致的

#client端不需要connect 因為UDP協議是不需要建立連接的#直接了解到對方的ip和端口信息就發送數據就行了#sendto和recvfrom的使用方法是完全和server端一致的

基于udp協議的socket

#服務器端

importsocket

sk= socket.socket(type=socket.SOCK_DGRAM)

sk.bind(('127.0.0.1',8080))whileTrue:

msg,addr= sk.recvfrom(1024)print(addr)print(msg.decode('utf-8'))

info= input('>>>').encode('utf-8')

sk.sendto(info,addr)

sk.close()#客戶端1

importsocket

sk= socket.socket(type=socket.SOCK_DGRAM)

ip_port= ('127.0.0.1',8080)whileTrue:

info= input('tiger :')

info= ('\033[34m來自tiger的消息 :%s\033[0m'%info).encode('utf-8')

sk.sendto(info,ip_port)

msg,addr= sk.recvfrom(1024)print(msg.decode('utf-8'))

sk.close()#客戶端2

importsocket

sk= socket.socket(type=socket.SOCK_DGRAM)

ip_port= ('127.0.0.1',8080)whileTrue:

info= input('二哥 :')

info= ('\033[32m來自二哥的消息 :%s\033[0m'%info).encode('utf-8')

sk.sendto(info,ip_port)

msg,addr= sk.recvfrom(1024)print(msg.decode('utf-8'))

sk.close()

QQ聊天

#服務器端

importtimeimportsocket

sk= socket.socket(type=socket.SOCK_DGRAM)

sk.bind(('127.0.0.1',8090))whileTrue:

strf,addr= sk.recvfrom(1024)

strf= strf.decode('utf-8')

res= time.strftime(strf).encode('utf-8')

sk.sendto(res,addr)

sk.close()#客戶端

importsocket

sk= socket.socket(type=socket.SOCK_DGRAM)

addr= ('127.0.0.1',8090)

info= input('>>>').encode('utf-8')

sk.sendto(info,addr)

ret,addr= sk.recvfrom(1024)print(ret.decode('utf-8'))

sk.close()

格式化時間

服務端套接字函數

s.bind()?  綁定(主機,端口號)到套接字

s.listen() ?? 開始TCP監聽

s.accept()? 被打接收TCP客戶的連接,出錯時返回出錯碼,而不是拋出異常

客戶端套接字函數

s.connect()  主動初始化TCP服務器連接

s.connect_ex() ? ? connect()函數的擴展版本,出錯時返回出錯碼,而不是拋出異常

公共用途的套接字函數

s.recv()  接收TCP數據

s.send()  發送TCP數據(send在待發數據量大于幾端緩存區剩余空間時,數據丟失,不會發完)

s.sendall() 發送完整的TCP數據(本質就是循環調用send,sendall在待發數據量大于幾端緩存區剩余空間時,數據不丟失,循環調用send直到發完)

s.recvfrom()   接收UDP數據

s.sendto()    發送UDP數據

s.getpeername() 連接到當前套接字的遠端的地址

s.getsockname() 當前套接字的地址

s.getsockopt()  返回指定套接字的參數

s.setsockopt()  設置指定套接字的參數

s.close()     關閉套接字

面向鎖的套接字方法

s.setblocking()  設置套接字的阻塞與非阻塞模式

s.settimeout()   設置阻塞套接字操作的超時時間

s.gettimeout()   得到阻塞套接字操作的超時時間

面向文件的套接字的函數

s.fileno()    套接字的文件描述符

s.makefile()   ? 創建一個與該套接字相關的文件

四、黏包

黏包:同時執行多條命令之后,得到的結果很可能只有一部分,在執行其他命令的時候又接收到之前執行的另外一部分結果,這種顯現就是黏包。

只有TCP有黏包現象,UDP永遠不會黏包:

1.從表面上看,黏包問題主要是因為發送方和接收方的緩存機制、tcp協議面向流通信的特點。

2.實際上,主要還是因為接收方不知道消息之間的界限,不知道一次性提取多少個字節的數據所造成的。

用UDP協議發送時,用sendto函數最大能發送數據的長度為:65535- IP頭(20) – UDP頭(8)=65507字節。用sendto函數發送數據時,如果發送數據長度大于該值,則函數會返回錯誤。(丟棄這個包,不進行發送)

用TCP協議發送時,由于TCP是數據流協議,因此不存在包大小的限制(暫不考慮緩沖區的大小),這是指在用send函數時,數據長度參數不受限制。而實際上,所指定的這段數據并不一定會一次性發送出去,如果這段數據比較長,會被分段發送,如果比較短,可能會等待和下一次數據一起發送。發送方引起的粘包是由TCP協議本身造成的,TCP為提高傳輸效率,發送方往往要收集到足夠多的數據后才發送一個TCP段。若連續幾次需要send的數據都很少,通常TCP會根據優化算法把這些數據合成一個TCP段后一次發送出去,這樣接收方就收到了粘包數據。

會發生黏包的兩種情況:

1、發送方的緩存機制

發送端需要等緩沖區滿才發送出去,造成黏包(發送數據時間間隔很短,數據量很小,會合到一起,產生黏包)

importsocket

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()

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()

服務端

importsocket

BUFSIZE=1024ip_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'))

客戶端

2、接收方的緩存機制

接收方不及時接收緩沖區的包,造成多個包接收(客戶端發送了一段數據,服務端只收了一小部分,服務端下次再收的時候還是從緩沖區拿上次遺留的數據,產生黏包)

importsocket

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) #一次沒有收完整

data2=conn.recv(10)#下次收的時候,會先取舊的數據,然后取新的

print('----->',data1.decode('utf-8'))print('----->',data2.decode('utf-8'))

conn.close()

服務端

importsocket

BUFSIZE=1024ip_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'))

客戶端

黏包的解決方案

方案一

importsocket

sk=socket.socket()

sk.bind(('127.0.0.1',8080))

sk.listen()

conn,addr=sk.accept()whileTrue:

cmd= input('>>>')if cmd == 'q':

conn.send(b'q')breakconn.send(cmd.encode('gbk'))

num= conn.recv(1024).decode('utf-8') #2048

conn.send(b'ok')

res= conn.recv(int(num)).decode('gbk')print(res)

conn.close()

sk.close()

服務端

importsocketimportsubprocess

sk=socket.socket()

sk.connect(('127.0.0.1',8080))whileTrue:

cmd= sk.recv(1024).decode('gbk')if cmd == 'q':breakres= 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')) #2000

sk.recv(1024) #ok

sk.send(std_out)

sk.send(std_err)

sk.close()#好處:確定了我到底要接收多大的數據

#要在文件中配置一個配置項 : 就是每一次recv的大小 buffer = 4096

#當我們要發送大數據的時候 ,要明確的告訴接收方要發送多大的數據,以便接收方能夠準確的接收到所有數據

#多用在文件傳輸的過程中

#大文件的傳輸 一定是按照字節讀 每一次讀固定的字節

#傳輸的過程中 一邊讀一邊傳 接收端 一邊收一邊寫

#不好的地方:多了一次交互#send sendto 在超過一定范圍的時候 都會報錯#程序的內存管理

客戶端

方案二

借用struct模塊,該模塊可以把一個類型,如數字,轉成固定長度的bytes。我們知道長度數字可以被轉換成一個標準大小的4字節數字。因此可以利用這個特點來預先發送數據長度。

發送時:

先發送struct轉換好的數據長度4字節;再發送數據。

接收時:

先接受4個字節使用struct轉換成數字來獲取要接收的數據長度;再按照長度接收數據。

importsocket,struct,jsonimportsubprocess

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)whileTrue:

conn,addr=phone.accept()whileTrue:

cmd=conn.recv(1024)if not cmd:break

print('cmd: %s' %cmd)

res=subprocess.Popen(cmd.decode('utf-8'),

shell=True,

stdout=subprocess.PIPE,

stderr=subprocess.PIPE)

err=res.stderr.read()print(err)iferr:

back_msg=errelse:

back_msg=res.stdout.read()

conn.send(struct.pack('i',len(back_msg))) #先發back_msg的長度

conn.sendall(back_msg) #在發真實的內容

conn.close()

服務端(自定制報頭)

importsocket,time,struct

s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)

res=s.connect_ex(('127.0.0.1',8080))whileTrue:

msg=input('>>:').strip()if len(msg) == 0:continue

if msg == 'quit':breaks.send(msg.encode('utf-8'))

l=s.recv(4)

x=struct.unpack('i',l)[0]print(type(x),x)#print(struct.unpack('I',l))

r_s=0

data=b''

while r_s

r_d=s.recv(1024)

data+=r_d

r_s+=len(r_d)#print(data.decode('utf-8'))

print(data.decode('gbk')) #windows默認gbk編碼

客戶端(自定制報頭)

也可以把報頭坐車字典,字典里包含將要發送的真實數據的詳細信息,然后json序列化,再用struct將序列化后的數據長度打包成4個字節。

發送時:

先發報頭長度;再編碼報頭內容然后發送;最后發真實內容。

接收時:

先接收報頭長度,用struct取出來;根據取出的長度收取報頭內容,然后解碼,反序列化;最后從反序列化的結果中取出待取數據的詳細信息,最后去取真實的數據內容。

importsocket,struct,jsonimportsubprocess

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()whileTrue:

conn,addr=phone.accept()whileTrue:

cmd=conn.recv(1024)if not cmd:break

print('cmd: %s' %cmd)

res=subprocess.Popen(cmd.decode('utf-8'),

shell=True,

stdout=subprocess.PIPE,

stderr=subprocess.PIPE)

err=res.stderr.read()print(err)iferr:

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))) #先發報頭的長度

conn.send(head_json_bytes) #再發報頭

conn.sendall(back_msg) #在發真實的內容

conn.close()

服務端

importsocketimportstruct,json

ip_port=('127.0.0.1',8080)

client=socket(AF_INET,SOCK_STREAM)

client.connect(ip_port)whileTrue:

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=0

recv_data=b''

while recv_size

recv_data+=client.recv(1024)

recv_size+=len(recv_data)print(recv_data.decode('utf-8'))#print(recv_data.decode('gbk')) #windows默認gbk編碼

客戶端

總結

以上是生活随笔為你收集整理的python的网络编程用途_python---网络编程的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。