王進文

摘 要:通過介紹Socket的基本組成結構及其發揮的作用,論述Socket通信的整個過程并用C語言加以簡要實現,以闡明Socket通信過程原理及相關系統調用。
關鍵詞:Socket;網絡通信;進程通信
中圖分類號:TP311 文獻標識碼:A
1 Socket基本組成結構
Socket是相同主機進程之間或者不同主機進程之間進行通信的主流手段之一,兩臺計算機之間的網絡通信可以通過在各自的系統中創建一個Socket,進而利用它來實現相互之間的通信。
那Socket究竟是什么呢?一個基本的Socket就是由本機IP,本機進程端口,目的IP,和目的進程端口,以及輸入輸出緩沖組成的一個數據結構。其中前四個屬性分別用來標識本機信息和目的計算機的信息,輸入輸出緩沖用來暫存保存通信的數據。
2 Socket通信連接步驟
Socket連接建立之前主要進行兩項工作,第一項是連接建立前的兩個Socket的初始化工作,第二個是兩臺計算機通信時的“三路握手”。下面詳細說明這兩個過程(假設兩臺通信的計算機為客戶端-服務器模型,調用中用到的參數,請自行查看相關API):
(1)兩個Socket的初始化。
服務器端
首先,用C語言通過系統調用socket()函數來創建套接口。通過以下程序段便可以建立一個用TCP的Socket:
int listensockfd=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP)
其次,通過系統調用int bind ()函數來初始化Socket中的本地IP地址和本地端口號。通過以下語句,便可以初始化服務器端的Socket:
bind( listensockfd, (struct sockaddr*) &saServer, sizeof(saServer));
最后,我們通過系統調用listen()函數來將已經綁定了本機IP地址和程序端口號的Socket的狀態由主動(positive)轉換為被動(passive)(只有處于被動的Socket才會接受對方的信息)。并調用accept()函數來獲取已經準備好的套接字準備收取數據。通過以下程序段,便可以啟動監聽Socket,并返回Q1隊列中和客戶端“三路握手”完成的套接字,如圖1所示。
listen( listensockfd, 5 );
int acceptSocket = accept( listenSocketfd, NULL, NULL );
客戶端
通過Socket系統調用建立Socket,然后初始化客戶端的IP地址和端口號,并通過connect()函數將初始化好的IP地址和端口號的數據結構綁定在新建的Socket上,與服務器端不同的是這個數據結構是為了初始化客戶端的IP地址和進程端口,如圖1所示。
(2)兩臺計算機通信時的“三路握手”
第一次握手:客戶端調用connect()函數將目的地(服務器端)的IP地址和進程端口初始化的同時,給內核運輸層發出指令,使其將封裝好(其中包含完整的四元組)的數據包(同步包,下面簡稱SYN包),通過更底層的協議層向目的地(服務器端)傳送,以發出請求。
第二次握手:當目的地(服務器端)收到客戶端發送的SYN包時,如果請求可以通過,服務器端也通過運輸層封裝好一個包含通過請求的SYN+ACK包,否則封裝一個拒絕請求的SYN+NACK包發送給客戶端,于此同時,服務器內核自動創建一個Socket,并將已將創建好的監聽套接口的本地IP地址和進程端口拷貝到新創建的Socket中的本地IP地址和進程端口中,將第一次握手過程中的SYN包中的客戶端的IP地址和端口號拷貝在新創建的Socket中的目的地IP地址和端口號中,然后將新創建的Socket放入監聽Socket中的Q0隊列(用于放置內核為服務器和不同客戶端通信創建的未完成“三路握手”Socket)中。
第三次握手:當客戶端收到服務器端回應的SYN+ACK包時,客戶端需要再返回給服務器一個SYN包表示已經收到SYN+ACK包,與此同時,服務器將剛才放入Q0隊列的Socket放入監聽套接口的Q1隊列中(用于放置內核為服務器和不同客戶端通信創建的已經完成“三路握手”Socket),而客戶端通過系統調用accept()正是獲取的Q1隊列中的套接字。如圖2所示。
3 通過socke進行通信
服務器端通過系統調用recv()進行數據的獲取,客戶端可以通過調用send()進行數據的發送。
以下便是整個Socket通信的流程圖,如圖2所示。
參考文獻
[1] Jesse Storimer. TCP Sockets編程[M].北京:人民郵電出版社,2013.