Unix Socket - 快速指南



什麼是 Socket?

Sockets 允許在同一臺或不同機器上的兩個不同程序之間進行通訊。更準確地說,它是一種使用標準 Unix 檔案描述符與其他計算機通訊的方式。在 Unix 中,每個 I/O 操作都是透過寫入或讀取檔案描述符來完成的。檔案描述符只是一個與開啟的檔案關聯的整數,它可以是網路連線、文字檔案、終端或其他東西。

對於程式設計師來說,socket 看起來和表現得都像一個低階檔案描述符。這是因為像 read() 和 write() 這樣的命令以與處理檔案和管道相同的方式處理 socket。

Sockets 最初是在 2.1BSD 中引入的,隨後在 4.2BSD 中被改進為其當前形式。現在大多數當前的 UNIX 系統版本都提供了 sockets 功能。

Socket 在哪裡使用?

Unix Socket 用於客戶端-伺服器應用程式框架。伺服器是一個程序,它根據客戶端的請求執行某些功能。大多數應用程式級協議(如 FTP、SMTP 和 POP3)都使用 sockets 在客戶端和伺服器之間建立連線,然後交換資料。

Socket 型別

使用者可以使用四種類型的 socket。前兩種最常用,後兩種很少使用。

假定程序僅在相同型別的 socket 之間通訊,但沒有限制阻止不同型別的 socket 之間的通訊。

  • 流式 Socket - 網路環境中的交付是有保證的。如果您透過流式 socket 傳送三個專案“A、B、C”,它們將按相同的順序到達 - “A、B、C”。這些 socket 使用 TCP(傳輸控制協議)進行資料傳輸。如果無法交付,傳送方會收到錯誤指示。資料記錄沒有任何邊界。

  • 資料報 Socket - 網路環境中的交付沒有保證。它們是無連線的,因為您不需要像流式 socket 那樣開啟連線 - 您構建一個包含目標資訊的包並將其傳送出去。它們使用 UDP(使用者資料報協議)。

  • 原始 Socket - 這些 socket 為使用者提供了對支援 socket 抽象的基礎通訊協議的訪問。這些 socket 通常是面向資料報的,儘管它們的精確特性取決於協議提供的介面。原始 socket 不是為一般使用者設計的;它們主要提供給那些有興趣開發新通訊協議或訪問現有協議的一些更神秘功能的人。

  • 順序分組 Socket - 它們類似於流式 socket,但保留了記錄邊界。此介面僅作為網路系統 (NS) socket 抽象的一部分提供,並且在大多數嚴肅的 NS 應用程式中非常重要。順序分組 socket 允許使用者透過以下方式操作包或一組包上的順序分組協議 (SPP) 或網際網路資料報協議 (IDP) 標頭:編寫一個原型標頭以及要傳送的任何資料,或指定一個預設標頭用於所有傳出資料,並允許使用者接收傳入包上的標頭。

接下來是什麼?

接下來的幾章旨在鞏固您的基礎,並在您能夠使用socket編寫伺服器和客戶端程式之前奠定基礎。如果您想直接跳到如何編寫客戶端和伺服器程式,那麼您可以這樣做,但我們不建議這樣做。強烈建議您一步一步地完成這些最初的幾章,以便在繼續進行程式設計之前打好基礎。

Unix Socket - 網路地址

在繼續進行實際內容之前,讓我們簡單討論一下網路地址 - IP 地址。

IP 主機地址,或更常見地稱為 IP 地址,用於識別連線到網際網路的主機。IP 代表網際網路協議,指的是網際網路整體網路架構的網路層。

IP 地址是一個 32 位的量,解釋為四個 8 位的數字或八位位元組。每個 IP 地址唯一地標識參與的使用者網路、網路上的主機以及使用者網路的類別。

IP 地址通常以點分十進位制表示法編寫,格式為 N1.N2.N3.N4,其中每個 Ni 都是 0 到 255 之間的十進位制數(00 到 FF 十六進位制)。

地址類別

IP 地址由網際網路號碼分配機構 (IANA) 管理和建立。有五種不同的地址類別。您可以透過檢查 IP 地址的前四位來確定 IP 地址屬於哪個類別。

  • A 類地址以0xxx開頭,或十進位制1 到 126

  • B 類地址以10xx開頭,或十進位制128 到 191

  • C 類地址以110x開頭,或十進位制192 到 223

  • D 類地址以1110開頭,或十進位制224 到 239

  • E 類地址以1111開頭,或十進位制240 到 254

01111111開頭或十進位制127開頭的地址保留用於環回和本地機器上的內部測試[您可以測試一下:您應該始終能夠 ping 127.0.0.1,它指向您自己];D 類地址保留用於多播;E 類地址保留供將來使用。它們不應用於主機地址。

示例

類別 最左邊的位 起始地址 結束地址
A 0xxx 0.0.0.0 127.255.255.255
B 10xx 128.0.0.0 191.255.255.255
C 110x 192.0.0.0 223.255.255.255
D 1110 224.0.0.0 239.255.255.255
E 1111 240.0.0.0 255.255.255.255

子網劃分

子網劃分或子網基本上意味著將網路分支出去。它可以出於各種原因進行,例如組織中的網路、使用不同的物理介質(如乙太網、FDDI、WAN 等)、保留地址空間和安全性。最常見的原因是控制網路流量。

子網劃分的基本思想是將 IP 地址的主機識別符號部分劃分為兩部分 -

  • 網路地址本身內的子網地址;以及
  • 子網上的主機地址。

例如,常見的 B 類地址格式為 N1.N2.S.H,其中 N1.N2 標識 B 類網路,8 位 S 欄位標識子網,8 位 H 欄位標識子網上的主機。

Unix Socket - 網路主機名

以數字形式表示的主機名很難記住,因此它們被稱為普通名稱,例如 Takshila 或 Nalanda。我們編寫軟體應用程式來查詢與給定名稱相對應的點分 IP 地址。

根據給定的字母數字主機名查詢點分 IP 地址的過程稱為主機名解析

主機名解析由駐留在高容量系統上的特殊軟體完成。這些系統稱為域名系統 (DNS),它們儲存 IP 地址及其對應普通名稱的對映。

/etc/hosts 檔案

主機名和 IP 地址之間的對應關係儲存在一個名為hosts的檔案中。在大多數系統上,此檔案位於/etc目錄中。

此檔案中的條目如下所示 -

