- Unix套接字教程
- Unix套接字 - 首頁
- Unix套接字 - 什麼是套接字?
- Unix套接字 - 網路地址
- Unix套接字 - 網路主機名
- Unix套接字 - 客戶端伺服器模型
- Unix套接字 - 結構體
- Unix套接字 - 埠和服務
- Unix套接字 - 網路位元組序
- Unix套接字 - IP地址函式
- Unix套接字 - 核心函式
- Unix套接字 - 輔助函式
- Unix套接字 - 伺服器示例
- Unix套接字 - 客戶端示例
- Unix套接字 - 總結
- Unix套接字有用資源
- Unix套接字 - 快速指南
- Unix套接字 - 有用資源
- Unix套接字 - 討論
Unix套接字 - 核心函式
本章描述了編寫完整的TCP客戶端和伺服器所需的核心套接字函式。
下圖顯示了完整的客戶端和伺服器互動:
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函式指示哪些指定的 檔案描述符已準備好讀取、已準備好寫入或有錯誤條件待處理。
當應用程式呼叫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將立即返回。如果超時引數為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 */
}