《網路編程》基本 TCP 通訊端編程

來源:互聯網
上載者:User

標籤:tcp 通訊端   通訊端編程   網路編程   

        在進行通訊端編程之前必須熟悉其地址結構,有關通訊端的地址結構可參考文章《通訊端編程簡介》。基於 TCP 的通訊端編程的所有用戶端和伺服器端都是從調用socket 開始,它返回一個通訊端描述符。用戶端隨後調用connect 函數,伺服器端則調用 bind、listen 和accept 函數。通訊端通常使用標準的close 函數關閉,但是也可以使用 shutdown 函數關閉通訊端。下面針對通訊端編程實現過程中所調用的函數進程分析。以下是基於 TCP 通訊端編程的流程圖:





socket 函數

        通訊端是通訊端點的抽象,實現端對端之間的通訊。與應用程式要使用檔案描述符訪問檔案一樣,訪問通訊端需要通訊端描述符。任何通訊端編程都必須調用socket 函數獲得通訊端描述符,這樣才能對通訊端進行操作。以下是該函數的描述:

/* 通訊端 *//* * 函數功能:建立通訊端描述符; * 傳回值:若成功則返回通訊端非負描述符,若出錯返回-1; * 函數原型: */#include <sys/socket.h>int socket(int family, int type, int protocol);/* * 說明: * socket類似與open對普通檔案操作一樣,都是返回描述符,後續的操作都是基於該描述符; * family 表示通訊端的通訊域,不同的取值決定了socket的地址類型,其一般取值如下: * (1)AF_INET         IPv4網際網路域 * (2)AF_INET6        IPv6網際網路域 * (3)AF_UNIX         Unix域 * (4)AF_ROUTE        路由通訊端 * (5)AF_KEY          密鑰通訊端 * (6)AF_UNSPEC       未指定 * * type確定socket的類型,常用類型如下: * (1)SOCK_STREAM     有序、可靠、雙向的連線導向位元組流通訊端 * (2)SOCK_DGRAM      長度固定的、不需連線的不可靠資料通訊端 * (3)SOCK_RAW        原始通訊端 * (4)SOCK_SEQPACKET  長度固定、有序、可靠的連線導向的有序分組通訊端 * * protocol指定協議,常用取值如下: * (1)0               選擇type類型對應的預設協議 * (2)IPPROTO_TCP     TCP傳輸協議 * (3)IPPROTO_UDP     UDP傳輸協議 * (4)IPPROTO_SCTP    SCTP傳輸協議 * (5)IPPROTO_TIPC    TIPC傳輸協議 * */


connect 函數

         在處理連線導向的網路服務時,例如 TCP ,交換資料之前必須在請求的進程通訊端和提供服務的進程通訊端之間建立串連。TCP 用戶端可以調用函數connect 來建立與 TCP 伺服器端的一個串連。該函數的描述如下:

/* * 函數功能:建立串連,即用戶端使用該函數來建立與伺服器的串連; * 傳回值:若成功則返回0,出錯則返回-1; * 函數原型: */#include <sys/socket.h>int connect(int sockfd, const struct sockaddr *servaddr, socklen_t addrlen);/* * 說明: * sockfd是系統調用的通訊端描述符,即由socket函數返回的通訊端描述符; * servaddr是目的通訊端的地址,該通訊端地址結構必須包含目的IP地址和目的連接埠號碼,即想與之通訊的伺服器位址; * addrlen是目的通訊端地址的大小; * * 如果sockfd沒有綁定到一個地址,connect會給調用者綁定一個預設地址,即核心會確定源IP地址,並選擇一個臨時連接埠號碼作為源連接埠號碼; */
         TCP 用戶端在調用函數 connect 前不必非得調用 bind 函數,因為核心會確定源 IP 位址,並選擇一個臨時連接埠作為源連接埠號碼。若 TCP 通訊端調用connect 函數將建立 TCP 串連(執行三向交握),而且僅在串連建立成功或出錯時才返回,其中出錯返回可能有以下幾種情況:

  1. 若 TCP 用戶端沒有收到 SYN 報文段的響應,則返回 ETIMEOUT 錯誤;
  2. 若用戶端的 SYN 報文段的響應是 RST (表示複位),則表明該伺服器主機在我們指定的連接埠上沒有進程在等待與之串連。只是一種硬性錯誤,用戶端一接收到 RST 就立即返回ECONNERFUSED 錯誤;
    • RST 是 TCP 在發生錯誤時發送的一種 TCP 報文段。產生 RST 的三個條件時:
      • 目的地為某連接埠的 SYN 到達,然而該連接埠上沒有正在監聽的伺服器;
      • TCP 想取消一個已有串連;
      • TCP 接收到一個不存在的串連上的報文段;
  3. 若用戶端發出的 SYN 在中某個路由器上引發一個目的地不可達的 ICMP 錯誤,這是一個軟性錯誤。用戶端主機核心儲存該訊息,並在一定的時間間隔繼續發送 SYN (即重發)。在某規定的時間後仍未收到響應,則把儲存的訊息(即 ICMP 錯誤)作為EHOSTUNREACH 或ENETUNREACH 錯誤返回給進行。


