Unix套接字 - 核心函式



本章描述了編寫完整的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函式

TCP客戶端使用connect函式與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 − 指向包含目標IP地址和埠的sockaddr結構體的指標。

  • addrlen − 設定為sizeof(struct sockaddr)。

bind函式

bind函式將本地協議地址分配給套接字。對於網際網路協議,協議地址是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 − 指向包含本地IP地址和埠的sockaddr結構體的指標。

  • 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函式

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

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

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

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

引數

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

  • cliaddr − 指向包含客戶端IP地址和埠的sockaddr結構體的指標。

  • 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 − 指向要傳送資料的目標主機的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 − 指向要讀取資料的目標主機的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函式指示哪些指定的 檔案描述符已準備好讀取、已準備好寫入或有錯誤條件待處理。

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

在輸入佇列中沒有資料的套接字上呼叫recvrecvfrom會阻止立即從其他套接字接收資料。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將立即返回。如果超時引數為NULL,則select將阻塞,直到至少有一個檔案/套接字控制代碼準備好進行可用I/O操作。否則,select將在超時時間過去或至少有一個檔案/套接字描述符準備好進行I/O操作後返回。

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

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

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

  • FD_SET(fd, &fdset) − 在檔案描述符集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 */
}
廣告
© . All rights reserved.