# This represents a comments in /etc/hosts file.
127.0.0.1       localhost
192.217.44.207  nalanda metro
153.110.31.18   netserve
153.110.31.19   mainserver centeral
153.110.31.20   samsonite
64.202.167.10   ns3.secureserver.net
64.202.167.97   ns4.secureserver.net
66.249.89.104   www.google.com
68.178.157.132  services.amrood.com

請注意,可能有多個名稱與給定的 IP 地址相關聯。此檔案用於在 IP 地址和主機名之間進行轉換。

您將無法訪問此檔案以進行編輯,因此如果您想將任何主機名與 IP 地址一起放置,則需要擁有 root 許可權。

Unix Socket - 客戶端-伺服器模型

大多數網路應用程式都使用客戶端-伺服器架構,它指的是兩個程序或兩個應用程式彼此通訊以交換某些資訊。這兩個程序之一充當客戶端程序,另一個程序充當伺服器。

客戶端程序

這是通常請求資訊的程序。獲取響應後,此程序可能會終止或執行其他處理。

例如,Internet 瀏覽器充當客戶端應用程式,它向 Web 伺服器傳送請求以獲取一個 HTML 網頁。

伺服器程序

這是接收客戶端請求的程序。在收到客戶端的請求後,此程序將執行所需的處理,收集請求的資訊並將其傳送到請求客戶端。完成後,它將準備服務另一個客戶端。伺服器程序始終處於警報狀態並準備服務傳入請求。

例如 - Web 伺服器一直等待來自 Internet 瀏覽器的請求,並且一旦收到瀏覽器的任何請求,它就會獲取請求的 HTML 頁面並將其傳送回該瀏覽器。

請注意,客戶端需要知道伺服器的地址,但伺服器在建立連線之前不需要知道客戶端的地址甚至存在與否。一旦建立連線,雙方都可以傳送和接收資訊。

兩層和三層架構

客戶端-伺服器架構有兩種型別 -

  • 兩層架構 - 在此架構中,客戶端直接與伺服器互動。這種型別的架構可能存在一些安全漏洞和效能問題。Internet Explorer 和 Web 伺服器在兩層架構上工作。此處使用安全套接字層 (SSL) 解決安全問題。

  • 三層架構 - 在這種架構中,客戶端和伺服器之間還存在一個軟體。這個中間軟體被稱為“中介軟體”。中介軟體用於執行所有安全檢查,並在負載過重的情況下進行負載均衡。中介軟體接收來自客戶端的所有請求,並在執行必要的身份驗證後,將該請求傳遞給伺服器。然後伺服器進行必要的處理,並將響應傳送回中介軟體,最後中介軟體將此響應傳遞迴客戶端。如果要實現三層架構,則可以在 Web 伺服器和 Web 瀏覽器之間保留任何中介軟體,例如 Web Logic 或 WebSphere 軟體。

伺服器型別

您可以擁有兩種型別的伺服器 -

  • 迭代伺服器 - 這是伺服器最簡單的形式,其中一個伺服器程序為一個客戶端服務,並在完成第一個請求後,接收來自另一個客戶端的請求。同時,其他客戶端保持等待狀態。

  • 併發伺服器 - 這種型別的伺服器執行多個併發程序以同時處理許多請求,因為一個程序可能需要更長時間,而另一個客戶端無法等待這麼長時間。在 Unix 下編寫併發伺服器的最簡單方法是fork一個子程序來分別處理每個客戶端。

如何建立客戶端

建立連線的系統呼叫對於客戶端和伺服器來說略有不同,但都涉及套接字的基本構造。這兩個程序都建立自己的套接字。

在客戶端建立套接字涉及的步驟如下 -

  • 使用socket()系統呼叫建立套接字。

  • 使用connect()系統呼叫將套接字連線到伺服器的地址。

  • 傳送和接收資料。有許多方法可以做到這一點,但最簡單的方法是使用read()write()系統呼叫。

如何建立伺服器

在伺服器端建立套接字涉及的步驟如下 -

  • 使用socket()系統呼叫建立套接字。

  • 使用bind()系統呼叫將套接字繫結到地址。對於 Internet 上的伺服器套接字,地址由主機上的埠號組成。

  • 使用listen()系統呼叫偵聽連線。

  • 使用accept()系統呼叫接受連線。此呼叫通常會阻塞連線,直到客戶端與伺服器連線。

  • 使用read()write()系統呼叫傳送和接收資料。

客戶端和伺服器互動

以下是顯示完整客戶端和伺服器互動的圖表 -

Socket Client Server

Unix Socket - 結構

在 Unix 套接字程式設計中,使用各種結構來儲存有關地址和埠以及其他資訊的資訊。大多數套接字函式都需要一個指向套接字地址結構的指標作為引數。本章中定義的結構與 Internet 協議族相關。

sockaddr

第一個結構是sockaddr,它儲存套接字資訊 -

struct sockaddr {
   unsigned short   sa_family;
   char             sa_data[14];
};

這是一個通用的套接字地址結構,將在大多數套接字函式呼叫中傳遞。下表提供了成員欄位的描述 -

屬性 描述
sa_family

AF_INET

AF_UNIX

AF_NS

AF_IMPLINK

它表示地址族。在大多數基於 Internet 的應用程式中,我們使用 AF_INET。
sa_data 特定於協議的地址 14 個位元組的特定於協議的地址的內容將根據地址型別進行解釋。對於 Internet 族,我們將使用埠號 IP 地址,它由下面定義的sockaddr_in結構表示。

sockaddr in

第二個幫助您引用套接字元素的結構如下 -

struct sockaddr_in {
   short int            sin_family;
   unsigned short int   sin_port;
   struct in_addr       sin_addr;
   unsigned char        sin_zero[8];
};

以下是成員欄位的描述 -

屬性 描述
sa_family

AF_INET

AF_UNIX

AF_NS

AF_IMPLINK

它表示地址族。在大多數基於 Internet 的應用程式中,我們使用 AF_INET。
sin_port 服務埠 網路位元組序中的 16 位埠號。
sin_addr IP 地址 網路位元組序中的 32 位 IP 地址。
sin_zero 未使用 只需將此值設定為 NULL,因為未使用。

in addr

此結構僅在上述結構中用作結構欄位,並儲存 32 位 netid/hostid。

struct in_addr {
   unsigned long s_addr;
};

以下是成員欄位的描述 -

屬性 描述
s_addr 服務埠 網路位元組序中的 32 位 IP 地址。

hostent

此結構用於儲存與主機相關的資訊。

struct hostent {
   char *h_name; 
   char **h_aliases; 
   int h_addrtype;  
   int h_length;    
   char **h_addr_list
	
#define h_addr  h_addr_list[0]
};

以下是成員欄位的描述 -