bind 函數

        調用函數 socket 建立通訊端描述符時,該通訊端描述符是儲存在它的協議族空間中,沒有具體的地址,要使它與一個地址相關聯,可以調用函數 bind 使其與地址綁定。用戶端的通訊端關聯的地址一般可由系統預設分配,因此不需要指定具體的地址。若要為伺服器端通訊端綁定地址,可以通過調用函數 bind 將通訊端綁定到一個地址。下面是該函數的描述:

/* 通訊端的基本操作 *//* * 函數功能:將協議地址綁定到一個通訊端;其中協議地址包含IP地址和連接埠號碼; * 傳回值:若成功則返回0,若出錯則返回-1; * 函數原型: */#include <sys/socket.h>int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);/* * 說明: * sockfd 為通訊端描述符; * addr是一個指向特定協議地址結構的指標; * addrlen是地址結構的長度; */

        對於 TCP 協議,調用 bind 函數可以指定一個連接埠號碼,或指定一個 IP 位址,也可以兩者都指定,還可以都不指定。若 TCP 用戶端或伺服器端不調用bind 函數綁定一個連接埠號碼,當調用connect 或 listen 函數時,核心會為相應的通訊端選擇一個臨時連接埠號碼。一般 TCP 用戶端使用核心為其選擇一個臨時的連接埠號碼,而伺服器端通過調用bind 函數將連接埠號碼與相應的通訊端綁定。進程可以把一個特定的 IP 位址捆綁到它的通訊端上,但是這個 IP 位址必須屬於其所在主機的網路介面之一。對於 TCP 用戶端,這就為在通訊端上發送的 IP 資料報指派了源 IP 位址。對於 TCP 伺服器端,這就限定該通訊端只接收那些目的地為這個 IP 位址的用戶端串連。TCP 用戶端一般不把 IP 位址捆綁到它的通訊端上。當串連通訊端時,核心將根據所用外出網路介面來選擇源 IP 位址,而所用外出介面則取決於到達伺服器端所需的路徑。若 TCP 伺服器端沒有把 IP 位址捆綁到它的通訊端上,核心就把用戶端發送的 SYN 的目的 IP 位址作為伺服器端的源 IP 位址。

   在地址使用方面有下面一些限制:

  1. 在進程所啟動並執行機器上,指定的地址必須有效,不能指定其他機器的地址;
  2. 地址必須和建立通訊端時的地址族所支援的格式相匹配;
  3. 連接埠號碼必須不小於1024,除非該進程具有相應的特權(超級使用者);
  4. 一般只有通訊端端點能夠與地址綁定,儘管有些協議允許多重綁定;

listen 函數

        在編寫伺服器程式時需要使用監聽函數 listen 。伺服器處理序不知道要與誰串連,因此,它不會主動地要求與某個進程串連,只是一直監聽是否有其他客戶進程與之串連,然後響應該串連請求,並對它做出處理,一個服務進程可以同時處理多個客戶進程的串連。listen 函數描述如下:

/* * 函數功能:接收串連請求; * 函數原型: */#include <sys/socket.h>int listen(int sockfd, int backlog);//若成功則返回0,若出錯則返回-1;/* * sockfd是通訊端描述符; * backlog是該進程所要入隊請求的最大請求數量; */

