康梅娟 郭狀先
(臨沂大學(xué) 山東臨沂 276000)
Socket 也叫作套接字,用來實現(xiàn)網(wǎng)絡(luò)通信[1]。它是網(wǎng)絡(luò)通信中應(yīng)用層和傳輸層之間的一個抽象層[2]。使用套接字,可以將網(wǎng)絡(luò)中通信的主機(jī)之間復(fù)雜的通信過程簡單化,套接字可以為應(yīng)用層提供一些接口,應(yīng)用層使用套接字提供的接口可以實現(xiàn)網(wǎng)絡(luò)中的主機(jī)之間的通信。套接字包括 IP 地址和端口號兩個部分。通過網(wǎng)絡(luò)通信的每對進(jìn)程需要使用一對套接字。不同的進(jìn)程之間的通信所使用的套接字是不一樣的,套接字可以用來區(qū)分不同的進(jìn)程之間的數(shù)據(jù)傳輸。套接字主要有目標(biāo)IP、傳輸層使用的傳輸協(xié)議、傳輸層使用的端口號這3 個重要參數(shù)。Socket 是面向客戶/服務(wù)器模型而設(shè)計的[3]。通信的一方扮演客戶機(jī)的角色,另一方扮演服務(wù)器的角色。服務(wù)器在運行中一直監(jiān)聽套接字指定的傳輸層端口,并等待著客戶機(jī)的連接請求。當(dāng)服務(wù)器端收到客戶機(jī)發(fā)來的連接請求以后,服務(wù)器會接受客戶機(jī)的連接請求,雙方建立連接后,就可進(jìn)行數(shù)據(jù)的傳遞。Socket 處于網(wǎng)絡(luò)協(xié)議的傳輸層[4]。
套接字可以分為流式套接字、原始套接字和數(shù)據(jù)報套接字這3種不同的類型。(1)流式套接字使用傳輸層的TCP協(xié)議,它能夠?qū)崿F(xiàn)面向連接的、可靠的數(shù)據(jù)傳輸。(2)原始套接字主要用于一些協(xié)議的開發(fā),可以進(jìn)行比較底層的操作。(3)數(shù)據(jù)報套接字使用傳輸層的UDP 協(xié)議進(jìn)行數(shù)據(jù)的傳輸,它實現(xiàn)的傳輸服務(wù)是無連接的、不可靠的。
該文主要介紹了網(wǎng)絡(luò)中的主機(jī)使用TCP套接字和UDP 套接字進(jìn)行通信的流程,以及在Python 中是如何實現(xiàn)TCP套接字和UDP套接字編程的。
在Python 中有許多網(wǎng)絡(luò)編程框架,有原生的Socket調(diào)用如Select、Epoll,有標(biāo)準(zhǔn)庫如TCPServer、UDPServer、HTTPServer[5]。每種編程語言都擁有各自的特征和優(yōu)勢,只有在合適的語言環(huán)境中,才能盡量發(fā)揮其功能和優(yōu)勢[6]。Python作為一種網(wǎng)絡(luò)編程語言,它有很多庫實現(xiàn)了常見的網(wǎng)絡(luò)協(xié)議,它可以讓程序員專注于程序的邏輯,而不需要關(guān)心底層的通信,可以使用Python 的socket 模塊來引用套接字。要使用Python 實現(xiàn)套接字編程,需要導(dǎo)入socket 模塊或框架。該模塊由創(chuàng)建套接字并幫助它們彼此關(guān)聯(lián)所需的內(nèi)置方法組成。一些重要的方法具體如下。
socket.socket():用于創(chuàng)建套接字,服務(wù)器和客戶端都需要創(chuàng)建套接字。
socket.accept():被動接受TCP 客戶的連接,阻塞式等待連接的到來。
socket.bind():用于將套接字綁定到指定為參數(shù)的地址。
socket.close():關(guān)閉套接字。
socket.connect():用于連接到指定為參數(shù)的遠(yuǎn)程地址,主動初始化TCP 服務(wù)器連接。
socket.listen():使服務(wù)器能夠接受連接,開始TCP監(jiān)聽。
socket.recv():接收TCP 數(shù)據(jù)。
socket.send():發(fā)送TCP 消息。
socket.sendall():發(fā)送完整的TCP數(shù)據(jù)。
sendto():發(fā)送UDP套接字的數(shù)據(jù)。
Recvfrom():接收UDP套接字的數(shù)據(jù)。
建立基于TCP 傳輸協(xié)議的服務(wù)器端連接的流程如下。
(1)調(diào)用socket方法創(chuàng)建套接字對象。
socket=socket.socket(netadd_type,socket_type)
socket 方法有兩個參數(shù)。netadd_type 表示IP 地址的類型,如果使用的是ipv4 地址,則需將其值設(shè)置為AF_INET。socket_type 表示要創(chuàng)建的套接字的類型,在此需要創(chuàng)建的套接字是流式套接字,需將其值設(shè)置為SOCK_STREAM,表示在傳輸層使用的TCP協(xié)議。
(2)使用bind 方法將已創(chuàng)建的套接字綁定到指定的地址和端口上。
socket.bind(site)
bind 方法有一個參數(shù)site,該參數(shù)是一個二元元組(socket_host,socket_port),socket_host 用來指定IP地址或主機(jī)名,127.0.0.1 是標(biāo)準(zhǔn)的IPv4 回環(huán)地址,socket_port表示套接字中的端口號。
(3)使用listen 方法在已創(chuàng)建的socket 句柄上建立監(jiān)聽。
socket.listen(linknum)
listen 方法有一個參數(shù)linknum,該參數(shù)用來指定服務(wù)器允許的最多連接數(shù)。
(4)使用accept方法被動接受客戶機(jī)的連接請求。
client_socket,client_address=socket.accept()
accept 方法用來接受一個來自客戶機(jī)的連接請求,并返回一個新的套接字。服務(wù)器與此次接受的客戶機(jī)之間的通信是通過在這個返回的新的套接字上收發(fā)數(shù)據(jù)來完成的。accept 方法被調(diào)用后會返回一個二元元組,client_socket 表示返回的新的套接字對象,client_address 表示發(fā)出連接請求的客戶機(jī)的IP地址。
(5)通信雙方進(jìn)入數(shù)據(jù)傳遞階段。
服務(wù)器可以使用send 或者sendall 方法向客戶機(jī)發(fā)送數(shù)據(jù),服務(wù)器要想接收客戶機(jī)發(fā)來的數(shù)據(jù)可以使用recv方法。
(6)雙方通信結(jié)束以后,服務(wù)器使用close()關(guān)閉已連接的套接字。
創(chuàng)建基于TCP 傳輸協(xié)議的客戶端連接的流程如下。
(1)使用socket 方法創(chuàng)建一個用來和服務(wù)器建立連接的套接字。
client_socket=socket.socket(netadd_type,socket_type)
(2)調(diào)用connect()和服務(wù)器建立連接。
(3)客戶機(jī)使用send 或sendall 方法發(fā)送數(shù)據(jù),使用recv方法接收數(shù)據(jù)。
(4)數(shù)據(jù)傳輸結(jié)束后,客戶機(jī)需要調(diào)用close()來關(guān)閉已連接的套接字。
編寫一個基于TCP 協(xié)議的客戶機(jī)與服務(wù)器程序,在Python的IDLE編譯器中運行,實現(xiàn)服務(wù)器與客戶機(jī)之間的相互通信,使發(fā)送程序和接收程序能夠接收鍵盤輸入并彼此之間相互發(fā)送數(shù)據(jù)。
服務(wù)器需要先創(chuàng)建TCP 套接字,綁定套接字到本地IP 與指定端口,接下來開始監(jiān)聽,用一個while 循環(huán)一直監(jiān)聽有沒有消息連接,并接受客戶端的連接請求,然后接收客戶端傳來的數(shù)據(jù),并發(fā)送給對方數(shù)據(jù),數(shù)據(jù)傳輸完畢后,關(guān)閉套接字。服務(wù)器端程序具體如下。
客戶端先創(chuàng)建TCP 套接字,申請和服務(wù)器建立連接,當(dāng)和服務(wù)器成功建立連接后,開始收發(fā)數(shù)據(jù),通信結(jié)束后,關(guān)閉已連接的套接字??蛻舳顺绦蛉缦滤尽?/p>
首先執(zhí)行服務(wù)器端編寫的程序,接下來執(zhí)行客戶端編寫的程序,然后讓客戶機(jī)給服務(wù)器發(fā)送數(shù)據(jù),再讓服務(wù)器給客戶機(jī)發(fā)送數(shù)據(jù),以此循環(huán),當(dāng)客戶機(jī)想和服務(wù)器結(jié)束通信時,可以輸入“q”或“Q”。經(jīng)過測試,服務(wù)器端程序和客戶端程序能夠接收鍵盤輸入并彼此之間相互發(fā)送數(shù)據(jù),程序運行正常,得到了預(yù)期的效果。
由于UDP 協(xié)議是無連接的,UDP 套接字編程與TCP 套接字編程的區(qū)別在服務(wù)器端表現(xiàn)為,不需要使用監(jiān)聽函數(shù)listen()對客戶端的連接進(jìn)行監(jiān)聽;在客戶端的表現(xiàn)為客戶端的socket不需要與服務(wù)器建立連接就能夠進(jìn)行數(shù)據(jù)的發(fā)送與接收,即不需要使用connect()函數(shù)。
建立基于UDP 傳輸協(xié)議的服務(wù)器端連接的流程如下。
(1)使用socket()創(chuàng)建一個套接字對象。
socket=socket.socket(netadd_type,socket_type)
對于IPV4協(xié)議的UDP協(xié)議,netadd_type參數(shù)應(yīng)取值為AF_INET;socket_type 參數(shù)應(yīng)取值為SOCK_DGRAM,表示創(chuàng)建的套接字類型是數(shù)據(jù)報套接字,使用的數(shù)據(jù)傳輸協(xié)議是UDP協(xié)議。
(2)將socket綁定到指定地址上,具體內(nèi)容如下。
socket.bind(address)
(3)服務(wù)器使用sendto()發(fā)送數(shù)據(jù),使用recvfrom()接收數(shù)據(jù)。
(4)通信雙方的數(shù)據(jù)傳遞完畢后,服務(wù)器使用close()關(guān)閉已連接的套接字。
創(chuàng)建基于UDP 傳輸協(xié)議的客戶端連接的流程如下。
(1)客戶端使用socket方法創(chuàng)建一個套接字對象,用來和服務(wù)器建立連接。
(2)客戶端使用sendto方法向服務(wù)器發(fā)送數(shù)據(jù),使用recvfrom方法接收服務(wù)器發(fā)來的數(shù)據(jù)。
(3)通信結(jié)束后,客戶端調(diào)用close 方法關(guān)閉已建立連接的套接字。
編寫一個基于UDP協(xié)議的客戶機(jī)與服務(wù)器程序,實現(xiàn)相互通信。
客戶端先創(chuàng)建UDP套接字,綁定套接字到本機(jī)IP和指定端口,然后開始循環(huán)收發(fā)消息,數(shù)據(jù)傳輸完畢之后,關(guān)閉套接字。服務(wù)器端程序如下。
客戶端先創(chuàng)建UDP 套接字,連接服務(wù)器端地址,連接后向服務(wù)器循環(huán)收發(fā)消息,等數(shù)據(jù)傳輸完畢后,關(guān)閉套接字??蛻舳顺绦蛉缦隆?/p>
首先運行服務(wù)器端程序,接下來運行客戶端程序,然后雙方進(jìn)行通信,雙方程序都能夠接收鍵盤輸入并彼此之間相互發(fā)送數(shù)據(jù),程序運行正常,得到了預(yù)期的效果。
該文介紹了套接字的概念和分類,并通過實例介紹了如何使用Python開發(fā)基于TCP協(xié)議和UDP協(xié)議的聊天程序以及使用Python進(jìn)行TCP套接字編程和UDP套接字編程的步驟。