康梅娟 郭狀先
(臨沂大學 山東臨沂 276000)
Socket 也叫作套接字,用來實現網絡通信[1]。它是網絡通信中應用層和傳輸層之間的一個抽象層[2]。使用套接字,可以將網絡中通信的主機之間復雜的通信過程簡單化,套接字可以為應用層提供一些接口,應用層使用套接字提供的接口可以實現網絡中的主機之間的通信。套接字包括 IP 地址和端口號兩個部分。通過網絡通信的每對進程需要使用一對套接字。不同的進程之間的通信所使用的套接字是不一樣的,套接字可以用來區分不同的進程之間的數據傳輸。套接字主要有目標IP、傳輸層使用的傳輸協議、傳輸層使用的端口號這3 個重要參數。Socket 是面向客戶/服務器模型而設計的[3]。通信的一方扮演客戶機的角色,另一方扮演服務器的角色。服務器在運行中一直監聽套接字指定的傳輸層端口,并等待著客戶機的連接請求。當服務器端收到客戶機發來的連接請求以后,服務器會接受客戶機的連接請求,雙方建立連接后,就可進行數據的傳遞。Socket 處于網絡協議的傳輸層[4]。
套接字可以分為流式套接字、原始套接字和數據報套接字這3種不同的類型。(1)流式套接字使用傳輸層的TCP協議,它能夠實現面向連接的、可靠的數據傳輸。(2)原始套接字主要用于一些協議的開發,可以進行比較底層的操作。(3)數據報套接字使用傳輸層的UDP 協議進行數據的傳輸,它實現的傳輸服務是無連接的、不可靠的。
該文主要介紹了網絡中的主機使用TCP套接字和UDP 套接字進行通信的流程,以及在Python 中是如何實現TCP套接字和UDP套接字編程的。
在Python 中有許多網絡編程框架,有原生的Socket調用如Select、Epoll,有標準庫如TCPServer、UDPServer、HTTPServer[5]。每種編程語言都擁有各自的特征和優勢,只有在合適的語言環境中,才能盡量發揮其功能和優勢[6]。Python作為一種網絡編程語言,它有很多庫實現了常見的網絡協議,它可以讓程序員專注于程序的邏輯,而不需要關心底層的通信,可以使用Python 的socket 模塊來引用套接字。要使用Python 實現套接字編程,需要導入socket 模塊或框架。該模塊由創建套接字并幫助它們彼此關聯所需的內置方法組成。一些重要的方法具體如下。
socket.socket():用于創建套接字,服務器和客戶端都需要創建套接字。
socket.accept():被動接受TCP 客戶的連接,阻塞式等待連接的到來。
socket.bind():用于將套接字綁定到指定為參數的地址。
socket.close():關閉套接字。
socket.connect():用于連接到指定為參數的遠程地址,主動初始化TCP 服務器連接。
socket.listen():使服務器能夠接受連接,開始TCP監聽。
socket.recv():接收TCP 數據。
socket.send():發送TCP 消息。
socket.sendall():發送完整的TCP數據。
sendto():發送UDP套接字的數據。
Recvfrom():接收UDP套接字的數據。
建立基于TCP 傳輸協議的服務器端連接的流程如下。
(1)調用socket方法創建套接字對象。
socket=socket.socket(netadd_type,socket_type)
socket 方法有兩個參數。netadd_type 表示IP 地址的類型,如果使用的是ipv4 地址,則需將其值設置為AF_INET。socket_type 表示要創建的套接字的類型,在此需要創建的套接字是流式套接字,需將其值設置為SOCK_STREAM,表示在傳輸層使用的TCP協議。
(2)使用bind 方法將已創建的套接字綁定到指定的地址和端口上。
socket.bind(site)
bind 方法有一個參數site,該參數是一個二元元組(socket_host,socket_port),socket_host 用來指定IP地址或主機名,127.0.0.1 是標準的IPv4 回環地址,socket_port表示套接字中的端口號。
(3)使用listen 方法在已創建的socket 句柄上建立監聽。
socket.listen(linknum)
listen 方法有一個參數linknum,該參數用來指定服務器允許的最多連接數。
(4)使用accept方法被動接受客戶機的連接請求。
client_socket,client_address=socket.accept()
accept 方法用來接受一個來自客戶機的連接請求,并返回一個新的套接字。服務器與此次接受的客戶機之間的通信是通過在這個返回的新的套接字上收發數據來完成的。accept 方法被調用后會返回一個二元元組,client_socket 表示返回的新的套接字對象,client_address 表示發出連接請求的客戶機的IP地址。
(5)通信雙方進入數據傳遞階段。
服務器可以使用send 或者sendall 方法向客戶機發送數據,服務器要想接收客戶機發來的數據可以使用recv方法。
(6)雙方通信結束以后,服務器使用close()關閉已連接的套接字。
創建基于TCP 傳輸協議的客戶端連接的流程如下。
(1)使用socket 方法創建一個用來和服務器建立連接的套接字。
client_socket=socket.socket(netadd_type,socket_type)
(2)調用connect()和服務器建立連接。
(3)客戶機使用send 或sendall 方法發送數據,使用recv方法接收數據。
(4)數據傳輸結束后,客戶機需要調用close()來關閉已連接的套接字。
編寫一個基于TCP 協議的客戶機與服務器程序,在Python的IDLE編譯器中運行,實現服務器與客戶機之間的相互通信,使發送程序和接收程序能夠接收鍵盤輸入并彼此之間相互發送數據。
服務器需要先創建TCP 套接字,綁定套接字到本地IP 與指定端口,接下來開始監聽,用一個while 循環一直監聽有沒有消息連接,并接受客戶端的連接請求,然后接收客戶端傳來的數據,并發送給對方數據,數據傳輸完畢后,關閉套接字。服務器端程序具體如下。