listen 函數僅由 TCP 伺服器調用,它有以下兩種作用:

  1. 當 socket 函數建立一個通訊端時,若它被假設為一個主動通訊端,即它是一個將調用connect 發起串連的用戶端通訊端。listen 函數把一個未串連的通訊端轉換成一個被動通訊端,指示核心應該接受指向該通訊端的串連請求;
  2. listen 函數的第二個參數規定核心應該為相應通訊端排隊的最大串連個數;

listen 函數一般應該在調用socket 和bind 這兩個函數之後,並在調用accept 函數之前調用。 核心為任何一個給定監聽通訊端維護兩個隊列:

  1. 未完成串連隊列,每個這樣的 SYN 報文段對應其中一項:已由某個用戶端發出併到達伺服器,而伺服器正在等待完成相應的 TCP 三向交握過程。這些通訊端處於 SYN_REVD 狀態;
  2. 已完成串連隊列,每個已完成 TCP 三向交握過程的用戶端對應其中一項。這些通訊端處於 ESTABLISHED 狀態;


accept 函數

        accept 函數由 TCP 伺服器調用,用於從已完成串連隊列隊頭返回下一個已完成串連。如果已完成串連隊列為空白,那麼進程被投入睡眠。該函數的返回值是一個新的通訊端描述符,返回值是表示已串連的通訊端描述符,而第一個參數是伺服器監聽通訊端描述符。一個伺服器通常僅僅建立一個監聽通訊端,它在該伺服器的生命週期內一直存在。核心為每個由伺服器處理序接受的客戶串連建立一個已串連通訊端(表示 TCP 三向交握已完成),當伺服器完成對某個給定客戶的服務時,相應的已串連通訊端就會被關閉。該函數描述如下:

/* 函數功能:從已完成串連隊列隊頭返回下一個已完成串連;若已完成串連隊列為空白,則進程進入睡眠; * 函數原型: */int accept(int sockfd, struct sockaddr *cliaddr, socklen_t *addrlen);//傳回值:若成功返回通訊端描述符,出錯返回-1;/* * 說明: * 參數 cliaddr 和 addrlen 用來返回已串連的對端(用戶端)的協議地址; * * 該函數返回通訊端描述符,該描述符串連到調用connect函數的用戶端; * 這個新的通訊端描述符和原始的通訊端描述符sockfd具有相同的通訊端類型和地址族,而傳給accept函數的通訊端描述符sockfd沒有關聯到這個連結, * 而是繼續保持可用狀態並接受其他串連請求; * 若不關心用戶端協議地址,可將cliaddr和addrlen參數設定為NULL,否則,在調用accept之前,應將參數cliaddr設為足夠大的緩衝區來存放地址, * 並且將addrlen設為指向代表這個緩衝區大小的整數指標; * accept函數返回時,會在緩衝區填充用戶端的地址並更新addrlen所指向的整數為該地址的實際大小; * * 若沒有串連請求等待處理,accept會阻塞直到一個請求到來; */

fork 和 exec 函數

/* 函數功能:建立子進程; * 傳回值: * (1)在子進程中,返回0; * (2)在父進程中,返回新建立子進程的進程ID; * (3)若出錯,則範回-1; * 函數原型: */#include <unistd.h>pid_t fork(void);/* 說明: * 該函數調用一次若成功則返回兩個值: * 在調用進程(即父進程)中,返回新建立進程(即子進程)的進程ID; * 在子進程傳回值是0; * 因此,可以根據傳回值判斷進程是子進程還是父進程; *//* exec 序列函數 *//* * 函數功能:把當前進程替換為一個新的進程,新進程與原進程ID相同; * 傳回值:若出錯則返回-1,若成功則不返回; * 函數原型: */#include <unistd.h>int execl(const char *pathname, const char *arg, ...);int execv(const char *pathnam, char *const argv[]);int execle(const char *pathname, const char *arg, ... , char *const envp[]);int execve(const char *pathnam, char *const argv[], char *const envp[]);int execlp(const char *filename, const char *arg, ...);int execvp(const char *filename, char *const argv[]);/*  6 個函數的區別如下: * (1)待執行的程式檔案是 檔案名稱 還是由 路徑名 指定; * (2)新程式的參數是 一一列出 還是由一個 指標數組 來引用; * (3)把調用進程的環境傳遞給新程式 還是 給新程式指定新的環境; */

  exec 6個函數在函數名和使用文法的規則上都有細微的區別,下面就從可執行檔尋找方式、參數傳遞方式及環境變數這幾個方面進行比較。

  1. 尋找方式:前4個函數的尋找方式都是完整的檔案目錄路徑 pathname ,而最後兩個函數(也就是以p結尾的兩個函數)可以只給出檔案名稱 filename,系統就會自動按照環境變數 “$PATH” 所指定的路徑進行尋找。
  2. 參數傳遞方式:exec 序列函數的參數傳遞有兩種方式:一種是逐個列舉的方式,而另一種則是將所有參數整體構造指標數組傳遞。在這裡是以函數名的第5位字母來區分的,字母為 “l”(list)的表示逐個列舉參數的方式,其文法為 const char *arg;字母為 “v”(vertor)的表示將所有參數整體構造指標數組傳遞,其文法為 char *const argv[]。讀者可以觀察 execl()、execle()、execlp() 的文法與 execv()、execve()、execvp() 的區別。這裡的參數實際上就是使用者在使用這個可執行檔時所需的全部命令選項字串(包括該可執行程式命令本身)。要注意的是,這些參數必須以NULL結束。
  3. 環境變數:exec 序列函數可以預設系統的環境變數,也可以傳入指定的環境變數。這裡以 “e”(environment)結尾的兩個函數 execle() 和 execve() 就可以在 envp[] 中指定當前進程所使用的環境變數。

表 1 exec 序列函數的總結前4位統一為:exec第5位l:參數傳遞為逐個列舉方式 execl、execle、execlp v:參數傳遞為構造指標數組方式 execv、execve、execvp第6位e:可傳遞新進程環境變數 execle、execve p:可執行檔尋找方式為檔案名稱 execlp、execvp

其關係如:




並發伺服器

        當要求一個伺服器同時為多個客戶服務時,需要並發伺服器。TCP 並發伺服器,它們為每個待處理的用戶端串連調用 fork 函數派生一個子進程。當一個串連建立時,accept 返回,伺服器接著調用 fork 函數,然後由子進程服務用戶端,父進程則等待另一個串連,此時,父進程必須關閉已串連通訊端。


close 和 shutdown 函數

        當要關閉通訊端時,可使用 close 和 shutdown 函數,其描述如下:

/* 函數功能:關閉通訊端,若是在 TCP 協議中,並終止 TCP 串連; * 傳回值:若成功則返回0,若出錯則返回-1; * 函數原型: */#include <unistd.h>int close(int sockfd);/* * 函數功能:關閉通訊端上的輸入或輸出; * 傳回值:若成功則返回0,若出錯返回-1; * 函數原型: */#include <sys/socket.h>int shutdown(int sockfd, int how);/* * 說明: * sockfd表示待操作的通訊端描述符; * how表示具體操作,取值如下: * (1)SHUT_RD     關閉讀端,即不能接收資料 * (2)SHUT_WR     關閉寫端,即不能發送資料 * (3)SHUT_RDWR   關閉讀、寫端,即不能發送和接收資料 * */


getsockname 和 getpeername 函數

        為了擷取已綁定到通訊端的地址,我們可以調用函數 getsockname 來實現:

/* * 函數功能:擷取已綁定到一個通訊端的地址; * 傳回值:若成功則返回0,若出錯則返回-1; * 函數原型: */#include <sys/socket.h>int getsockname(int sockfd, struct sockaddr *addr, socklen_t *alenp);/* * 說明: * 調用該函數之前,設定alenp為一個指向整數的指標,該整數指定緩衝區sockaddr的大小; * 返回時,該整數會被設定成返回地址的大小,如果該地址和提供的緩衝區長度不匹配,則將其截斷而不報錯; *//* * 函數功能:擷取通訊端對方串連的地址; * 傳回值:若成功則返回0,若出錯則返回-1; * 函數原型: */#include <sys/socket.h>int getpeername(int sockfd, struct sockaddr *addr, socklen_t *alenp);/* * 說明: * 該函數除了返回對方的地址之外,其他功能和getsockname一樣; */

參考資料:

《Unix 網路編程》

《網路編程》基本 TCP 通訊端編程

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.