索引:
1.
字節序函數
2.
字節操做函數
3.
地址轉換函數
4.
readn、writen和readline
5.
測試描述符類型
6.
socket函數
7.
connect函數
8.
bind函數
9.
listen函數
10.
accept函數
11.
close函數
12.
getsockname和getpeername
13.
select函數
14.
shutdown函數
15.
pselect函數
16.
poll函數
17.
getsockopt和setsockopt
18.
套接口選項列表
19.
處理套接口的fcntl函數
20.
gethostbyname函數
21.
gethostbyname2函數
22.
ethostbyaddr函數
23.
uname函數
24.
gethostname函數
25.
getservbyname函數
26.
getservbyport函數
27.
recv和send
28.
readv和writev
29.
readmsg和writemsg
30.
socketpair函數
31.
套接口ioctl函數
1.字節序函數
#include <netinet.h>
uint16_t htons(uint16_t host16bitvalue);
uint32_t htonl(uint32_t host32bitvalue);
返回:網絡字節序值
uint16_t ntohs(uint16_t net16bitvalue);
uint32_t ntohl(uint32_t net32bitvalue);
返回:主機字節序值html
一個測試本機字節序的程序,可參見見unpv12e:intro/byteorder.c。算法
2.字節操做函數
#include <strings.h>
void bzero(void *dest, size_t nbytes);
void bcopy(const void *src, void *dest, size_t nbytes);
int bcmp(const void *ptr1, const void *ptr2, size_t nbytes);
返回:0—相等,非0—不相等
#include <string.h>
void *memset(void *dest, int c, size_t len);
void *memcpy(void *dest, void *src, size_t nbytes);
int memcmp(const void *ptr1, const void *ptr2, size_t nbytes);
返回:0—相同,>0或<0—不相同;進行比較操做時,假定兩個不相等的字節均爲無符號字符(unsigned char)。編程
3.地址轉換函數
#include <arpa/inet.h>
int inet_aton(const char *strptr, struct in_addr *addrptr);
返回:1—串有效,0—串有錯。
in_addr_t inet_addr(const char *strptr);
返回:若成功,返回32爲二進制的網絡字節序地址;如有錯,則返回INADDR_NONE。api
char *inet_ntoa(struct in_addr inaddr);
返回:指向點分十進制數串的指針。數組
int inet_pton(int family, const char *strptr, void *addrptr);
返回:1—成功;0—輸入不是有效的表達格式,-1—出錯。服務器
const char *inet_ntop(int family, const void *addrptr, char *strptr, size_t len);
返回:指向結果的指針—成功,NULL—失敗。網絡
說明:併發
- inet_aton函數的指針若爲空,則函數仍然執行輸入串的有效性檢查,但不存儲任何結果。
- inet_addr的缺陷:出錯返回值INADDR_NONE等於255.255.255.255(IPv4的有限廣播地址),因此該函數不能處理此地址。
儘可能使用inet_aton,不使用inet_addr。
- inet_ntoa函數的執行結果放在靜態內存中,是不可重入的。
- 參數family能夠是AF_INET,也能夠是AF_INET6,若參數family不被支持,則出錯,errno置爲EAFNOSUPPORT。
- 指針addrptr是結構指針。
- len指定目標的大小,避免緩衝區溢出。若是len過小,則返回一個空指針,errno置爲ENOSPC。爲有助於規定該大小,有以下定義:
#include <netinet.h>
#define INET_ADDRSTRLEN 16 /*fro IPv4 dotted-decimal */
#define INET6_ADDRSTRLEN 46 /*for IPv6 hex string */
- inet_ntop函數的參數strptr不能爲空指針,成功時,此指針便是函數的返回值。
實現IPv4版本的inet_pton和inet_ntop的程序,參見:unpv12e:libfree/inet_pton_ipv4.c和libfree/inet_ntop_ipv4.c。
4.readn、writen和readline
函數原型以下:
ssize_t readn(int filedes, void *buff, size_t nbytes);
ssize-t writen(int filedes, void *buff, size_t nbytes);
ssize_t readline(int filedes, void *buff, size_t maxlen);
返回:讀寫字節數,-1—出錯。
實現程序見:unpv12e:lib/readn.c、lib/writen.c、lib/readline1.c和lib/readline.c。socket
5.測試描述符類型
#include <sys/stat.h>
int isfdtype( int fd, int fdtype);
返回:1—是指定類型,0—不是指定類型,-1—出錯。
要測試是否爲套接口描述子,fdtype應設爲S_IFSOCK。
該函數的一個實現程序,參見unpv12e:lib/isfdtype.c
6.socket函數
#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 密鑰套接口
type指定套接口類型:
- SOCK_STREAM 字節流套接口
- SOCK_DGRAM 數據報套接口
- SOCK_RAW 原始套接口
protocol通常設爲0,除非用在原始套接口上。
並不是全部family和type的組合都是有效的。
AF_LOCAL等於早期的AF_UNIX。
7.connect函數
#include <sys/socket.h>
int connect(int sockfd, const struct sockaddr *servaddr, socklen_t addrlen);
返回:0—成功,-1—出錯。
sockfd是socket函數返回的套接口描述字,servaddr和addrlen是指向服務器的套接口地址結構指針和結構大小。
在調用connect以前沒必要非得調用bind函數。
若是是TCP,則connect激發TCP的三路握手過程,在阻塞狀況下,只有在鏈接創建成功或出錯時該函數才返回,
出錯狀況:
- 沒有收到SYN分節的響應,在規定時間內通過重發仍無效,則返回ETIMEDOUT;
- 若是對SYN分節的響應是RST,表示服務器在指定端口上沒有相應的服務,返回ECONNREFUSED;
- 若是發出 SYN在中間路由器上引起一個目的地不可達ICMP錯誤,在規定時間內通過重發仍無效,則返回EHOSTUNREACH或ENETUNREACH錯誤。
注意:若是connect失敗,則套接口將不能再使用,必須關閉,不能對此套接口再調用函數connect。
8.bind函數
#include <sys/socket.h>
int bind(int sockfd, const struct sockaddr *maddr, socklen_t addrlen);
返回:0—成功,-1—出錯。
進程能夠把一個特定的IP地址捆綁到他的套接口上,但此IP地址必須是主機的一個接口。
對於IPv4,通配地址是INADDR_ANY,其值通常爲0;使用方法以下:
struct sockaddr_in servaddr;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
對於IPv6,方法以下:
struct sockaddr_in6 serv;
serv.sin6_addr = in6addr_any; (系統分配變量in6addr_any並將其初始化爲常值IN6ADDR_ANY_INIT。)
若是讓內核選擇臨時端口,注意的是bind並不返回所選的斷口值,要獲得一個端口,必須使用getsockname函數。
bind失敗的常見錯誤是EADDRINUSE(地址已使用)。
9.listen函數
#include <sys/socket.h>
int listen(int sockfd, int backlog);
返回:0—成功,-1—出錯。
listen把未鏈接的套接口轉化爲被動套接口,指示內核應接受指向此套接口的鏈接請求。第二個參數規定了內核爲此套接口排隊的最大鏈接數。
參數backlog曾經規定爲監聽套接口上的未完成鏈接隊列和已完成鏈接隊列總和的最大值,但各個系統的定義方法都不盡相同;歷史上常把backlog置爲5,但對於繁忙的服務器是不夠的;backlog的設置沒有一個通用的方法,依狀況而定,但不要設爲0。
10.accept函數
#include <sys/socket.h>
int accept(int sockfd, struct sockaddr *cliaddr, socklen_t *addrlen);
返回:非負描述字—OK,-1—出錯。
accept從已完成鏈接隊列頭返回下一個鏈接,若已完成鏈接隊列爲空,則進程睡眠(套接口爲阻塞方式時)。
參數cliaddr和addrlen返回鏈接對方的協議地址,其中addrlen是值-結果參數,調用前addrlen所指的整數值要置爲cliaddr所指的套接口結構的長度,返回時由內核修改。
accept成功執行後,返回一個鏈接套接口描述字。
若是對客戶的協議地址沒有興趣,能夠把cliaddr和addrlen置爲空指針。
11.close函數
#include <unistd.h>
int close(int sockfd);
返回:0—OK,-1—出錯。
TCP套接口的close缺省功能是將套接口作上「已關閉」標記,並當即返回到進程。這個套接口描述字不能再爲進程使用,但TCP將試着發送已排隊待發的任何數據,而後按正常的TCP鏈接終止序列進行操做。
close把描述字的訪問計數減1,當訪問計數仍大於0時,close並不會引起TCP的四分組鏈接終止序列。若確實要發一個FIN,能夠用函數shutdown。
12.getsockname和getpeername
#include <sys/socket.h>
int getsockname(int sockfd, struct sockaddr *localaddr, socklen_t *addrlen);
int getpeername(int sockfd, struct sockaddr *peeraddr, socklen_t *addrlen);
返回:0—OK,-1—出錯。
getsockname函數返回與套接口關聯的本地協議地址。
getpeername函數返回與套接口關聯的遠程協議地址。
addrlen是值-結果參數。
使用場合:
- 在不調用bind的TCP客戶,當connect成功返回後,getsockname返回分配給此鏈接的本地IP地址和本地端口號;
- 在以端口號爲0調用bind後,使用getsockname返回內核分配的本地端口號;
- getsockname可用來獲取某套接口的地址族;
- 在捆綁了通配IP地址的TCP服務器上,當鏈接創建後,可使用getsockname得到分配給此鏈接的本地IP地址;
- 當一個服務器調用exec啓動後,他得到客戶身份的惟一途徑是調用getpeername函數。
13.select函數
#include <sys/select.h>
#include <sys/time.h>
int select(int maxfdp1, fd_set *readset, fd_set *writeset, fd_set *exceptset, const struct timeval *timeout);
返回:準備好描述字的正數目,0—超時,-1—出錯。
結構timeval的定義:
struct timeval {
long tv_sec; /* seconds */
long tv_usec; /* microseconds */
};
timeout取值的三種狀況:
- 永遠等下去:僅在有一個描述字準備好I/O時才返回,設置timeout爲空指針;
- 等待固定時間:在有一個描述字準備好I/O時返回,但不超過由timeout參數所指定的秒數和微秒數;
- 根本不等待:檢查描述字後當即返回,將timeout中的秒數和微秒數都設置爲0。
在等待過程當中,若進程捕獲了信號並從信號處理程序返回,等待通常被中斷,爲了可移植性,必須準備好select返回EINTR錯誤。
timeout的值在返回時並不會被select修改(const標誌)。
readset、writeset、exceptset指定咱們要讓內核測試讀、寫和異常條件所需的描述字。
當前支持的異常條件有兩個:
- 套接口帶外數據的到達;
- 控制狀態信息的存在,可從一個已置爲分組方式的僞終端主端讀到。
描述字集的使用:
數據類型:fd_set;
void FD_ZERO(fd_set *fdset);
void FD_SET(int fd, fd_set *fdset);
void FD_CLR(int fd, fd_set *fdset);
void FD_ISSET(int fd, fd_set *fdset);
參數maxfdp1指定被測試的描述字個數,它的值是要被測試的最大描述字加1。描述字0,1,2,…,maxfdp1-1都被測試。
readset、writeset、exceptset是值-結果參數,select修改三者所指的描述字集。因此,每次調用select時,咱們都要將全部描述字集中關心的位置爲1。
套接口準備好讀的條件:
- 套接口接收緩衝區中的數據字節數大於等於套接口接收緩衝區低潮限度的當前值。對這樣的套接口的讀操做將不阻塞並返回一個大於0的值(即準備好讀入的數據量)。能夠用套接口選項SO_RCVLOWAT來設置低潮限度,對於TCP和UDP,缺省值爲1;
- 鏈接的讀這一半關閉(接收了FIN的TCP鏈接)。對這樣的套接口讀操做將不阻塞而且返回0(即文件結束符);
- 套接口是一個監聽套接口且已完成的鏈接數爲非0;
- 有一個套接口錯誤待處理。對這樣的套接口讀操做將不阻塞且返回一個錯誤,errno設置成明確的錯誤條件。這些待處理錯誤也能夠經過指定套接口選項SO_ERROR調用getsockopt來取得並清除。
套接口準備好寫的條件:
- 套接口發送緩衝區中的可用字節數大於等於套接口發送緩衝區低潮限度的當前值,且或者(1)套接口已鏈接,或者(2)套接口不要求鏈接(如UDP套接口)。能夠用套接口選項SO_SNDLOWAT來設置此低潮限度,對於TCP和UDP,缺省值爲2048;
- 鏈接的寫這一半關閉。對這樣的套接口寫將產生信號SIGPIPE;
- 有一個套接口錯誤待處理。對這樣的套接口寫操做將不阻塞且返回一個錯誤,errno設置成明確的錯誤條件。這些待處理錯誤也能夠經過指定套接口選項SO_ERROR調用getsockopt來取得並清除。
若是一個套接口存在帶外數據或者仍處於帶外標記,那它有異常條件待處理。
一個套接口出錯時,它被select標記爲既可讀又可寫。
14.shutdown函數
#include <sys/socket.h>
int shutdown(int sockfd, int howto);
返回:0—成功,-1—失敗。
函數的行爲依賴於參數howto的值:
- SHUT_RD:關閉鏈接的讀這一半,再也不接收套接口中的數據且留在套接口緩衝區中的數據都做廢。進程不能再對套接口任何讀函數。調用此函數後,由TCP套接口接收的任何數據都被確認,但數據自己被扔掉。
- SHUT_WR:關閉鏈接的寫這一半,在TCP場合下,這稱爲半關閉。當前留在套接口發送緩衝區中的數據都被髮送,後跟正常的TCP鏈接終止序列。此半關閉無論套接口描述字的訪問計數是否大於0。進程不能再執行對套接口的任何寫函數。
SHUT_RDWR:鏈接的讀這一半和寫這一半都關閉。這等效於調用shutdown兩次:第一次調用時用SHUT_RD,第二次調用時用SHUT_WR。
15.pselect函數
#include <sys/select.h>
#include <signal.h>
#include <time.h>
int pselect(int maxfdp1, fd_set *readset, fd_set *writeset, fd_set *exceptset, const struct timespec *timeout, const sigset_t *sigmask);
返回:準備好描述字的個數,0—超時,-1—出錯。
pselect是Posix.1g發明的。相對select的變化:
- pselect使用結構timespec:
struct timespec {
time_t tv_sec; /* seconds */
long tv_nsec; /* nanoseconds */
};
新結構中的tv_nsec規定納秒數。
- pselect增長了第六個參數:指向信號掩碼的指針。容許程序禁止遞交某些信號。
16.poll函數
#include <poll.h>
int poll(struct pollfd *fdarray, unsigned long nfds, int timeout);
返回:準備好描述字的個數,0—超時,-1—出錯。
第一個參數是指向一個結構數組的第一個元素的指針,每一個數組元素都是一個pollfd結構:
struct pollfd {
int fd; /* descriptor to check */
short events; /* events of interest on fd */
short revents; /* events that occurred on fd */
};
要測試的條件由成員events規定,函數在相應的revents成員中返回描述字的狀態(一個描述字有兩個變量:一個爲調用值,一個爲結果)。
第二個參數指定數組中元素的個數。
第三個參數timeout指定函數返回前等待多長時間,單位是毫秒。可能值以下:
- INFTIM,永遠等待;
- 0,當即返回,不阻塞;
- >0,等待指定數目的毫秒數。
標誌的範圍:
常量 |
能做爲events的輸入嗎? |
能做爲revents的結果嗎? |
解釋 |
POLLIN |
yes |
yes |
普通或優先級帶數據可讀 |
POLLRDNORM |
yes |
yes |
普通數據可讀 |
POLLRDBAND |
yes |
yes |
優先級帶數據可讀 |
POLLPRI |
yes |
yes |
高優先級數據可讀 |
POLLOUT |
yes |
yes |
普通或優先級帶數據可寫 |
POLLWRNORM |
yes |
yes |
普通數據可寫 |
POLLWRBAND |
yes |
yes |
優先級帶數據可寫 |
POLLERR |
|
yes |
發生錯誤 |
POLLHUP |
|
yes |
發生掛起 |
POLLNVAL |
|
yes |
描述字不是一個打開的文件 |
圖可分爲三部分:處理輸入的四個常值;處理輸出的三個常值;處理錯誤的三個常值。
poll識別三個類別的數據:普通(normal)、優先級帶(priority band)、高優先級(high priority)。術語來自流的概念。
返回條件:
- 全部正規TCP數據和UDP數據都被認爲是普通數據;
- TCP的帶外數據被認爲是優先級帶數據;
- 當TCP鏈接的讀這一半關閉時(如接收了一個FIN),這也認爲是普通數據,且後續的讀操做將返回0;
- TCP鏈接存在錯誤既能夠認爲是普通數據,也能夠認爲是錯誤(POLLERR)。不管哪一種狀況,後續的讀操做將返回-1,並將errno置爲適當的值,這就處理了諸如接收到RST或超時等條件;
- 在監聽套接口上新鏈接的可用性既可認爲是普通數據,也能夠認爲是優先級帶數據,大多數實現都將其做爲普通數據考慮。
- 若是不關心某個特定的描述字,可將其pollfd結構的fd成員置爲一個負值,這樣就能夠忽略成員events,且返回時將成員revents的值置爲0。
poll沒有select存在的最大描述字數目問題。但可移植性select要好於poll。
17.getsockopt和setsockopt
#include <sys/socket.h>
int getsockopt(int sockfd, int level, int optname, void *optval, socklen_t *optlen);
int setsockopt(int sockfd, int level, int optname, void *optval, socklen_t *optlen);
返回:0—OK,-1—出錯。
sockfd必須是一個打開的套接口描述字;level(級別)指定系統中解釋選項的代碼:普通套接口代碼或特定於協議的代碼);optval是一個指向變量的指針;此變量的大小由最後一個參數決定。
對於某些套接口選項,何時進行設置或獲取是有差異的。下面的套接口選項是由TCP已鏈接套接口從監聽套接口繼承來的:
- SO_DEBUG;
- SO_DONTROUTE;
- SO_KEEPALIVE;
- SO_LINGER;
- SO_OOBINLINE;
- SO_RCVBUF;
- SO_SNDBUF。
若是想在三路握手完成時確保這些套接口選項中的某一個是給已鏈接套接口設置的,咱們必須先給監聽套接口設置此選項。
18.套接口選項列表
level |
Optname |
get |
set |
說明 |
標誌 |
數據類型 |
|
|
|
|
|
|
|
SOL_SOCKET |
SO_BROADCAST |
y |
y |
容許發送廣播數據報 |
y |
int |
|
SO_DEBUG |
y |
y |
使能調試跟蹤 |
y |
int |
|
SO_DONTROUTE |
y |
y |
旁路路由表查詢 |
y |
int |
|
SO_ERROR |
y |
|
獲取待處理錯誤並消除 |
|
int |
|
SO_KEEPALIVE |
y |
y |
週期性測試鏈接是否存活 |
y |
int |
|
SO_LINGER |
y |
y |
如有數據待發送則延遲關閉 |
|
linger{} |
|
SO_OOBINLINE |
y |
y |
讓接收到的帶外數據繼續在線存放 |
y |
int |
|
SO_RCVBUF |
y |
y |
接收緩衝區大小 |
|
int |
|
SO_SNDBUF |
y |
y |
發送緩衝區大小 |
|
int |
|
SO_RCVLOWAT |
y |
y |
接收緩衝區低潮限度 |
|
int |
|
SO_SNDLOWAT |
y |
y |
發送緩衝區低潮限度 |
|
int |
|
SO_RCVTIMEO |
y |
y |
接收超時 |
|
timeval{} |
|
SO_SNDTIMEO |
y |
y |
發送超時 |
|
timeval{} |
|
SO_REUSEADDR |
y |
y |
容許重用本地地址 |
y |
int |
|
SO_REUSEPORT |
y |
y |
容許重用本地地址 |
y |
int |
|
SO_TYPE |
y |
|
取得套接口類型 |
|
int |
|
SO_USELOOPBACK |
y |
y |
路由套接口取得所發送數據的拷貝 |
y |
int |
|
|
|
|
|
|
|
IPPROTO_IP |
IP_HDRINCL |
y |
y |
IP頭部包括數據 |
y |
int |
|
IP_OPTIONS |
y |
y |
IP頭部選項 |
|
見後面說明 |
|
IP_RECVDSTADDR |
y |
y |
返回目的IP地址 |
y |
int |
|
IP_RECVIF |
y |
y |
返回接收到的接口索引 |
y |
int |
|
IP_TOS |
y |
y |
服務類型和優先權 |
|
int |
|
IP_TTL |
y |
y |
存活時間 |
|
int |
|
IP_MULTICAST_IF |
y |
y |
指定外出接口 |
|
in_addr{} |
|
IP_MULTICAST_TTL |
y |
y |
指定外出TTL |
|
u_char |
|
IP_MULTICAST_LOOP |
y |
y |
指定是否回饋 |
|
u_char |
|
IP_ADD_MEMBERSHIP |
|
y |
加入多播組 |
|
ip_mreq{} |
|
IP_DROP_MEMBERSHIP |
|
y |
離開多播組 |
|
ip_mreq{} |
|
|
|
|
|
|
|
IPPROTO_ICMPV6 |
ICMP6_FILTER |
y |
y |
指定傳遞的ICMPv6消息類型 |
|
icmp6_filter{} |
|
|
|
|
|
|
|
IPPROTO_IPV6 |
IPV6_ADDRFORM |
y |
y |
改變套接口的地址結構 |
|
int |
|
IPV6_CHECKSUM |
y |
y |
原始套接口的校驗和字段偏移 |
|
int |
|
IPV6_DSTOPTS |
y |
y |
接收目標選項 |
y |
int |
|
IPV6_HOPLIMIT |
y |
y |
接收單播跳限 |
y |
int |
|
IPV6_HOPOPTS |
y |
y |
接收步跳選項 |
y |
int |
|
IPV6_NEXTHOP |
y |
y |
指定下一跳地址 |
y |
sockaddr{} |
|
IPV6_PKTINFO |
y |
y |
接收分組信息 |
y |
int |
|
IPV6_PKTOPTIONS |
y |
y |
指定分組選項 |
|
見後面說明 |
|
IPV6_RTHDR |
y |
y |
接收原路徑 |
y |
int |
|
IPV6_UNICAST_HOPS |
y |
y |
缺省單播跳限 |
|
int |
|
IPV6_MULTICAST_IF |
y |
y |
指定外出接口 |
|
in6_addr{} |
|
IPV6_MULTICAST_HOPS |
y |
y |
指定外出跳限 |
|
u_int |
|
IPV6_MULTICAST_LOOP |
y |
y |
指定是否回饋 |
y |
u_int |
|
IPV6_ADD_MEMBERSHIP |
|
y |
加入多播組 |
|
ipv6_mreq{} |
|
IPV6_DROP_MEMBERSHIP |
|
y |
離開多播組 |
|
ipv6_mreq{} |
|
|
|
|
|
|
|
IPPROTO_TCP |
TCP_KEEPALIVE |
y |
y |
控測對方是否存活前鏈接閒置秒數 |
|
int |
|
TCP_MAXRT |
y |
y |
TCP最大重傳時間 |
|
int |
|
TCP_MAXSEG |
y |
y |
TCP最大分節大小 |
|
int |
|
TCP_NODELAY |
y |
y |
禁止Nagle算法 |
y |
int |
|
TCP_STDURG |
y |
y |
緊急指針的解釋 |
y |
int |
詳細說明:
SO_BROADCAST
使能或禁止進程發送廣播消息的能力。只有數據報套接口支持廣播,而且還必須在支持廣播消息的網絡上(如以太網、令牌環網等)。
若是目的地址是廣播地址但此選項未設,則返回EACCES錯誤。
SO_DEBUG
僅僅TCP支持。當打開此選項時,內核對TCP在此套接口所發送和接收的全部分組跟蹤詳細信息。這些信息保存在內核的環形緩衝區內,可由程序trpt進行檢查。
SO_DONTROUTE
此選項規定發出的分組將旁路底層協議的正常路由機制。
該選項常常由路由守護進程(routed和gated)用來旁路路由表(路由表不正確的狀況下),強制一個分組從某個特定接口發出。
SO_ERROR
當套接口上發生錯誤時,源自Berkeley的內核中的協議模塊將此套接口的名爲so_error的變量設爲標準的UNIX Exxx值中的一個,它稱爲此套接口的待處理錯誤(pending error)。內核可當即以如下兩種方式通知進程:
- 若是進程阻塞於次套接口的select調用,則不管是檢查可讀條件仍是可寫條件,select都返回並設置其中一個或全部兩個條件。
- 若是進程使用信號驅動I/O模型,則給進程或進程組生成信號SIGIO。
進程而後能夠經過獲取SO_ERROR套接口選項來獲得so_error的值。由getsockopt返回的整數值就是此套接口的待處理錯誤。so_error隨後由內核復位爲0。
當進程調用read且沒有數據返回時,若是so_error爲非0值,則read返回-1且errno設爲so_error的值,接着so_error的值被複位爲0。若是此套接口上有數據在排隊,則read返回那些數據而不是返回錯誤條件。
若是進程調用write時so_error爲非0值,則write返回-1且errno設爲so_error的值,隨後so_error也被複位。
SO_KEEPALIVE
打開此選項後,若是2小時內在此套接口上沒有任何數據交換,TCP就會自動給對方發一個保持存活探測分節,結果以下:
- 對方以指望的ACK響應,則一切正常,應用程序得不到通知;
- 對方以RST響應,套接口的待處理錯誤被置爲ECONNRESET,套接口自己則被關閉;
- 對方對探測分節無任何響應,通過重試都沒有任何響應,套接口的待處理錯誤被置爲ETIMEOUT,套接口自己被關閉;若接收到一個ICMP錯誤做爲某個探測分節的響應,則返回相應錯誤。
此選項通常由服務器使用。服務器使用它是爲了檢測出半開鏈接並終止他們。
SO_LINGER
此選項指定函數close對面向鏈接的協議如何操做(如TCP)。缺省close操做是當即返回,若是有數據殘留在套接口緩衝區中則系統將試着將這些數據發送給對方。
SO_LINGER選項用來改變此缺省設置。使用以下結構:
struct linger {
int l_onoff; /* 0 = off, nozero = on */
int l_linger; /* linger time */
};
有下列三種狀況:
- l_onoff爲0,則該選項關閉,l_linger的值被忽略,等於缺省狀況,close當即返回;
- l_onoff爲非0,l_linger爲0,則套接口關閉時TCP夭折鏈接,TCP將丟棄保留在套接口發送緩衝區中的任何數據併發送一個RST給對方,而不是一般的四分組終止序列,這避免了TIME_WAIT狀態;
- l_onoff 爲非0,l_linger爲非0,當套接口關閉時內核將拖延一段時間(由l_linger決定)。若是套接口緩衝區中仍殘留數據,進程將處於睡眠狀態,直到(a)全部數據發送完且被對方確認,以後進行正常的終止序列(描述字訪問計數爲0)或(b)延遲時間到。此種狀況下,應用程序檢查close的返回值是很是重要的,若是在數據發送完並被確認前時間到,close將返回EWOULDBLOCK錯誤且套接口發送緩衝區中的任何數據都丟失。close的成功返回僅告訴咱們發送的數據(和FIN)已由對方TCP確認,它並不能告訴咱們對方應用進程是否已讀了數據。若是套接口設爲非阻塞的,它將不等待close完成。
l_linger的單位依賴於實現,4.4BSD假設其單位是時鐘滴答(百分之一秒),但Posix.1g規定單位爲秒。
讓客戶知道服務器已經讀其數據的一個方法時:調用shutdown(SHUT_WR)而不是調用close,並等待對方close鏈接的本地(服務器)端。
SO_OOBINLINE
此選項打開時,帶外數據將被保留在正常的輸入隊列中(即在線存放)。當發生這種狀況時,接收函數的MSG_OOB標誌不能用來讀帶外數據。
SO_RCVBUF和SO_SNDBUF
每一個套接口都有一個發送緩衝區和一個接收緩衝區,使用這兩個套接口選項能夠改變缺省緩衝區大小。
當設置TCP套接口接收緩衝區的大小時,函數調用順序是很重要的,由於TCP的窗口規模選項是在創建鏈接時用SYN與對方互換獲得的。對於客戶,SO_RCVBUF選項必須在connect以前設置;對於服務器,SO_RCVBUF選項必須在listen前設置。
TCP套接口緩衝區的大小至少是鏈接的MSS的三倍,而必須是鏈接的MSS的偶數倍。
SO_RCVLOWAT和SO_SNDLOWAT
每一個套接口有一個接收低潮限度和一個發送低潮限度,他們由函數select使用。這兩個選項能夠修改他們。
接收低潮限度是讓select返回「可讀」而在套接口接收緩衝區中必須有的數據量,對於一個TCP或UDP套接口,此值缺省爲1。發送低潮限度是讓select返回「可寫」而在套接口發送緩衝區中必須有的可用空間,對於TCP套接口,此值常爲2048。
SO_RCVTIMEO和SO_SNDTIMEO
使用這兩個選項能夠給套接口設置一個接收和發送超時。經過設置參數的值爲0秒和0微秒來禁止超時。缺省時兩個超時都是禁止的。
接收超時影響5個輸入函數:read、readv、recv、recvfrom和recvmsg;發送超時影響5個輸出函數:write、writev、send、sendto和sendmsg。
SO_REUSEADDR和SO_REUSEPORT
SO_REUSEADDR提供以下四個功能:
- SO_REUSEADDR容許啓動一個監聽服務器並捆綁其衆所周知端口,即便之前創建的將此端口用作他們的本地端口的鏈接仍存在。這一般是重啓監聽服務器時出現,若不設置此選項,則bind時將出錯。
- SO_REUSEADDR容許在同一端口上啓動同一服務器的多個實例,只要每一個實例捆綁一個不一樣的本地IP地址便可。對於TCP,咱們根本不可能啓動捆綁相同IP地址和相同端口號的多個服務器。
- SO_REUSEADDR容許單個進程捆綁同一端口到多個套接口上,只要每一個捆綁指定不一樣的本地IP地址便可。這通常不用於TCP服務器。
- SO_REUSEADDR容許徹底重複的捆綁:當一個IP地址和端口綁定到某個套接口上時,還容許此IP地址和端口捆綁到另外一個套接口上。通常來講,這個特性僅在支持多播的系統上纔有,並且只對UDP套接口而言(TCP不支持多播)。
SO_REUSEPORT選項有以下語義:
- 此選項容許徹底重複捆綁,但僅在想捆綁相同IP地址和端口的套接口都指定了此套接口選項才性。
- 若是被捆綁的IP地址是一個多播地址,則SO_REUSEADDR和SO_REUSEPORT等效。
使用這兩個套接口選項的建議:
- 在全部TCP服務器中,在調用bind以前設置SO_REUSEADDR套接口選項;
- 當編寫一個同一時刻在同一主機上可運行屢次的多播應用程序時,設置SO_REUSEADDR選項,並將本組的多播地址做爲本地IP地址捆綁。
SO_TYPE
該選項返回套接口的類型,返回的整數值是一個諸如SOCK_STREAM或SOCK_DGRAM這樣的值。
SO_USELOOPBACK
該選項僅用於路由域(AF_ROUTE)的套接口,它對這些套接口的缺省設置爲打開(這是惟一一個缺省爲打開而不是關閉的SO_xxx套接口選項)。當此套接口打開時,套接口接收在其上發送的任何數據的一個拷貝。
禁止這些回饋拷貝的另外一個方法是shutdown,第二個參數應設爲SHUT_RD。
IP_HDRINCL
若是一個原始套接口設置該選項,則咱們必須爲全部發送到此原始套接口上的數據報構造本身的IP頭部。
IP_OPTIONS
設置此選項容許咱們在IPv4頭部中設置IP選項。這要求掌握IP頭部中IP選項的格式信息。
IP_RECVDSTADDR
該選項致使所接收到的UDP數據報的目的IP地址由函數recvmsg做爲輔助數據返回。
IP_RECVIF
該選項致使所接收到的UDP數據報的接口索引由函數recvmsg做爲輔助數據返回。
IP_TOS
該選項使咱們能夠給TCP或UDP套接口在IP頭部中設置服務類型字段。若是咱們給此選項調用getsockopt,則放到外出IP數據報頭部的TOS字段中的當前值將返回(缺省爲0)。尚未辦法從接收到的IP數據報中取此值。
能夠將TOS設置爲以下的值:
- IPTOS_LOWDELAY:最小化延遲
- IPTOS_THROUGHPUT:最大化吞吐量
- IPTOS_RELIABILITY:最大化可靠性
- IPTOS_LOWCOST:最小化成本
IP_TTL
用次選項,能夠設置和獲取系統用於某個給定套接口的缺省TTL值(存活時間字段)。與TOS同樣,沒有辦法從接收到的數據報中獲得此值。
ICMP6_FILTER
可獲取和設置一個icmp6_filter結構,他指明256個可能的ICMPv6消息類型中哪個傳遞給在原始套接口上的進程。
IPV6_ADDRFORM
容許套接口從IPv4轉換到IPv6,反之亦可。
IPV6_CHECKSUM
指定用戶數據中校驗和所處位置的字節偏移。若是此值爲非負,則內核將(1)給全部外出分組計算並存儲校驗和;(2)輸入時檢查所收到的分組的校驗和,丟棄帶有無效校驗和的分組。此選項影響出ICMPv6原始套接口外的全部IPv6套接口。若是指定的值爲-1(缺省值),內核在此原始套接口上將不給外出的分組計算並存儲校驗和,也不檢查所收到的分組的校驗和。
IPV6_DSTOPTS
設置此選項指明:任何接收到的IPv6目標選項都將由recvmsg做爲輔助數據返回。此選項缺省爲關閉。
IPV6_HOPLIMIT
設置此選項指明:接收到的跳限字段將由recvmsg做爲輔助數據返回。
IPV6_HOPOPTS
設置此選項指明:任何接收到的步跳選項都將由recvmsg做爲輔助數據返回。
IPV6_NEXTHOP
這不是一個套接口選項,而是一個可指定個sendmsg的輔助數據對象的類型。此對象以一個套接口地址結構指定某個數據報的下一跳地址。
IPV6_PKTINFO
設置此選項指明:下面關於接收到的IPv6數據報的兩條信息將由recvmsg做爲輔助數據返回:目的IPv6地址和到達接口索引。
IPV6_PKTOPTIONS
大多數IPv6套接口選項假設UDP套接口使用recvmsg和sendmsg所用的輔助數據在內核與應用進程間傳遞信息。TCP套接口使用IPV6_PKTOPTIONS來獲取和存儲這些值。
IPV6_RTHDR
設置此選項指明:接收到的IPv6路由頭部將由recvmsg做爲輔助數據返回。
IPV6_UNICAST_HOPS
相似於IPv4的IP_TTL,它的設置指定發送到套接口上的外出數據報的缺省跳限,而它的獲取則返回內核將用於套接口的跳限值。爲了從接收到的IPv6數據報中獲得真實的跳限字段,要求使用IPV6_HOPLIMIT套接口選項。
TCP_KEEPALIVE
它指定TCP開始發送保持存活探測分節前以秒爲單位的鏈接空閒時間。缺省值至少爲7200秒,即2小時。該選項僅在SO_KEEPALIVE套接口選項打開時纔有效。
TCP_MAXRT
它指定一旦TCP開始重傳數據,在鏈接斷開以前需經歷的以秒爲單位的時間總量。值0意味着使用系統缺省值,值-1意味着永遠重傳數據。
TCP_MAXSEG
容許獲取或設置TCP鏈接的最大分節大小(MSS)。返回值是咱們的TCP發送給另外一端的最大數據量,他經常就是由另外一端用SYN分節通告的MSS,除非咱們的TCP選擇使用一個比對方通告的MSS小的值。若是此選項在套接口鏈接以前取得,則返回值爲未從另外一端收到的MSS選項的狀況下所用的缺省值。
TCP_NODELAY
若是設置,此選項禁止TCP的Nagle算法。缺省時,該算法是使能的。
Nagle算法的目的是減小WAN上小分組的數目。
Nagle算法經常與另外一個TCP算法聯合使用:延遲ACK(delayed ACK)算法。
解決屢次寫致使Nagle算法和延遲ACK算法負面影響的方法:
- 使用writev而不是屢次write;
- 合併緩衝區,對此緩衝區使用一次write;
- 設置TCP_NODELAY選項,繼續調用write屢次,這是最不可取的解決方法。
TCP_STDURG
它影響對TCP緊急指針的解釋。
19.處理套接口的fcntl函數
#include <fcntl.h>
int fcntl(int fd, int cmd, … /* arg */);
返回:依賴於參數cmd—成功,-1—失敗。
函數fcntl提供了以下關於網絡編程的特性:
- 非阻塞I/O:經過用F_SETFL命令設置O_NONBLOCK文件狀態標誌來設置套接口爲非阻塞型。
- 信號驅動I/O:用F_SETFL命令來設置O_ASYNC文件狀態標誌,這致使在套接口狀態發生變化時內核生成信號SIGIO。
- F_SETOWN命令設置套接口屬主(進程ID或進程組ID),由它來接收信號SIGIO和SIGURG。SIGIO在設置套接口爲信號驅動I/O型時生成,SIGURG在新的帶外數據到達套接口時生成。
- F_GETOWN命令返回套接口的當前屬主。
注意事項:
- 設置某個文件狀態標誌時,先取得當前標誌,與新標誌路邏輯或後再設置標誌。
- 信號SIGIO和SIGURG與其餘信號不一樣之處在於,這兩個信號只有在已使用命令F_SETOWN給套接口指派了屬主後纔會生成。F_SETOWN命令的整參數arg既能夠是一個正整數,指明接收信號的進程ID,也能夠是一個負整數,它的絕對值是接收信號的進程組ID。
- 當一個新的套接口由函數socket建立時,他沒有屬主,可是當一個新的套接口從一個監聽套接口建立時,套接口屬主便由已鏈接套接口從監聽套接口繼承而來。
20.gethostbyname函數
#include <netdb.h>