客戶端先創建TCP 套接字,申請和服務器建立連接,當和服務器成功建立連接后,開始收發數據,通信結束后,關閉已連接的套接字。客戶端程序如下所示。

首先執行服務器端編寫的程序,接下來執行客戶端編寫的程序,然后讓客戶機給服務器發送數據,再讓服務器給客戶機發送數據,以此循環,當客戶機想和服務器結束通信時,可以輸入“q”或“Q”。經過測試,服務器端程序和客戶端程序能夠接收鍵盤輸入并彼此之間相互發送數據,程序運行正常,得到了預期的效果。
由于UDP 協議是無連接的,UDP 套接字編程與TCP 套接字編程的區別在服務器端表現為,不需要使用監聽函數listen()對客戶端的連接進行監聽;在客戶端的表現為客戶端的socket不需要與服務器建立連接就能夠進行數據的發送與接收,即不需要使用connect()函數。
建立基于UDP 傳輸協議的服務器端連接的流程如下。
(1)使用socket()創建一個套接字對象。
socket=socket.socket(netadd_type,socket_type)
對于IPV4協議的UDP協議,netadd_type參數應取值為AF_INET;socket_type 參數應取值為SOCK_DGRAM,表示創建的套接字類型是數據報套接字,使用的數據傳輸協議是UDP協議。
(2)將socket綁定到指定地址上,具體內容如下。
socket.bind(address)
(3)服務器使用sendto()發送數據,使用recvfrom()接收數據。
(4)通信雙方的數據傳遞完畢后,服務器使用close()關閉已連接的套接字。
創建基于UDP 傳輸協議的客戶端連接的流程如下。
(1)客戶端使用socket方法創建一個套接字對象,用來和服務器建立連接。
(2)客戶端使用sendto方法向服務器發送數據,使用recvfrom方法接收服務器發來的數據。
(3)通信結束后,客戶端調用close 方法關閉已建立連接的套接字。
編寫一個基于UDP協議的客戶機與服務器程序,實現相互通信。
客戶端先創建UDP套接字,綁定套接字到本機IP和指定端口,然后開始循環收發消息,數據傳輸完畢之后,關閉套接字。服務器端程序如下。

客戶端先創建UDP 套接字,連接服務器端地址,連接后向服務器循環收發消息,等數據傳輸完畢后,關閉套接字。客戶端程序如下。

首先運行服務器端程序,接下來運行客戶端程序,然后雙方進行通信,雙方程序都能夠接收鍵盤輸入并彼此之間相互發送數據,程序運行正常,得到了預期的效果。
該文介紹了套接字的概念和分類,并通過實例介紹了如何使用Python開發基于TCP協議和UDP協議的聊天程序以及使用Python進行TCP套接字編程和UDP套接字編程的步驟。