屬性 描述
h_name ti.com 等 它是主機的官方名稱。例如,tutorialspoint.com、google.com 等。
h_aliases TI 它儲存主機名別名的列表。
h_addrtype AF_INET 它包含地址族,在基於 Internet 的應用程式中,它始終為 AF_INET。
h_length 4 它儲存 IP 地址的長度,對於 Internet 地址為 4。
h_addr_list in_addr 對於 Internet 地址,指標陣列 h_addr_list[0]、h_addr_list[1] 等指向結構 in_addr。

注意 - h_addr 定義為 h_addr_list[0] 以保持向後相容性。

servent

此特定結構用於儲存與服務和關聯埠相關的資訊。

struct servent {
   char  *s_name; 
   char  **s_aliases; 
   int   s_port;  
   char  *s_proto;
};

以下是成員欄位的描述 -

屬性 描述
s_name http 這是服務的官方名稱。例如,SMTP、FTP POP3 等。
s_aliases 別名 它儲存服務別名的列表。大多數情況下,這將設定為 NULL。
s_port 80 它將具有關聯的埠號。例如,對於 HTTP,這將是 80。
s_proto

TCP

UDP

它設定為使用的協議。Internet 服務是使用 TCP 或 UDP 提供的。

關於套接字結構的提示

套接字地址結構是每個網路程式不可分割的一部分。我們分配它們,填寫它們,並將指向它們的指標傳遞給各種套接字函式。有時我們將指向其中一個結構的指標傳遞給套接字函式,它會填寫內容。

我們始終透過引用傳遞這些結構(即,我們傳遞指向結構的指標,而不是結構本身),並且我們始終將結構的大小作為另一個引數傳遞。

當套接字函式填寫結構時,長度也透過引用傳遞,以便其值可以由函式更新。我們稱這些值為結果引數。

始終使用 memset() 或 bzero() 函式將結構變數設定為 NULL(即 '\0'),否則您的結構中可能會獲得意外的垃圾值。

Unix Socket - 埠和服務

當客戶端程序想要連線伺服器時,客戶端必須有一種識別其想要連線的伺服器的方法。如果客戶端知道伺服器所在主機的 32 位 Internet 地址,它可以聯絡該主機。但是客戶端如何識別在該主機上執行的特定伺服器程序呢?

為了解決識別主機上執行的特定伺服器程序的問題,TCP 和 UDP 都定義了一組眾所周知的埠。

出於我們的目的,埠將定義為 1024 到 65535 之間的整數。這是因為所有小於 1024 的埠號都被認為是眾所周知的 - 例如,telnet 使用埠 23,http 使用 80,ftp 使用 21,依此類推。

網路服務的埠分配可以在檔案 /etc/services 中找到。如果您正在編寫自己的伺服器,則必須小心地為伺服器分配埠。您應該確保此埠未分配給任何其他伺服器。

通常的做法是分配大於 5000 的任何埠號。但是,許多組織編寫了埠號大於 5000 的伺服器。例如,Yahoo Messenger 在 5050 上執行,SIP 伺服器在 5060 上執行,等等。

示例埠和服務

這是一個服務和關聯埠的小列表。您可以在IANA - TCP/IP 埠分配上找到 Internet 埠和關聯服務的最新列表。

服務 埠號 服務描述
回顯 7 UDP/TCP 傳送回它接收到的內容。
丟棄 9 UDP/TCP 丟棄輸入。
日期時間 13 UDP/TCP 返回 ASCII 時間。
字元生成 19 UDP/TCP 返回字元。
ftp 21 TCP 檔案傳輸。
telnet 23 TCP 遠端登入。
smtp 25 TCP 電子郵件。
日期時間 37 UDP/TCP 返回二進位制時間。
tftp 69 UDP 簡單檔案傳輸。
finger 79 TCP 使用者資訊。
http 80 TCP 全球資訊網。
登入 513 TCP 遠端登入。
who 513 UDP 關於使用者的不同資訊。
Xserver 6000 TCP X 視窗(注意 >1023)。

埠和服務函式

Unix 提供以下函式來從 /etc/services 檔案中獲取服務名稱。

  • struct servent *getservbyname(char *name, char *proto) - 此呼叫獲取服務名稱和協議名稱,並返回該服務的相應埠號。

  • struct servent *getservbyport(int port, char *proto) - 此呼叫獲取埠號和協議名稱,並返回相應服務名稱。

每個函式的返回值都是指向具有以下形式的結構的指標 -

struct servent {
   char  *s_name;
   char  **s_aliases;
   int   s_port;
   char  *s_proto;
};

以下是成員欄位的描述 -

屬性 描述
s_name http 它是服務的官方名稱。例如,SMTP、FTP POP3 等。
s_aliases 別名 它儲存服務別名的列表。大多數情況下,它將設定為 NULL。
s_port 80 它將具有關聯的埠號。例如,對於 HTTP,它將是 80。
s_proto

TCP

UDP

它設定為使用的協議。Internet 服務是使用 TCP 或 UDP 提供的。

Unix Socket - 網路位元組序

不幸的是,並非所有計算機都以相同的順序儲存構成多位元組值的位元組。考慮一個由 2 個位元組組成的 16 位網際網路。有兩種方法可以儲存此值。

  • 小端序 - 在此方案中,低位位元組儲存在起始地址 (A) 上,高位位元組儲存在下一個地址 (A + 1) 上。

  • 大端序 - 在此方案中,高位位元組儲存在起始地址 (A) 上,低位位元組儲存在下一個地址 (A + 1) 上。

為了允許具有不同位元組順序約定的機器相互通訊,Internet 協議為透過網路傳輸的資料指定了規範的位元組順序約定。這稱為網路位元組序。

在建立 Internet 套接字連線時,必須確保 sockaddr_in 結構的 sin_port 和 sin_addr 成員中的資料以網路位元組序表示。

位元組排序函式

在主機內部表示和網路位元組序之間轉換資料的例程如下 -

函式 描述
htons() 主機到網路短整型
htonl() 主機到網路長整型
ntohl() 網路到主機長整型
ntohs() 網路到主機短整型

以下列出了一些關於這些函式的更多詳細資訊 -

  • unsigned short htons(unsigned short hostshort) - 此函式將 16 位(2 位元組)數量從主機位元組序轉換為網路位元組序。

  • unsigned long htonl(unsigned long hostlong) - 此函式將 32 位(4 位元組)數量從主機位元組序轉換為網路位元組序。

  • unsigned short ntohs(unsigned short netshort) - 此函式將 16 位(2 位元組)數量從網路位元組序轉換為主機位元組序。

  • unsigned long ntohl(unsigned long netlong) - 此函式將 32 位數量從網路位元組序轉換為主機位元組序。

