Python網絡編程(一)


一、網絡基礎

在學習網絡編程前,要對網絡通信的五層協議有所了解,那什么是協議呢,協議就是各方規定遵守的一種標准。

網絡通信就像寄信件,是信息與數據的交換,而在生活中我們寄信件,信件也不是從我們手里瞬間到收件人手里,每一次信件通信,都會經歷這樣幾個固定流程:寫信、裝信封、投到郵箱、郵局取件、運輸到目的地郵局、目的地郵局根據詳細地址派送、收件人收件、拆信封、讀信。

網絡通信也是同樣的道理,數據的傳輸總有一定的流程:發送端程序將數據打包,給數據包印上目標地址,將數據包交給網關,通過路由轉發到達目的網絡,目的網絡網關在根據詳細地址分發、目的主機接收數據、拆包、讀數據。

這中間我們按照每部分負責的任務的不同,將整個通信划分為五層(分法不唯一,這是最易理解的一種),由下到上分別是物理層、數據鏈路層、網絡層、運輸層、應用層,每一層都將較下的一層完全封裝,消除通信雙方軟硬件的差別,而每一層又有約定好的規則以和對方通信,我們這篇文章所要講的,就是發生在運輸層的通信。

我們先來思考一個問題:一台主機的一個進程發送數據時,是如何標識它是發送給目標主機的哪個進程呢?目標主機接受到數據時,由如何判斷這份數據是發送給本機的哪個進程呢?

這就是運輸層的任務,也就是說,運輸層負責兩個主機中的應用進程之間的通信。那么上面的問題到底是怎么解決的呢?這就要提到一個新的概念:套接字。

套接字的概念很簡單,每台主機有一個唯一的主機地址標識,同時主機內還有標識自己進程的序號id,稱作端口,將這兩個標識符結合就構成了一個套接字(socket),這個套接字能唯一標識網絡中的一個進程。接下來我們的網絡編程,都是圍繞這個套接字展開的。

運輸層的協議主要有UDP和TCP協議,這兩個協議都是為了解決進程通信,它們的區別主要是這幾點:UDP是無連接、不可靠協議,TCP是有連接、可靠協議,連接指的是通信前要建立連接(連接提供了許多功能:確認、流量控制、連接管理等),可靠指的是如果數據未抵達有對應處理(不可靠則指只負責發送數據)。

這樣就了解的差不多了,當然,關於運輸層的技術還有很多,難點重點都沒有提到,有興趣的同學可以自己再學習。

 

二、通信模式

對我們來說,進行網絡通信的目的是為了獲取信息,而互聯網上的信息需求者遠遠多於信息提供者,不止如此,信息的提供者必須24小時立即回復需求者的請求,這樣就產生了客戶服務器模式。客戶服務器模式是一個邏輯概念,它將主機分為兩類,等待請求的是服務器端、發起請求的是客戶端,所以服務器端的套接字是必須是公開的,客戶端的套接字可以在請求時給服務器端。

 

三、實現

對UDP來說:因為不需要建立連接,所以只需要在兩台主機上各構造一個socket,就能直接發送和接收數據了。

對TCP來說:首先需要建立連接,一個連接就是一對手動綁定的socket,連接成功后就能收發數據了。

結合客戶服務端模式,對同一種協議,socket的構造方法也按客戶端和服務器分為兩種。

話不多說,上代碼!

這是基於TCP協議的:

#socket_server_tcp

# import socket
from socket import *
ip_port=('127.0.0.1',8080)
back_log=5
buffer_size=1024

tcp_server=socket(AF_INET,SOCK_STREAM)
tcp_server.bind(ip_port)
tcp_server.listen(back_log)

while True:
    print('服務端開始運行了')
    conn,addr=tcp_server.accept() #服務端阻塞
    print('雙向鏈接是',conn)
    print('客戶端地址',addr)

    while True:
        try:
            data=conn.recv(buffer_size)
            print('客戶端發來的消息是',data.decode('utf-8'))
            conn.send(data.upper())
        except Exception:
            break
    conn.close()

tcp_server.close()
#socket_client_tcp

# import socket
from socket import *
ip_port=('127.0.0.1',8080)
back_log=5
buffer_size=1024

tcp_client=socket(AF_INET,SOCK_STREAM)
tcp_client.connect(ip_port)

while True:
    msg=input('>>: ').strip()
    if not msg:continue
    tcp_client.send(msg.encode('utf-8'))
    print('客戶端已經發送消息')
    data=tcp_client.recv(buffer_size)
    print('收到服務端發來的消息',data.decode('utf-8'))

tcp_client.close()

接下來是基於UDP協議的:

# socket_server_udp

from socket import *
ip_port=('127.0.0.1',8080)
buffer_size=1024

udp_server=socket(AF_INET,SOCK_DGRAM) #數據報
udp_server.bind(ip_port)

while True:
    data,addr=udp_server.recvfrom(buffer_size)
    print('收到來自{}的消息:{}'.format(addr, data))

    udp_server.sendto(data.upper(),addr)
# socket_client_udp

from socket import *
ip_port=('127.0.0.1',8080)
buffer_size=1024

udp_client=socket(AF_INET,SOCK_DGRAM) #數據報

while True:
    msg=input('>>: ').strip()
    udp_client.sendto(msg.encode('utf-8'),ip_port)

    data,addr=udp_client.recvfrom(buffer_size)
    # print(data.decode('utf-8'))
    print('收到服務端發來的消息', data)

運行下試試!

這兩對代碼已經能實現基本的功能,但還有許多功能沒有實現,如TCP版不能同時連接多用戶,粘包問題(一次接收的數據如果大於緩存區大小就會把剩下的內容附加到下次接受的開頭),這些我們放到下次來講。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM