1. 基本操作
套接子是通訊端點的抽象。通訊端描述符作為通訊端的標識符,它是以一種檔案形式存在的。如果通訊端點是一個“人”,那麼通訊端標識符就是人的“身份證”。
函數socket()用來建立一個通訊端,並獲得通訊端描述符。
1 #include <sys/socket.h>2 int socket(int domain, int type, int procotol);3 // 成功返回通訊端描述符;出錯返回-1
其中,參數domain確定通訊特徵,它包括AF_INET,AF_INET6,AF_UNIX;
參數type確定通訊端的類型:
- SOCK_DGRAM:是長度固定的、不需連線的不可靠報文傳遞。
- SOCK_STREAM:是有序、可靠、雙向的連線導向位元組流。作為位元組流服務,需要資料讀取一端不停讀取資料,直到傳遞一端關閉或自訂結束符。
- SOCK_RAW:該通訊端提供了一個資料報介面用於直接存取網路層(即IP協議層),不經過傳輸層(即TCP協議棧)。應用程式需要負責構建需要的協議首部資訊。
由於通訊端是雙向的,函數shutdown()用來禁止通訊端上的輸出/輸入。
1 #include <sys/socket.h>2 int shutdown(int sockfd, int how);3 // 成功返回0;若出錯返回-1
其中,how用來表示關閉方式,包括以下幾種:
- SHUT_RD(關閉讀端),則無法從通訊端中讀取資料;
- SHUT_WR(關閉寫端),則無法使用通訊端發送資料;
- SHUT_RDWR(關閉讀寫),則無法同時讀取和發送資料。
使用shutdown代替close來關閉套接的好處:首先,close只有在最後一個活動引用被關閉時才釋放網路端點。而shutdown可以強制使一個通訊端處於不活動狀態,無論他的檔案描述符數目是多少。並且使用shutdown可以關閉單方向的資料轉送,或雙方向的資料轉送。
函數bind()將地址綁定到一個通訊端。
1 #include <sys/socket.h>2 int bind(int sockfd, const struct sockaddr *addr, socklen_t len);3 // 成功返回0;出錯返回-1
函數getsockname用來擷取綁定到sockfd上的通訊端。
若通訊端已和對方連結上,則函數getpeername來擷取對方的地址。
如果處理的是連線導向的網路服務(SOCK_STREAM或SOCK_SEQPACKET),在開始交換資料之前,需要在請求服務的進程通訊端和提供服務的進程通訊端之間建立一個邏輯串連。函數connect()提供了這個功能,建立一個串連。
1 #include <sys/socket.h>2 int connect(int sockfd, const struct sockaddr *addr, socklen_t len);3 // 成功返回0;錯誤返回-1
其中,若sockfd沒有綁定到一個地址上,connect會將其綁定到一個預設地址上。
伺服器端用listen()函數來監聽通訊端上的串連。
#include <sys/socket.h>int listen(int sockfd, int backlog); // 成功返回0;出錯返回-1
其中,參數backlog用於表示該進程所要入隊的串連請求的數量。一旦隊列滿,系統會拒絕多的串連請求。
一旦伺服器調用了listen監聽指定的地址,通訊端就能通過函數accept來擷取請求並建立串連。
#include <sys/socket.h>int accept(int sockfd, struct sockaddr* addr, socklen_t *len); // 若成功返迴文件(通訊端)描述符;若出錯返回-1。
2. 資料轉送
1)發送資料的三個函數,並且三個函數在發送成功時均返回傳送的位元組數;若出錯則返回-1:
- send函數。
1 #include <sys/socket.h>2 ssize_t send(int sockfd, const void *buf, size_t nbytes, int flags);
其中,對於支援位報文設限的協議,如果單個報文超過協議所支援的最大尺寸,send()函數失敗定將errno設定為EMSGSIZE;對於位元組流協議,send()函數將阻塞直到整個資料被傳輸完畢。
- sendto函數,該函數允許在不需連線的通訊端上指定一個目標地址。
#include <sys/socket.h>ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen);
- sendmsg函數,可以指定多重緩衝區來傳輸資料。
#include <sys/socket.h>ssize_t sendmsg(int sockfd, const struct msgaddr* msg, int flags)
2) 接收資料的三個函數,並三個函數成功時返回以位元組計數的訊息長度;若無可用訊息或對方已經按序結束並返回則返回0;若出錯則返回-1:
- recv函數,從指定通訊端上接收訊息,用選項來控制如何接收資料。
#include <sys/socket.h>ssize_t recv(int sockfd, void *buf, size_t nbytes, int flags);
- recvfrom函數,不僅完成函數recv功能,還能擷取資料寄件者的源地址。
#include <sys/socket.h>ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr* src_addr, socklen_t *addrlen);
- recvmsg函數,可以將接收到的資料送入多個緩衝區,或者可以接收輔助資料。
#include <sys/socket.h>ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags);
3. 通訊端選項
通訊端機制提供通訊端選項介面來控制通訊端行為,但僅支援擷取或設定下面三種選項:
- 通用選項,工作在所有通訊端類型上;
- 在通訊端層次管理的選項,但是依賴於下層協議的支援;
- 特定於某協議的選項,為每個協議所專屬
函數getsockopt()用來擷取當前通訊端的選項:
#include <sys/socket.h>int getsockopt(int sockfd, int level, int option, void *val, socklen_t *len); // 成功返回0;出錯則返回-1
函數setsockopt()用來設定通訊端的選項:
#include <sys/socket.h>int setsockopt(int sockfd, int level, int option, const void *val, socklen_t len); // 成功返回0;失敗則返回-1。
4. 非阻塞和非同步I/O
recv函數在沒有資料可用時會阻塞等待,而send函數會在資料隊列已滿的情況下阻塞等待。若將函數設定為非阻塞狀態,則函數在需要阻塞時失敗返回,並將errno設定為EWOULDBLOCK或EAGAIN。當這些情況發生時,可以使用poll、select或epoll來判斷何時接收或發送資料。
在基於通訊端的非同步I/O中,當能夠從通訊端中讀取資料,或通訊端寫隊列中有空間可用時,可以發送訊號SIGIO。通過兩個步驟來使用非同步I/O:
- 確立通訊端擁有者關係,訊號可以被傳遞到合適的進程:
在fcntl使用F_SETOWN命令。
- 當I/O操作不會阻塞時發送訊號告知通訊端:
在fcntl使用F_SETFL命令比並開機檔案標誌O_ASYNC。