這些函式是宏,導致將轉換原始碼插入到呼叫程式中。在小端序機器上,程式碼將更改值以使其成為網路位元組序。在大端序機器上,不會插入任何程式碼,因為不需要任何程式碼;這些函式定義為空。

確定主機位元組序的程式

將以下程式碼儲存在檔案byteorder.c中,然後對其進行編譯並在您的機器上執行。

在此示例中,我們將兩位元組值 0x0102 儲存在短整型中,然後檢視兩個連續的位元組,c[0](地址 A)和 c[1](地址 A + 1)以確定位元組順序。

#include <stdio.h>

int main(int argc, char **argv) {

   union {
      short s;
      char c[sizeof(short)];
   }un;
	
   un.s = 0x0102;
   
   if (sizeof(short) == 2) {
      if (un.c[0] == 1 && un.c[1] == 2)
         printf("big-endian\n");
      
      else if (un.c[0] == 2 && un.c[1] == 1)
         printf("little-endian\n");
      
      else
         printf("unknown\n");
   }
   else {
      printf("sizeof(short) = %d\n", sizeof(short));
   }
	
   exit(0);
}

此程式在奔騰機器上生成的輸出如下 -

$> gcc byteorder.c
$> ./a.out
little-endian
$>

Unix Socket - IP 地址函式

Unix 提供各種函式呼叫來幫助您操作 IP 地址。這些函式在 ASCII 字串(人類喜歡使用的)和網路位元組序二進位制值(儲存在套接字地址結構中的值)之間轉換 Internet 地址。

以下三個函式呼叫用於 IPv4 地址 -

  • int inet_aton(const char *strptr, struct in_addr *addrptr)
  • in_addr_t inet_addr(const char *strptr)
  • char *inet_ntoa(struct in_addr inaddr)

int inet_aton(const char *strptr, struct in_addr *addrptr)

此函式呼叫將指定字串(以網際網路標準點分十進位制表示法)轉換為網路地址,並將地址儲存在提供的結構中。轉換後的地址將採用網路位元組序(位元組從左到右排序)。如果字串有效則返回 1,錯誤則返回 0。

以下是用法示例 -

#include <arpa/inet.h>

(...)

   int retval;
   struct in_addr addrptr
   
   memset(&addrptr, '\0', sizeof(addrptr));
   retval = inet_aton("68.178.157.132", &addrptr);

(...)

in_addr_t inet_addr(const char *strptr)

此函式呼叫將指定字串(以網際網路標準點分十進位制表示法)轉換為適合用作網際網路地址的整數值。轉換後的地址將採用網路位元組序(位元組從左到右排序)。它返回一個 32 位二進位制網路位元組序 IPv4 地址,錯誤時返回 INADDR_NONE。

以下是用法示例 -

#include <arpa/inet.h>

(...)

   struct sockaddr_in dest;

   memset(&dest, '\0', sizeof(dest));
   dest.sin_addr.s_addr = inet_addr("68.178.157.132");
   
(...)

char *inet_ntoa(struct in_addr inaddr)

此函式呼叫將指定的網際網路主機地址轉換為網際網路標準點分十進位制表示法的字串。

以下是用法示例 -

#include <arpa/inet.h>

(...)

   char *ip;
   
   ip = inet_ntoa(dest.sin_addr);
   
   printf("IP Address is: %s\n",ip);
   
(...)

Unix Socket - 核心函式

本章描述了編寫完整 TCP 客戶端和伺服器所需的核心套接字函式。

下圖顯示了完整的客戶端和伺服器互動 -

Socket Client Server

socket 函式

要執行網路 I/O,程序首先必須執行的操作是呼叫 socket 函式,指定所需的通訊協議型別和協議族等。

#include <sys/types.h>
#include <sys/socket.h>

int socket (int family, int type, int protocol);

此呼叫返回一個套接字描述符,您可以在後續的系統呼叫中使用它,錯誤時返回 -1。

引數

family - 指定協議族,是以下所示常量之一 -

描述
AF_INET IPv4 協議
AF_INET6 IPv6 協議
AF_LOCAL Unix 域協議
AF_ROUTE 路由套接字
AF_KEY 金鑰套接字

本章不涵蓋 IPv4 以外的其他協議。

type - 指定您想要的套接字型別。它可以取以下值之一 -

型別 描述
SOCK_STREAM 流套接字
SOCK_DGRAM 資料報套接字
SOCK_SEQPACKET 順序資料包套接字
SOCK_RAW 原始套接字

protocol - 該引數應設定為以下給定的特定協議型別,或設定為 0 以選擇系統為給定的 family 和 type 組合提供的預設值 -

協議 描述
IPPROTO_TCP TCP 傳輸協議
IPPROTO_UDP UDP 傳輸協議
IPPROTO_SCTP SCTP 傳輸協議

connect 函式

connect 函式由 TCP 客戶端用於與 TCP 伺服器建立連線。

#include <sys/types.h>
#include <sys/socket.h>

int connect(int sockfd, struct sockaddr *serv_addr, int addrlen);

如果成功連線到伺服器,此呼叫返回 0,否則錯誤時返回 -1。

引數

  • sockfd - 是 socket 函式返回的套接字描述符。

  • serv_addr - 是指向 struct sockaddr 的指標,其中包含目標 IP 地址和埠。

  • addrlen - 將其設定為 sizeof(struct sockaddr)。

bind 函式

bind 函式將本地協議地址分配給套接字。對於 Internet 協議,協議地址是 32 位 IPv4 地址或 128 位 IPv6 地址與 16 位 TCP 或 UDP 埠號的組合。此函式僅由 TCP 伺服器呼叫。

#include <sys/types.h>
#include <sys/socket.h>

int bind(int sockfd, struct sockaddr *my_addr,int addrlen);

如果成功繫結到地址,此呼叫返回 0,否則錯誤時返回 -1。

引數

  • sockfd - 是 socket 函式返回的套接字描述符。

  • my_addr - 是指向 struct sockaddr 的指標,其中包含本地 IP 地址和埠。

  • addrlen - 將其設定為 sizeof(struct sockaddr)。

您可以自動設定您的 IP 地址和埠

埠號為 0 表示系統將選擇一個隨機埠,IP 地址為 INADDR_ANY 表示伺服器的 IP 地址將自動分配。

server.sin_port = 0;  		     
server.sin_addr.s_addr = INADDR_ANY;

