如果處理的是面向連接的網絡服務(SOCK_STREAM或SOCK_SEQPACKET),在開始交換數據以前,需要在請求服務的進程套接字(客戶端)和提供服務的進程套接字(服務器)之間建立一個連接。客戶端可以用connect建立一個連接。
#include <sys/socket.h>int connect(int sockfd, const struct sockaddr *addr, socklen_t len);返回值:若成功則返回0,出錯則返回-1
在connect中所指定的地址是想與之通信的服務器地址。如果sockfd沒有綁定到一個地址,connect會給調用者綁定一個默認地址。
當連接一個服務器時,出于一些原因,連接可能失敗。要連接的機器必須開啟并且正在運行,服務器必須綁定到一個想與之連接的地址,并且在服務器的等待連接隊列中應有足夠的空間。因此,應用程序必須能夠處理connect返回的錯誤,這些錯誤可能由一些瞬時變化條件引起。
實例
程序清單16-2顯示了一種如何處理瞬時connect錯誤的方法。這在一個負載很重的服務器上很有可能發生。
#include "apue.h"#include <sys/socket.h>#define MAXSLEEP 128intconnect_retry(int sockfd, const struct sockaddr *addr, socklen_t len){ int nsec; /* * Try to connect with exponential backoff. */ for(nsec = 1; nsec <= MAXSLEEP; nsec <<= 1) { if(connect(sockfd, addr, alen) == 0) { /* * Connection accepted. */ return(0); } /* * Delay before trying again. */ if(nsec <= MAXSEELP/2) sleep(nsec); } return(-1);}
這個函數使用了名為指數補償(exponential backoff)的算法。如果調用connect失敗,進程就休眠一小段時間后再嘗試,每循環一次增加每次嘗試的延遲,直到最大延遲為2分鐘。
如果套接字描述符處于非阻塞模式下,那么在連接不能馬上建立時,connect將會返回-1,并且將errno設為特殊的錯誤碼EINPROGRESS。應用程序可以使用poll或select來判斷文件描述符何時可寫。如果可寫,連接完成。
函數connect還可以用于無連接的網絡服務(SOCK_DGRAM)。這看起來有點矛盾,實際上卻是一個不錯的選擇。如果在SOCK_DGRAM套接字上調用connect,所有發送報文的目標地址設為connect調用中所指定的地址,這樣每次傳送報文時就不需要再提供地址。另外,僅能接收來自指定地址的報文。
服務器調用listen來宣告可以接受連接請求。
#include <sys/socket.h>int listen(int sockfd, int backlog);返回值:若成功則返回0,出錯則返回-1
參數backlog提供了一個提示,用于表示該進程所要入隊的連接請求數量。其實際值由系統決定,但上限由<sys/socket.h>中SOMAXCONN指定。
一旦隊列滿,系統會拒絕多余連接請求,所以backlog的值應該基于服務器期望負載和接受連接請求與啟動服務的處理能力來選擇。
一旦服務器調用了listen,套接字就能接收連接請求。使用函數accept獲得連接請求并建立連接。
#include <sys/socket.h>int accept(int sockfd, struct sockaddr *restrict addr, socklen_t *restrict len);返回值:若成功則返回文件(套接字)描述符,出錯則返回-1
函數accept所返回的文件描述符是套接字描述符,該描述符連接到調用connect的客戶端。這個新的套接字描述符和原始套接字(sockfd)具有相同的套接字類型和地址族。傳給accept的原始套接字沒有關聯到這個連接,而是繼續保持可用狀態并接受其他連接請求。
如果不關心客戶端標識,可以將參數addr和len設為NULL;否則,在調用accept之前,應將參數addr設為足夠大的緩沖區來存放地址,并且將len設為指向代表這個緩沖區大小的整數的指針。返回時,accept會在緩沖區填充客戶端的地址并且更新指針len所指向的整數為該地址的大小。
如果沒有連接請求等待處理,accept會阻塞直到一個請求到來。如果sockfd處于非阻塞模式,accept會返回-1并將errno設置為EAGAIN或EWOULDBLOCK。
如果服務器調用accept并且當前沒有連接請求,服務器會阻塞直到一個請求到來。另外,服務器可以使用poll或select來等待一個請求的到來。在這種情況下,一個帶等待處理的連接請求套接字會以可讀的方式出現。
實例
程序清單16-3顯示了一個服務器進程用以分配和初始化套接字的函數。
程序清單16-3 服務器初始化套接字端點
#include "apue.h"#include <errno.h>#include <sys/socket.h>intinitserver(int type, const struct sockaddr *addr, socklen_t alen, int qlen){ int fd; int err = 0; if((fd = socket(addr->sa_family, type, 0)) < 0) return(-1); if(bind(fd, addr, alen) < 0) { err = errno; goto errout; } if(type == SOCK_STREAM || type == SOCK_SEQPACKET) { if(listen(fd, qlen) < 0) { err = errno; goto errout; } } return(fd);errout: close(fd); errno = err; return(-1);}
本篇博文內容摘自《UNIX環境高級編程》(第2版),僅作個人學習記錄所用。關于本書可參考:http://www.apuebook.com/。
新聞熱點
疑難解答