注意 - 1024 以下的所有埠均為保留埠。您可以設定 1024 以上且 65535 以下的埠,除非這些埠正在被其他程式使用。

listen 函式

listen 函式僅由 TCP 伺服器呼叫,它執行兩個操作 -

  • listen 函式將未連線的套接字轉換為被動套接字,指示核心應接受定向到此套接字的傳入連線請求。

  • 此函式的第二個引數指定核心應為此套接字排隊的最大連線數。

#include <sys/types.h>
#include <sys/socket.h>

int listen(int sockfd,int backlog);

此呼叫在成功時返回 0,否則錯誤時返回 -1。

引數

  • sockfd - 是 socket 函式返回的套接字描述符。

  • backlog - 是允許的連線數。

accept 函式

accept 函式由 TCP 伺服器呼叫,以返回已完成連線佇列前端的下一個已完成連線。呼叫的簽名如下 -

#include <sys/types.h>
#include <sys/socket.h>

int accept (int sockfd, struct sockaddr *cliaddr, socklen_t *addrlen);

此呼叫在成功時返回非負描述符,否則錯誤時返回 -1。返回的描述符假定為客戶端套接字描述符,所有讀寫操作都將在該描述符上執行以與客戶端通訊。

引數

  • sockfd - 是 socket 函式返回的套接字描述符。

  • cliaddr - 是指向 struct sockaddr 的指標,其中包含客戶端 IP 地址和埠。

  • addrlen - 將其設定為 sizeof(struct sockaddr)。

send 函式

send 函式用於透過流套接字或已連線的資料報套接字傳送資料。如果要透過未連線的資料報套接字傳送資料,則必須使用 sendto() 函式。

您可以使用 write() 系統呼叫傳送資料。其簽名如下 -

int send(int sockfd, const void *msg, int len, int flags);

此呼叫返回傳送出的位元組數,否則錯誤時返回 -1。

引數

  • sockfd - 是 socket 函式返回的套接字描述符。

  • msg - 是您要傳送的資料的指標。

  • len - 是您要傳送的資料的長度(以位元組為單位)。

  • flags - 設定為 0。

recv 函式

recv 函式用於透過流套接字或已連線的資料報套接字接收資料。如果要透過未連線的資料報套接字接收資料,則必須使用 recvfrom()。

您可以使用 read() 系統呼叫讀取資料。此呼叫在輔助函式章節中進行了說明。

int recv(int sockfd, void *buf, int len, unsigned int flags);

此呼叫返回讀取到緩衝區的位元組數,否則錯誤時返回 -1。

引數

  • sockfd - 是 socket 函式返回的套接字描述符。

  • buf - 是將資訊讀取到的緩衝區。

  • len - 是緩衝區的最大長度。

  • flags - 設定為 0。

sendto 函式

sendto 函式用於透過未連線的資料報套接字傳送資料。其簽名如下 -

int sendto(int sockfd, const void *msg, int len, unsigned int flags, const struct sockaddr *to, int tolen);

此呼叫返回傳送的位元組數,否則錯誤時返回 -1。

引數

  • sockfd - 是 socket 函式返回的套接字描述符。

  • msg - 是您要傳送的資料的指標。

  • len - 是您要傳送的資料的長度(以位元組為單位)。

  • flags - 設定為 0。

  • to - 是指向 struct sockaddr 的指標,用於要傳送資料的目標主機。

  • tolen - 將其設定為 sizeof(struct sockaddr)。

recvfrom 函式

recvfrom 函式用於從未連線的資料報套接字接收資料。

int recvfrom(int sockfd, void *buf, int len, unsigned int flags struct sockaddr *from, int *fromlen);

此呼叫返回讀取到緩衝區的位元組數,否則錯誤時返回 -1。

引數

  • sockfd - 是 socket 函式返回的套接字描述符。

  • buf - 是將資訊讀取到的緩衝區。

  • len - 是緩衝區的最大長度。

  • flags - 設定為 0。

  • from - 是指向 struct sockaddr 的指標,用於要讀取資料的目標主機。

  • fromlen - 將其設定為 sizeof(struct sockaddr)。

close 函式

close 函式用於關閉客戶端和伺服器之間的通訊。其語法如下 -

int close( int sockfd );

此呼叫在成功時返回 0,否則錯誤時返回 -1。

引數

  • sockfd - 是 socket 函式返回的套接字描述符。

shutdown 函式

shutdown 函式用於優雅地關閉客戶端和伺服器之間的通訊。與 close 函式相比,此函式提供了更多控制。以下是 shutdown 的語法 -

int shutdown(int sockfd, int how);

此呼叫在成功時返回 0,否則錯誤時返回 -1。

引數

  • sockfd - 是 socket 函式返回的套接字描述符。

  • how - 放入以下數字之一 -

    • 0 - 表示不允許接收,

    • 1 - 表示不允許傳送,以及

    • 2 - 表示不允許傳送和接收。當 how 設定為 2 時,它與 close() 相同。

select 函式

select 函式指示哪些指定的描述符已準備好讀取、已準備好寫入或是否有錯誤條件掛起。

當應用程式呼叫 recv 或 recvfrom 時,它會被阻塞,直到該套接字的資料到達。在傳入資料流為空時,應用程式可以執行其他有用的處理。另一種情況是當應用程式從多個套接字接收資料時。

在輸入佇列中沒有資料的套接字上呼叫 recv 或 recvfrom 會阻止立即從其他套接字接收資料。select 函式呼叫透過允許程式輪詢所有套接字控制代碼來解決此問題,以檢視它們是否可用於非阻塞讀寫操作。

以下是 select 的語法 -

 int select(int  nfds, fd_set  *readfds, fd_set  *writefds, fd_set *errorfds, struct timeval *timeout);

此呼叫在成功時返回 0,否則錯誤時返回 -1。

引數

  • nfds - 指定要測試的檔案描述符的範圍。select() 函式測試範圍從 0 到 nfds-1 的檔案描述符

  • readfds - 指向型別為 fd_set 的物件,該物件在輸入時指定要檢查是否已準備好讀取的檔案描述符,在輸出時指示哪些檔案描述符已準備好讀取。它可以為 NULL 以指示空集。

  • writefds - 指向型別為 fd_set 的物件,該物件在輸入時指定要檢查是否已準備好寫入的檔案描述符,在輸出時指示哪些檔案描述符已準備好寫入。它可以為 NULL 以指示空集。

  • exceptfds - 指向型別為 fd_set 的物件,該物件在輸入時指定要檢查是否有錯誤條件掛起的檔案描述符,在輸出時指示哪些檔案描述符是否有錯誤條件掛起。它可以為 NULL 以指示空集。

  • timeout - 指向 timeval 結構,該結構指定 select 呼叫應輪詢描述符以獲取可用 I/O 操作的時間長度。如果超時值為 0,則 select 將立即返回。如果 timeout 引數為 NULL,則 select 將阻塞,直到至少有一個檔案/套接字控制代碼已準備好進行可用 I/O 操作。否則,select 將在超時中指定的時間量過去或至少有一個檔案/套接字描述符已準備好進行 I/O 操作後返回。

select 函式的返回值是在檔案描述符集中指定的可用於 I/O 的控制代碼數量。如果超時欄位指定的時限已到,則 select 返回 0。以下宏用於操作檔案描述符集:

  • FD_CLR(fd, &fdset) − 清除檔案描述符集中檔案描述符 fd 的位。

  • FD_ISSET(fd, &fdset) − 如果 fdset 指向的檔案描述符集中檔案描述符 fd 的位已設定,則返回非零值;否則返回 0。

  • FD_SET(fd, &fdset) − 設定檔案描述符集中檔案描述符 fd 的位。

  • FD_ZERO(&fdset) − 初始化檔案描述符集 fdset,使其所有檔案描述符的位都為零。

如果 fd 引數小於 0 或大於等於 FD_SETSIZE,則這些宏的行為未定義。

示例

fd_set fds;

struct timeval tv;

/* do socket initialization etc.
tv.tv_sec = 1;
tv.tv_usec = 500000;

/* tv now represents 1.5 seconds */
FD_ZERO(&fds);

/* adds sock to the file descriptor set */
FD_SET(sock, &fds); 

/* wait 1.5 seconds for any data to be read from any single socket */
select(sock+1, &fds, NULL, NULL, &tv);

if (FD_ISSET(sock, &fds)) {
   recvfrom(s, buffer, buffer_len, 0, &sa, &sa_len);
   /* do something */
}
else {
   /* do something else */
}

Unix Socket - 輔助函式

本章描述了所有輔助函式,這些函式在進行套接字程式設計時使用。其他輔助函式在以下章節中描述:埠和服務以及網路位元組序

write 函式

write 函式嘗試將 buf 指向的緩衝區中的 nbyte 個位元組寫入與開啟的檔案描述符 fildes 關聯的檔案。

您也可以使用 send() 函式將資料傳送到另一個程序。

#include <unistd.h>

int write(int fildes, const void *buf, int nbyte);

成功完成後,write() 返回實際寫入與 fildes 關聯的檔案的位元組數。此數字絕不超過 nbyte。否則,返回 -1。

引數

  • fildes − 它是由 socket 函式返回的套接字描述符。

  • buf − 它是指向要傳送的資料的指標。

  • nbyte − 它是要寫入的位元組數。如果 nbyte 為 0,則 write() 將返回 0 並且如果檔案是常規檔案則沒有其他結果;否則,結果未指定。

read 函式

read 函式嘗試從與緩衝區 fildes 關聯的檔案中讀取 nbyte 個位元組,並將這些位元組讀入 buf 指向的緩衝區。

您也可以使用 recv() 函式讀取另一個程序的資料。

#include <unistd.h>

int read(int fildes, const void *buf, int nbyte);

成功完成後,write() 返回實際寫入與 fildes 關聯的檔案的位元組數。此數字絕不超過 nbyte。否則,返回 -1。

引數

  • fildes − 它是由 socket 函式返回的套接字描述符。

  • buf - 是將資訊讀取到的緩衝區。

  • nbyte − 它是要讀取的位元組數。

fork 函式

fork 函式建立一個新的程序。新程序稱為子程序,它是呼叫程序(父程序)的精確副本。子程序從父程序繼承許多屬性。

#include <sys/types.h>
#include <unistd.h>

int fork(void);

成功完成後,fork() 返回 0 給子程序,並將子程序的程序 ID 返回給父程序。否則,返回 -1 給父程序,不建立子程序,並且 errno 被設定為指示錯誤。

引數

  • void − 表示不需要引數。

bzero 函式

bzero 函式在字串 s 中放置 nbyte 個空位元組。此函式用於將所有套接字結構設定為零值。

void bzero(void *s, int nbyte);

此函式不返回任何內容。

引數

  • s − 它指定要填充空位元組的字串。這將是指向套接字結構變數的指標。

  • nbyte − 它指定要填充空值的位元組數。這將是套接字結構的大小。

bcmp 函式

bcmp 函式將位元組字串 s1 與位元組字串 s2 進行比較。假設這兩個字串都長 nbyte 個位元組。

int bcmp(const void *s1, const void *s2, int nbyte);

如果兩個字串相同,則此函式返回 0,否則返回 1。當 nbyte 為 0 時,bcmp() 函式始終返回 0。

引數

  • s1 − 它指定要比較的第一個字串。

  • s2 − 它指定要比較的第二個字串。

  • nbyte − 它指定要比較的位元組數。

bcopy 函式

bcopy 函式將 nbyte 個位元組從字串 s1 複製到字串 s2。正確處理重疊的字串。

void bcopy(const void *s1, void *s2, int nbyte);

此函式不返回任何內容。

引數

  • s1 − 它指定源字串。

  • s2v − 它指定目標字串。

  • nbyte − 它指定要複製的位元組數。

memset 函式

memset 函式也用於像 bzero 一樣設定結構變數。請檢視下面給出的語法。

void *memset(void *s, int c, int nbyte);

此函式返回指向 void 的指標;實際上,是指向已設定記憶體的指標,您需要相應地將其強制轉換。

引數

  • s − 它指定要設定的源。

  • c − 它指定在 nbyte 個位置上要設定的字元。

  • nbyte − 它指定要設定的位元組數。

Unix 套接字 - 伺服器示例

要使程序成為 TCP 伺服器,您需要按照以下步驟操作:

  • 使用 socket() 系統呼叫建立套接字。

  • 使用 bind() 系統呼叫將套接字繫結到地址。對於 Internet 上的伺服器套接字,地址包含主機上的埠號。

  • 使用 listen() 系統呼叫偵聽連線。

  • 使用 accept() 系統呼叫接受連線。此呼叫通常會阻塞,直到客戶端連線到伺服器。

  • 使用 read()write() 系統呼叫傳送和接收資料。

現在讓我們將這些步驟以原始碼的形式表示出來。將此程式碼放入 server.c 檔案中,並使用 gcc 編譯器進行編譯。

#include <stdio.h>
#include <stdlib.h>

#include <netdb.h>
#include <netinet/in.h>

#include <string.h>

int main( int argc, char *argv[] ) {
   int sockfd, newsockfd, portno, clilen;
   char buffer[256];
   struct sockaddr_in serv_addr, cli_addr;
   int  n;
   
   /* First call to socket() function */
   sockfd = socket(AF_INET, SOCK_STREAM, 0);
   
   if (sockfd < 0) {
      perror("ERROR opening socket");
      exit(1);
   }
   
   /* Initialize socket structure */
   bzero((char *) &serv_addr, sizeof(serv_addr));
   portno = 5001;
   
   serv_addr.sin_family = AF_INET;
   serv_addr.sin_addr.s_addr = INADDR_ANY;
   serv_addr.sin_port = htons(portno);
   
   /* Now bind the host address using bind() call.*/
   if (bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) {
      perror("ERROR on binding");
      exit(1);
   }
      
   /* Now start listening for the clients, here process will
      * go in sleep mode and will wait for the incoming connection
   */
   
   listen(sockfd,5);
   clilen = sizeof(cli_addr);
   
   /* Accept actual connection from the client */
   newsockfd = accept(sockfd, (struct sockaddr *)&cli_addr, &clilen);
	
   if (newsockfd < 0) {
      perror("ERROR on accept");
      exit(1);
   }
   
   /* If connection is established then start communicating */
   bzero(buffer,256);
   n = read( newsockfd,buffer,255 );
   
   if (n < 0) {
      perror("ERROR reading from socket");
      exit(1);
   }
   
   printf("Here is the message: %s\n",buffer);
   
   /* Write a response to the client */
   n = write(newsockfd,"I got your message",18);
   
   if (n < 0) {
      perror("ERROR writing to socket");
      exit(1);
   }
      
   return 0;
}

處理多個連線

為了允許伺服器處理多個同時連線,我們在上述程式碼中進行以下更改:

  • accept 語句和後續程式碼放入無限迴圈中。

  • 建立連線後,呼叫 fork() 建立一個新程序。

  • 子程序將關閉 sockfd 並呼叫 doprocessing 函式,並將新的套接字檔案描述符作為引數傳遞。當兩個程序完成對話後(由 doprocessing() 返回指示),此程序只需退出。

  • 父程序關閉 newsockfd。由於所有這些程式碼都在無限迴圈中,因此它將返回到 accept 語句以等待下一個連線。

#include <stdio.h>
#include <stdlib.h>

#include <netdb.h>
#include <netinet/in.h>

#include <string.h>

void doprocessing (int sock);

int main( int argc, char *argv[] ) {
   int sockfd, newsockfd, portno, clilen;
   char buffer[256];
   struct sockaddr_in serv_addr, cli_addr;
   int n, pid;
   
   /* First call to socket() function */
   sockfd = socket(AF_INET, SOCK_STREAM, 0);
   
   if (sockfd < 0) {
      perror("ERROR opening socket");
      exit(1);
   }
   
   /* Initialize socket structure */
   bzero((char *) &serv_addr, sizeof(serv_addr));
   portno = 5001;
   
   serv_addr.sin_family = AF_INET;
   serv_addr.sin_addr.s_addr = INADDR_ANY;
   serv_addr.sin_port = htons(portno);
   
   /* Now bind the host address using bind() call.*/
   if (bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) {
      perror("ERROR on binding");
      exit(1);
   }
   
   /* Now start listening for the clients, here
      * process will go in sleep mode and will wait
      * for the incoming connection
   */
   
   listen(sockfd,5);
   clilen = sizeof(cli_addr);
   
   while (1) {
      newsockfd = accept(sockfd, (struct sockaddr *) &cli_addr, &clilen);
		
      if (newsockfd < 0) {
         perror("ERROR on accept");
         exit(1);
      }
      
      /* Create child process */
      pid = fork();
		
      if (pid < 0) {
         perror("ERROR on fork");
         exit(1);
      }
      
      if (pid == 0) {
         /* This is the client process */
         close(sockfd);
         doprocessing(newsockfd);
         exit(0);
      }
      else {
         close(newsockfd);
      }
		
   } /* end of while */
}

以下程式碼段顯示了 doprocessing 函式的簡單實現。

void doprocessing (int sock) {
   int n;
   char buffer[256];
   bzero(buffer,256);
   n = read(sock,buffer,255);
   
   if (n < 0) {
      perror("ERROR reading from socket");
      exit(1);
   }
   
   printf("Here is the message: %s\n",buffer);
   n = write(sock,"I got your message",18);
   
   if (n < 0) {
      perror("ERROR writing to socket");
      exit(1);
   }
	
}

Unix 套接字 - 客戶端示例

要使程序成為 TCP 客戶端,您需要按照以下步驟操作:

  • 使用 socket() 系統呼叫建立套接字。

  • 使用 connect() 系統呼叫將套接字連線到伺服器的地址。

  • 傳送和接收資料。有多種方法可以做到這一點,但最簡單的方法是使用 read()write() 系統呼叫。

現在讓我們將這些步驟以原始碼的形式表示出來。將此程式碼放入 client.c 檔案中,並使用 gcc 編譯器進行編譯。

執行此程式並傳遞伺服器的 主機名埠號 以連線到伺服器,您已經在另一個 Unix 視窗中運行了該伺服器。

#include <stdio.h>
#include <stdlib.h>

#include <netdb.h>
#include <netinet/in.h>

#include <string.h>

int main(int argc, char *argv[]) {
   int sockfd, portno, n;
   struct sockaddr_in serv_addr;
   struct hostent *server;
   
   char buffer[256];
   
   if (argc < 3) {
      fprintf(stderr,"usage %s hostname port\n", argv[0]);
      exit(0);
   }
	
   portno = atoi(argv[2]);
   
   /* Create a socket point */
   sockfd = socket(AF_INET, SOCK_STREAM, 0);
   
   if (sockfd < 0) {
      perror("ERROR opening socket");
      exit(1);
   }
	
   server = gethostbyname(argv[1]);
   
   if (server == NULL) {
      fprintf(stderr,"ERROR, no such host\n");
      exit(0);
   }
   
   bzero((char *) &serv_addr, sizeof(serv_addr));
   serv_addr.sin_family = AF_INET;
   bcopy((char *)server->h_addr, (char *)&serv_addr.sin_addr.s_addr, server->h_length);
   serv_addr.sin_port = htons(portno);
   
   /* Now connect to the server */
   if (connect(sockfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) < 0) {
      perror("ERROR connecting");
      exit(1);
   }
   
   /* Now ask for a message from the user, this message
      * will be read by server
   */
	
   printf("Please enter the message: ");
   bzero(buffer,256);
   fgets(buffer,255,stdin);
   
   /* Send message to the server */
   n = write(sockfd, buffer, strlen(buffer));
   
   if (n < 0) {
      perror("ERROR writing to socket");
      exit(1);
   }
   
   /* Now read server response */
   bzero(buffer,256);
   n = read(sockfd, buffer, 255);
   
   if (n < 0) {
      perror("ERROR reading from socket");
      exit(1);
   }
	
   printf("%s\n",buffer);
   return 0;
}

Unix Socket - 總結

以下是與套接字程式設計相關的所有函式的列表。

埠和服務函式

Unix 提供以下函式來從 /etc/services 檔案中獲取服務名稱。

  • struct servent *getservbyname(char *name, char *proto) − 此呼叫獲取服務名稱和協議名稱,並返回該服務的相應埠號。

  • struct servent *getservbyport(int port, char *proto) − 此呼叫獲取埠號和協議名稱,並返回相應的服務名稱。

位元組排序函式

  • unsigned short htons (unsigned short hostshort) − 此函式將 16 位(2 位元組)數量從主機位元組序轉換為網路位元組序。

  • unsigned long htonl (unsigned long hostlong) − 此函式將 32 位(4 位元組)數量從主機位元組序轉換為網路位元組序。

  • unsigned short ntohs (unsigned short netshort) − 此函式將 16 位(2 位元組)數量從網路位元組序轉換為主機位元組序。

  • unsigned long ntohl (unsigned long netlong) − 此函式將 32 位數量從網路位元組序轉換為主機位元組序。

IP 地址函式

  • int inet_aton (const char *strptr, struct in_addr *addrptr) − 此函式呼叫將指定的字串(以 Internet 標準點分十進位制表示法表示)轉換為網路地址,並將地址儲存在提供的結構中。轉換後的地址將採用網路位元組序(位元組從左到右排序)。如果字串有效則返回 1,否則返回 0。

  • in_addr_t inet_addr (const char *strptr) − 此函式呼叫將指定的字串(以 Internet 標準點分十進位制表示法表示)轉換為適合用作 Internet 地址的整數值。轉換後的地址將採用網路位元組序(位元組從左到右排序)。它返回一個 32 位二進位制網路位元組序 IPv4 地址,並在出錯時返回 INADDR_NONE。

  • char *inet_ntoa (struct in_addr inaddr) − 此函式呼叫將指定的 Internet 主機地址轉換為 Internet 標準點分十進位制表示法的字串。

套接字核心函式

  • int socket (int family, int type, int protocol) − 此呼叫返回一個套接字描述符,您可以在以後的系統呼叫中使用它,或者在出錯時返回 -1。

  • int connect (int sockfd, struct sockaddr *serv_addr, int addrlen) − connect 函式由 TCP 客戶端用於建立與 TCP 伺服器的連線。如果成功連線到伺服器,則此呼叫返回 0,否則返回 -1。

  • int bind(int sockfd, struct sockaddr *my_addr,int addrlen) − bind 函式將本地協議地址分配給套接字。如果成功繫結到地址,則此呼叫返回 0,否則返回 -1。

  • int listen(int sockfd, int backlog) − listen 函式僅由 TCP 伺服器呼叫以偵聽客戶端請求。此呼叫在成功時返回 0,否則返回 -1。

  • int accept (int sockfd, struct sockaddr *cliaddr, socklen_t *addrlen) − accept 函式由 TCP 伺服器呼叫以接受客戶端請求並建立實際連線。此呼叫在成功時返回非負描述符,否則返回 -1。

  • int send(int sockfd, const void *msg, int len, int flags) − send 函式用於透過流套接字或已連線的資料報套接字傳送資料。此呼叫返回傳送出的位元組數,否則返回 -1。

  • int recv (int sockfd, void *buf, int len, unsigned int flags) − recv 函式用於透過流套接字或已連線的資料報套接字接收資料。此呼叫返回讀取到緩衝區的位元組數,否則在出錯時返回 -1。

  • int sendto (int sockfd, const void *msg, int len, unsigned int flags, const struct sockaddr *to, int tolen) − sendto 函式用於透過未連線的資料報套接字傳送資料。此呼叫返回傳送的位元組數,否則返回 -1。

  • int recvfrom (int sockfd, void *buf, int len, unsigned int flags struct sockaddr *from, int *fromlen) − recvfrom 函式用於從未連線的資料報套接字接收資料。此呼叫返回讀取到緩衝區的位元組數,否則在出錯時返回 -1。

  • int close (int sockfd) − close 函式用於關閉客戶端和伺服器之間的通訊。此呼叫在成功時返回 0,否則返回 -1。

  • int shutdown (int sockfd, int how) − shutdown 函式用於優雅地關閉客戶端和伺服器之間的通訊。與 close 函式相比,此函式提供了更多控制。它在成功時返回 0,否則返回 -1。

  • int select (int nfds, fd_set *readfds, fd_set *writefds, fd_set *errorfds, struct timeval *timeout) − 此函式用於讀取或寫入多個套接字。

套接字輔助函式

  • int write (int fildes, const void *buf, int nbyte) − write 函式嘗試將 buf 指向的緩衝區中的 nbyte 個位元組寫入與開啟的檔案描述符 fildes 關聯的檔案。成功完成後,write() 返回實際寫入與 fildes 關聯的檔案的位元組數。此數字絕不超過 nbyte。否則,返回 -1。

  • int read (int fildes, const void *buf, int nbyte) − read 函式嘗試從與開啟的檔案描述符 fildes 關聯的檔案中讀取 nbyte 位元組到 buf 指向的緩衝區中。成功完成後,write() 返回實際寫入與 fildes 關聯的檔案的位元組數。此數字永遠不會大於 nbyte。否則,返回 -1。

  • int fork (void) − fork 函式建立一個新的程序。新程序稱為子程序,將是呼叫程序(父程序)的精確副本。

  • void bzero (void *s, int nbyte) − bzero 函式在字串 s 中放置 nbyte 個空位元組。此函式將用於用空值設定所有套接字結構。

  • int bcmp (const void *s1, const void *s2, int nbyte) − bcmp 函式將位元組字串 s1 與位元組字串 s2 進行比較。假定這兩個字串都長 nbyte 位元組。

  • void bcopy (const void *s1, void *s2, int nbyte) − bcopy 函式將 nbyte 個位元組從字串 s1 複製到字串 s2。正確處理重疊的字串。

  • void *memset(void *s, int c, int nbyte) − memset 函式也用於以與 bzero 相同的方式設定結構變數。

廣告