索引:
1.
字節(jié)序函數(shù)2.
字節(jié)操作函數(shù)3.
地址轉(zhuǎn)換函數(shù)4.
readn、writen和readline5.
測(cè)試描述符類型6.
socket函數(shù)7.
connect函數(shù)8.
bind函數(shù)9.
listen函數(shù)10.
accept函數(shù)11.
close函數(shù)12.
getsockname和getpeername13.
select函數(shù)14.
shutdown函數(shù)15.
pselect函數(shù)16.
poll函數(shù)17.
getsockopt和setsockopt18.
套接口選項(xiàng)列表19.
處理套接口的fcntl函數(shù)20.
gethostbyname函數(shù)21.
gethostbyname2函數(shù)22.
ethostbyaddr函數(shù)23.
uname函數(shù)24.
gethostname函數(shù)25.
getservbyname函數(shù)26.
getservbyport函數(shù)27.
recv和send28.
readv和writev29.
readmsg和writemsg30.
socketpair函數(shù)31.
套接口ioctl函數(shù)
1.字節(jié)序函數(shù)
#include <netinet.h>
uint16_t htons(uint16_t host16bitvalue);
uint32_t htonl(uint32_t host32bitvalue);
返回:網(wǎng)絡(luò)字節(jié)序值
uint16_t ntohs(uint16_t net16bitvalue);
uint32_t ntohl(uint32_t net32bitvalue);
返回:主機(jī)字節(jié)序值
一個(gè)測(cè)試本機(jī)字節(jié)序的程序,可參見(jiàn)見(jiàn)unpv12e:intro/byteorder.c。
2.字節(jié)操作函數(shù)
#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—不相同;進(jìn)行比較操作時(shí),假定兩個(gè)不相等的字節(jié)均為無(wú)符號(hào)字符(unsigned char)。
3.地址轉(zhuǎn)換函數(shù)
#include <arpa/inet.h>
int inet_aton(const char *strptr, struct in_addr *addrptr);
返回:1—串有效,0—串有錯(cuò)。
in_addr_t inet_addr(const char *strptr);
返回:若成功,返回32為二進(jìn)制的網(wǎng)絡(luò)字節(jié)序地址;若有錯(cuò),則返回INADDR_NONE。
char *inet_ntoa(struct in_addr inaddr);
返回:指向點(diǎn)分十進(jìn)制數(shù)串的指針。
int inet_pton(int family, const char *strptr, void *addrptr);
返回:1—成功;0—輸入不是有效的表達(dá)格式,-1—出錯(cuò)。
const char *inet_ntop(int family, const void *addrptr, char *strptr, size_t len);
返回:指向結(jié)果的指針—成功,NULL—失敗。
說(shuō)明:
- inet_aton函數(shù)的指針若為空,則函數(shù)仍然執(zhí)行輸入串的有效性檢查,但不存儲(chǔ)任何結(jié)果。
- inet_addr的缺陷:出錯(cuò)返回值INADDR_NONE等于255.255.255.255(IPv4的有限廣播地址),所以該函數(shù)不能處理此地址。
盡量使用inet_aton,不使用inet_addr。 - inet_ntoa函數(shù)的執(zhí)行結(jié)果放在靜態(tài)內(nèi)存中,是不可重入的。
- 參數(shù)family可以是AF_INET,也可以是AF_INET6,若參數(shù)family不被支持,則出錯(cuò),errno置為EAFNOSUPPORT。
- 指針addrptr是結(jié)構(gòu)指針。
- len指定目標(biāo)的大小,避免緩沖區(qū)溢出。如果len太小,則返回一個(gè)空指針,errno置為ENOSPC。為有助于規(guī)定該大小,有如下定義:
#include <netinet.h>
#define INET_ADDRSTRLEN 16 /*fro IPv4 dotted-decimal */
#define INET6_ADDRSTRLEN 46 /*for IPv6 hex string */ - inet_ntop函數(shù)的參數(shù)strptr不能為空指針,成功時(shí),此指針即是函數(shù)的返回值。
實(shí)現(xiàn)IPv4版本的inet_pton和inet_ntop的程序,參見(jiàn):unpv12e:libfree/inet_pton_ipv4.c和libfree/inet_ntop_ipv4.c。
4.readn、writen和readline
函數(shù)原型如下:
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);
返回:讀寫(xiě)字節(jié)數(shù),-1—出錯(cuò)。
實(shí)現(xiàn)程序見(jiàn):unpv12e:lib/readn.c、lib/writen.c、lib/readline1.c和lib/readline.c。
5.測(cè)試描述符類型
#include <sys/stat.h>
int isfdtype( int fd, int fdtype);
返回:1—是指定類型,0—不是指定類型,-1—出錯(cuò)。
要測(cè)試是否為套接口描述子,fdtype應(yīng)設(shè)為S_IFSOCK。
該函數(shù)的一個(gè)實(shí)現(xiàn)程序,參見(jiàn)unpv12e:lib/isfdtype.c
6.socket函數(shù)
#include <sys/socket.h>
int socket(int family, int type, int protocol);
返回:非負(fù)描述字—成功,-1—出錯(cuò)。
family指定協(xié)議族,有如下取值:
- AF_INET IPv4協(xié)議
- AF_INET6 IPv6協(xié)議
- AF_LOCAL Unix域協(xié)議
- AF_ROUTE 路由套接口
- AF_KEY 密鑰套接口
type指定套接口類型:
- SOCK_STREAM 字節(jié)流套接口
- SOCK_DGRAM 數(shù)據(jù)報(bào)套接口
- SOCK_RAW 原始套接口
protocol一般設(shè)為0,除非用在原始套接口上。
并非所有family和type的組合都是有效的。
AF_LOCAL等于早期的AF_UNIX。
7.connect函數(shù)
#include <sys/socket.h>
int connect(int sockfd, const struct sockaddr *servaddr, socklen_t addrlen);
返回:0—成功,-1—出錯(cuò)。
sockfd是socket函數(shù)返回的套接口描述字,servaddr和addrlen是指向服務(wù)器的套接口地址結(jié)構(gòu)指針和結(jié)構(gòu)大小。
在調(diào)用connect之前不必非得調(diào)用bind函數(shù)。
如果是TCP,則connect激發(fā)TCP的三路握手過(guò)程,在阻塞情況下,只有在連接建立成功或出錯(cuò)時(shí)該函數(shù)才返回,
出錯(cuò)情況:
- 沒(méi)有收到SYN分節(jié)的響應(yīng),在規(guī)定時(shí)間內(nèi)經(jīng)過(guò)重發(fā)仍無(wú)效,則返回ETIMEDOUT;
- 如果對(duì)SYN分節(jié)的響應(yīng)是RST,表示服務(wù)器在指定端口上沒(méi)有相應(yīng)的服務(wù),返回ECONNREFUSED;
- 如果發(fā)出 SYN在中間路由器上引發(fā)一個(gè)目的地不可達(dá)ICMP錯(cuò)誤,在規(guī)定時(shí)間內(nèi)經(jīng)過(guò)重發(fā)仍無(wú)效,則返回EHOSTUNREACH或ENETUNREACH錯(cuò)誤。
注意:如果connect失敗,則套接口將不能再使用,必須關(guān)閉,不能對(duì)此套接口再調(diào)用函數(shù)connect。
8.bind函數(shù)
#include <sys/socket.h>
int bind(int sockfd, const struct sockaddr *maddr, socklen_t addrlen);
返回:0—成功,-1—出錯(cuò)。
進(jìn)程可以把一個(gè)特定的IP地址捆綁到他的套接口上,但此IP地址必須是主機(jī)的一個(gè)接口。
對(duì)于IPv4,通配地址是INADDR_ANY,其值一般為0;使用方法如下:
struct sockaddr_in servaddr;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
對(duì)于IPv6,方法如下:
struct sockaddr_in6 serv;
serv.sin6_addr = in6addr_any; (系統(tǒng)分配變量in6addr_any并將其初始化為常值IN6ADDR_ANY_INIT。)
如果讓內(nèi)核選擇臨時(shí)端口,注意的是bind并不返回所選的斷口值,要得到一個(gè)端口,必須使用getsockname函數(shù)。
bind失敗的常見(jiàn)錯(cuò)誤是EADDRINUSE(地址已使用)。
9.listen函數(shù)
#include <sys/socket.h>
int listen(int sockfd, int backlog);
返回:0—成功,-1—出錯(cuò)。
listen把未連接的套接口轉(zhuǎn)化為被動(dòng)套接口,指示內(nèi)核應(yīng)接受指向此套接口的連接請(qǐng)求。第二個(gè)參數(shù)規(guī)定了內(nèi)核為此套接口排隊(duì)的最大連接數(shù)。
參數(shù)backlog曾經(jīng)規(guī)定為監(jiān)聽(tīng)套接口上的未完成連接隊(duì)列和已完成連接隊(duì)列總和的最大值,但各個(gè)系統(tǒng)的定義方法都不盡相同;歷史上常把backlog置為5,但對(duì)于繁忙的服務(wù)器是不夠的;backlog的設(shè)置沒(méi)有一個(gè)通用的方法,依情況而定,但不要設(shè)為0。
10.accept函數(shù)
#include <sys/socket.h>
int accept(int sockfd, struct sockaddr *cliaddr, socklen_t *addrlen);
返回:非負(fù)描述字—OK,-1—出錯(cuò)。
accept從已完成連接隊(duì)列頭返回下一個(gè)連接,若已完成連接隊(duì)列為空,則進(jìn)程睡眠(套接口為阻塞方式時(shí))。
參數(shù)cliaddr和addrlen返回連接對(duì)方的協(xié)議地址,其中addrlen是值-結(jié)果參數(shù),調(diào)用前addrlen所指的整數(shù)值要置為cliaddr所指的套接口結(jié)構(gòu)的長(zhǎng)度,返回時(shí)由內(nèi)核修改。
accept成功執(zhí)行后,返回一個(gè)連接套接口描述字。
如果對(duì)客戶的協(xié)議地址沒(méi)有興趣,可以把cliaddr和addrlen置為空指針。
11.close函數(shù)
#include <unistd.h>
int close(int sockfd);
返回:0—OK,-1—出錯(cuò)。
TCP套接口的close缺省功能是將套接口做上“已關(guān)閉”標(biāo)記,并立即返回到進(jìn)程。這個(gè)套接口描述字不能再為進(jìn)程使用,但TCP將試著發(fā)送已排隊(duì)待發(fā)的任何數(shù)據(jù),然后按正常的TCP連接終止序列進(jìn)行操作。
close把描述字的訪問(wèn)計(jì)數(shù)減1,當(dāng)訪問(wèn)計(jì)數(shù)仍大于0時(shí),close并不會(huì)引發(fā)TCP的四分組連接終止序列。若確實(shí)要發(fā)一個(gè)FIN,可以用函數(shù)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—出錯(cuò)。
getsockname函數(shù)返回與套接口關(guān)聯(lián)的本地協(xié)議地址。
getpeername函數(shù)返回與套接口關(guān)聯(lián)的遠(yuǎn)程協(xié)議地址。
addrlen是值-結(jié)果參數(shù)。
使用場(chǎng)合:
- 在不調(diào)用bind的TCP客戶,當(dāng)connect成功返回后,getsockname返回分配給此連接的本地IP地址和本地端口號(hào);
- 在以端口號(hào)為0調(diào)用bind后,使用getsockname返回內(nèi)核分配的本地端口號(hào);
- getsockname可用來(lái)獲取某套接口的地址族;
- 在捆綁了通配IP地址的TCP服務(wù)器上,當(dāng)連接建立后,可以使用getsockname獲得分配給此連接的本地IP地址;
- 當(dāng)一個(gè)服務(wù)器調(diào)用exec啟動(dòng)后,他獲得客戶身份的唯一途徑是調(diào)用getpeername函數(shù)。
13.select函數(shù)
#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);
返回:準(zhǔn)備好描述字的正數(shù)目,0—超時(shí),-1—出錯(cuò)。
結(jié)構(gòu)timeval的定義:
struct timeval {
long tv_sec; /* seconds */
long tv_usec; /* microseconds */
};
timeout取值的三種情況:
- 永遠(yuǎn)等下去:僅在有一個(gè)描述字準(zhǔn)備好I/O時(shí)才返回,設(shè)置timeout為空指針;
- 等待固定時(shí)間:在有一個(gè)描述字準(zhǔn)備好I/O時(shí)返回,但不超過(guò)由timeout參數(shù)所指定的秒數(shù)和微秒數(shù);
- 根本不等待:檢查描述字后立即返回,將timeout中的秒數(shù)和微秒數(shù)都設(shè)置為0。
在等待過(guò)程中,若進(jìn)程捕獲了信號(hào)并從信號(hào)處理程序返回,等待一般被中斷,為了可移植性,必須準(zhǔn)備好select返回EINTR錯(cuò)誤。
timeout的值在返回時(shí)并不會(huì)被select修改(const標(biāo)志)。
readset、writeset、exceptset指定我們要讓內(nèi)核測(cè)試讀、寫(xiě)和異常條件所需的描述字。
當(dāng)前支持的異常條件有兩個(gè):
- 套接口帶外數(shù)據(jù)的到達(dá);
- 控制狀態(tài)信息的存在,可從一個(gè)已置為分組方式的偽終端主端讀到。
描述字集的使用:
數(shù)據(jù)類型: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);
參數(shù)maxfdp1指定被測(cè)試的描述字個(gè)數(shù),它的值是要被測(cè)試的最大描述字加1。描述字0,1,2,…,maxfdp1-1都被測(cè)試。
readset、writeset、exceptset是值-結(jié)果參數(shù),select修改三者所指的描述字集。所以,每次調(diào)用select時(shí),我們都要將所有描述字集中關(guān)心的位置為1。
套接口準(zhǔn)備好讀的條件:
- 套接口接收緩沖區(qū)中的數(shù)據(jù)字節(jié)數(shù)大于等于套接口接收緩沖區(qū)低潮限度的當(dāng)前值。對(duì)這樣的套接口的讀操作將不阻塞并返回一個(gè)大于0的值(即準(zhǔn)備好讀入的數(shù)據(jù)量)。可以用套接口選項(xiàng)SO_RCVLOWAT來(lái)設(shè)置低潮限度,對(duì)于TCP和UDP,缺省值為1;
- 連接的讀這一半關(guān)閉(接收了FIN的TCP連接)。對(duì)這樣的套接口讀操作將不阻塞并且返回0(即文件結(jié)束符);
- 套接口是一個(gè)監(jiān)聽(tīng)套接口且已完成的連接數(shù)為非0;
- 有一個(gè)套接口錯(cuò)誤待處理。對(duì)這樣的套接口讀操作將不阻塞且返回一個(gè)錯(cuò)誤,errno設(shè)置成明確的錯(cuò)誤條件。這些待處理錯(cuò)誤也可以通過(guò)指定套接口選項(xiàng)SO_ERROR調(diào)用getsockopt來(lái)取得并清除。
套接口準(zhǔn)備好寫(xiě)的條件:
- 套接口發(fā)送緩沖區(qū)中的可用字節(jié)數(shù)大于等于套接口發(fā)送緩沖區(qū)低潮限度的當(dāng)前值,且或者(1)套接口已連接,或者(2)套接口不要求連接(如UDP套接口)。可以用套接口選項(xiàng)SO_SNDLOWAT來(lái)設(shè)置此低潮限度,對(duì)于TCP和UDP,缺省值為2048;
- 連接的寫(xiě)這一半關(guān)閉。對(duì)這樣的套接口寫(xiě)將產(chǎn)生信號(hào)SIGPIPE;
- 有一個(gè)套接口錯(cuò)誤待處理。對(duì)這樣的套接口寫(xiě)操作將不阻塞且返回一個(gè)錯(cuò)誤,errno設(shè)置成明確的錯(cuò)誤條件。這些待處理錯(cuò)誤也可以通過(guò)指定套接口選項(xiàng)SO_ERROR調(diào)用getsockopt來(lái)取得并清除。
如果一個(gè)套接口存在帶外數(shù)據(jù)或者仍處于帶外標(biāo)記,那它有異常條件待處理。
一個(gè)套接口出錯(cuò)時(shí),它被select標(biāo)記為既可讀又可寫(xiě)。
14.shutdown函數(shù)
#include <sys/socket.h>
int shutdown(int sockfd, int howto);
返回:0—成功,-1—失敗。
函數(shù)的行為依賴于參數(shù)howto的值:
- SHUT_RD:關(guān)閉連接的讀這一半,不再接收套接口中的數(shù)據(jù)且留在套接口緩沖區(qū)中的數(shù)據(jù)都作廢。進(jìn)程不能再對(duì)套接口任何讀函數(shù)。調(diào)用此函數(shù)后,由TCP套接口接收的任何數(shù)據(jù)都被確認(rèn),但數(shù)據(jù)本身被扔掉。
- SHUT_WR:關(guān)閉連接的寫(xiě)這一半,在TCP場(chǎng)合下,這稱為半關(guān)閉。當(dāng)前留在套接口發(fā)送緩沖區(qū)中的數(shù)據(jù)都被發(fā)送,后跟正常的TCP連接終止序列。此半關(guān)閉不管套接口描述字的訪問(wèn)計(jì)數(shù)是否大于0。進(jìn)程不能再執(zhí)行對(duì)套接口的任何寫(xiě)函數(shù)。
SHUT_RDWR:連接的讀這一半和寫(xiě)這一半都關(guān)閉。這等效于調(diào)用shutdown兩次:第一次調(diào)用時(shí)用SHUT_RD,第二次調(diào)用時(shí)用SHUT_WR。
15.pselect函數(shù)
#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);
返回:準(zhǔn)備好描述字的個(gè)數(shù),0—超時(shí),-1—出錯(cuò)。
pselect是Posix.1g發(fā)明的。相對(duì)select的變化:
- pselect使用結(jié)構(gòu)timespec:
struct timespec {
time_t tv_sec; /* seconds */
long tv_nsec; /* nanoseconds */
};
新結(jié)構(gòu)中的tv_nsec規(guī)定納秒數(shù)。
- pselect增加了第六個(gè)參數(shù):指向信號(hào)掩碼的指針。允許程序禁止遞交某些信號(hào)。
16.poll函數(shù)
#include <poll.h>
int poll(struct pollfd *fdarray, unsigned long nfds, int timeout);
返回:準(zhǔn)備好描述字的個(gè)數(shù),0—超時(shí),-1—出錯(cuò)。
第一個(gè)參數(shù)是指向一個(gè)結(jié)構(gòu)數(shù)組的第一個(gè)元素的指針,每個(gè)數(shù)組元素都是一個(gè)pollfd結(jié)構(gòu):
struct pollfd {
int fd; /* descriptor to check */
short events; /* events of interest on fd */
short revents; /* events that occurred on fd */
};
要測(cè)試的條件由成員events規(guī)定,函數(shù)在相應(yīng)的revents成員中返回描述字的狀態(tài)(一個(gè)描述字有兩個(gè)變量:一個(gè)為調(diào)用值,一個(gè)為結(jié)果)。
第二個(gè)參數(shù)指定數(shù)組中元素的個(gè)數(shù)。
第三個(gè)參數(shù)timeout指定函數(shù)返回前等待多長(zhǎng)時(shí)間,單位是毫秒。可能值如下:
- INFTIM,永遠(yuǎn)等待;
- 0,立即返回,不阻塞;
- >0,等待指定數(shù)目的毫秒數(shù)。
標(biāo)志的范圍:
常量 | 能作為events的輸入嗎? | 能作為revents的結(jié)果嗎? | 解釋 |
POLLIN | yes | yes | 普通或優(yōu)先級(jí)帶數(shù)據(jù)可讀 |
POLLRDNORM | yes | yes | 普通數(shù)據(jù)可讀 |
POLLRDBAND | yes | yes | 優(yōu)先級(jí)帶數(shù)據(jù)可讀 |
POLLPRI | yes | yes | 高優(yōu)先級(jí)數(shù)據(jù)可讀 |
POLLOUT | yes | yes | 普通或優(yōu)先級(jí)帶數(shù)據(jù)可寫(xiě) |
POLLWRNORM | yes | yes | 普通數(shù)據(jù)可寫(xiě) |
POLLWRBAND | yes | yes | 優(yōu)先級(jí)帶數(shù)據(jù)可寫(xiě) |
POLLERR |
| yes | 發(fā)生錯(cuò)誤 |
POLLHUP |
| yes | 發(fā)生掛起 |
POLLNVAL |
| yes | 描述字不是一個(gè)打開(kāi)的文件 |
圖可分為三部分:處理輸入的四個(gè)常值;處理輸出的三個(gè)常值;處理錯(cuò)誤的三個(gè)常值。
poll識(shí)別三個(gè)類別的數(shù)據(jù):普通(normal)、優(yōu)先級(jí)帶(priority band)、高優(yōu)先級(jí)(high priority)。術(shù)語(yǔ)來(lái)自流的概念。
返回條件:
- 所有正規(guī)TCP數(shù)據(jù)和UDP數(shù)據(jù)都被認(rèn)為是普通數(shù)據(jù);
- TCP的帶外數(shù)據(jù)被認(rèn)為是優(yōu)先級(jí)帶數(shù)據(jù);
- 當(dāng)TCP連接的讀這一半關(guān)閉時(shí)(如接收了一個(gè)FIN),這也認(rèn)為是普通數(shù)據(jù),且后續(xù)的讀操作將返回0;
- TCP連接存在錯(cuò)誤既可以認(rèn)為是普通數(shù)據(jù),也可以認(rèn)為是錯(cuò)誤(POLLERR)。無(wú)論哪種情況,后續(xù)的讀操作將返回-1,并將errno置為適當(dāng)?shù)闹担@就處理了諸如接收到RST或超時(shí)等條件;
- 在監(jiān)聽(tīng)套接口上新連接的可用性既可認(rèn)為是普通數(shù)據(jù),也可以認(rèn)為是優(yōu)先級(jí)帶數(shù)據(jù),大多數(shù)實(shí)現(xiàn)都將其作為普通數(shù)據(jù)考慮。
- 如果不關(guān)心某個(gè)特定的描述字,可將其pollfd結(jié)構(gòu)的fd成員置為一個(gè)負(fù)值,這樣就可以忽略成員events,且返回時(shí)將成員revents的值置為0。
poll沒(méi)有select存在的最大描述字?jǐn)?shù)目問(wèn)題。但可移植性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—出錯(cuò)。
sockfd必須是一個(gè)打開(kāi)的套接口描述字;level(級(jí)別)指定系統(tǒng)中解釋選項(xiàng)的代碼:普通套接口代碼或特定于協(xié)議的代碼);optval是一個(gè)指向變量的指針;此變量的大小由最后一個(gè)參數(shù)決定。
對(duì)于某些套接口選項(xiàng),什么時(shí)候進(jìn)行設(shè)置或獲取是有差別的。下面的套接口選項(xiàng)是由TCP已連接套接口從監(jiān)聽(tīng)套接口繼承來(lái)的:
- SO_DEBUG;
- SO_DONTROUTE;
- SO_KEEPALIVE;
- SO_LINGER;
- SO_OOBINLINE;
- SO_RCVBUF;
- SO_SNDBUF。
如果想在三路握手完成時(shí)確保這些套接口選項(xiàng)中的某一個(gè)是給已連接套接口設(shè)置的,我們必須先給監(jiān)聽(tīng)套接口設(shè)置此選項(xiàng)。
18.套接口選項(xiàng)列表
level | Optname | get | set | 說(shuō)明 | 標(biāo)志 | 數(shù)據(jù)類型 |
|
|
|
|
|
|
|
SOL_SOCKET | SO_BROADCAST | y | y | 允許發(fā)送廣播數(shù)據(jù)報(bào) | y | int |
| SO_DEBUG | y | y | 使能調(diào)試跟蹤 | y | int |
| SO_DONTROUTE | y | y | 旁路路由表查詢 | y | int |
| SO_ERROR | y |
| 獲取待處理錯(cuò)誤并消除 |
| int |
| SO_KEEPALIVE | y | y | 周期性測(cè)試連接是否存活 | y | int |
| SO_LINGER | y | y | 若有數(shù)據(jù)待發(fā)送則延遲關(guān)閉 |
| linger{} |
| SO_OOBINLINE | y | y | 讓接收到的帶外數(shù)據(jù)繼續(xù)在線存放 | y | int |
| SO_RCVBUF | y | y | 接收緩沖區(qū)大小 |
| int |
| SO_SNDBUF | y | y | 發(fā)送緩沖區(qū)大小 |
| int |
| SO_RCVLOWAT | y | y | 接收緩沖區(qū)低潮限度 |
| int |
| SO_SNDLOWAT | y | y | 發(fā)送緩沖區(qū)低潮限度 |
| int |
| SO_RCVTIMEO | y | y | 接收超時(shí) |
| timeval{} |
| SO_SNDTIMEO | y | y | 發(fā)送超時(shí) |
| timeval{} |
| SO_REUSEADDR | y | y | 允許重用本地地址 | y | int |
| SO_REUSEPORT | y | y | 允許重用本地地址 | y | int |
| SO_TYPE | y |
| 取得套接口類型 |
| int |
| SO_USELOOPBACK | y | y | 路由套接口取得所發(fā)送數(shù)據(jù)的拷貝 | y | int |
|
|
|
|
|
|
|
IPPROTO_IP | IP_HDRINCL | y | y | IP頭部包括數(shù)據(jù) | y | int |
| IP_OPTIONS | y | y | IP頭部選項(xiàng) |
| 見(jiàn)后面說(shuō)明 |
| IP_RECVDSTADDR | y | y | 返回目的IP地址 | y | int |
| IP_RECVIF | y | y | 返回接收到的接口索引 | y | int |
| IP_TOS | y | y | 服務(wù)類型和優(yōu)先權(quán) |
| int |
| IP_TTL | y | y | 存活時(shí)間 |
| 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 | 離開(kāi)多播組 |
| ip_mreq{} |
|
|
|
|
|
|
|
IPPROTO_ICMPV6 | ICMP6_FILTER | y | y | 指定傳遞的ICMPv6消息類型 |
| icmp6_filter{} |
|
|
|
|
|
|
|
IPPROTO_IPV6 | IPV6_ADDRFORM | y | y | 改變套接口的地址結(jié)構(gòu) |
| int |
| IPV6_CHECKSUM | y | y | 原始套接口的校驗(yàn)和字段偏移 |
| int |
| IPV6_DSTOPTS | y | y | 接收目標(biāo)選項(xiàng) | y | int |
| IPV6_HOPLIMIT | y | y | 接收單播跳限 | y | int |
| IPV6_HOPOPTS | y | y | 接收步跳選項(xiàng) | y | int |
| IPV6_NEXTHOP | y | y | 指定下一跳地址 | y | sockaddr{} |
| IPV6_PKTINFO | y | y | 接收分組信息 | y | int |
| IPV6_PKTOPTIONS | y | y | 指定分組選項(xiàng) |
| 見(jiàn)后面說(shuō)明 |
| 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 | 離開(kāi)多播組 |
| ipv6_mreq{} |
|
|
|
|
|
|
|
IPPROTO_TCP | TCP_KEEPALIVE | y | y | 控測(cè)對(duì)方是否存活前連接閑置秒數(shù) |
| int |
| TCP_MAXRT | y | y | TCP最大重傳時(shí)間 |
| int |
| TCP_MAXSEG | y | y | TCP最大分節(jié)大小 |
| int |
| TCP_NODELAY | y | y | 禁止Nagle算法 | y | int |
| TCP_STDURG | y | y | 緊急指針的解釋 | y | int |
詳細(xì)說(shuō)明:
SO_BROADCAST
使能或禁止進(jìn)程發(fā)送廣播消息的能力。只有數(shù)據(jù)報(bào)套接口支持廣播,并且還必須在支持廣播消息的網(wǎng)絡(luò)上(如以太網(wǎng)、令牌環(huán)網(wǎng)等)。
如果目的地址是廣播地址但此選項(xiàng)未設(shè),則返回EACCES錯(cuò)誤。
SO_DEBUG
僅僅TCP支持。當(dāng)打開(kāi)此選項(xiàng)時(shí),內(nèi)核對(duì)TCP在此套接口所發(fā)送和接收的所有分組跟蹤詳細(xì)信息。這些信息保存在內(nèi)核的環(huán)形緩沖區(qū)內(nèi),可由程序trpt進(jìn)行檢查。
SO_DONTROUTE
此選項(xiàng)規(guī)定發(fā)出的分組將旁路底層協(xié)議的正常路由機(jī)制。
該選項(xiàng)經(jīng)常由路由守護(hù)進(jìn)程(routed和gated)用來(lái)旁路路由表(路由表不正確的情況下),強(qiáng)制一個(gè)分組從某個(gè)特定接口發(fā)出。
SO_ERROR
當(dāng)套接口上發(fā)生錯(cuò)誤時(shí),源自Berkeley的內(nèi)核中的協(xié)議模塊將此套接口的名為so_error的變量設(shè)為標(biāo)準(zhǔn)的UNIX Exxx值中的一個(gè),它稱為此套接口的待處理錯(cuò)誤(pending error)。內(nèi)核可立即以以下兩種方式通知進(jìn)程:
- 如果進(jìn)程阻塞于次套接口的select調(diào)用,則無(wú)論是檢查可讀條件還是可寫(xiě)條件,select都返回并設(shè)置其中一個(gè)或所有兩個(gè)條件。
- 如果進(jìn)程使用信號(hào)驅(qū)動(dòng)I/O模型,則給進(jìn)程或進(jìn)程組生成信號(hào)SIGIO。
進(jìn)程然后可以通過(guò)獲取SO_ERROR套接口選項(xiàng)來(lái)得到so_error的值。由getsockopt返回的整數(shù)值就是此套接口的待處理錯(cuò)誤。so_error隨后由內(nèi)核復(fù)位為0。
當(dāng)進(jìn)程調(diào)用read且沒(méi)有數(shù)據(jù)返回時(shí),如果so_error為非0值,則read返回-1且errno設(shè)為so_error的值,接著so_error的值被復(fù)位為0。如果此套接口上有數(shù)據(jù)在排隊(duì),則read返回那些數(shù)據(jù)而不是返回錯(cuò)誤條件。
如果進(jìn)程調(diào)用write時(shí)so_error為非0值,則write返回-1且errno設(shè)為so_error的值,隨后so_error也被復(fù)位。
SO_KEEPALIVE
打開(kāi)此選項(xiàng)后,如果2小時(shí)內(nèi)在此套接口上沒(méi)有任何數(shù)據(jù)交換,TCP就會(huì)自動(dòng)給對(duì)方發(fā)一個(gè)保持存活探測(cè)分節(jié),結(jié)果如下:
- 對(duì)方以期望的ACK響應(yīng),則一切正常,應(yīng)用程序得不到通知;
- 對(duì)方以RST響應(yīng),套接口的待處理錯(cuò)誤被置為ECONNRESET,套接口本身則被關(guān)閉;
- 對(duì)方對(duì)探測(cè)分節(jié)無(wú)任何響應(yīng),經(jīng)過(guò)重試都沒(méi)有任何響應(yīng),套接口的待處理錯(cuò)誤被置為ETIMEOUT,套接口本身被關(guān)閉;若接收到一個(gè)ICMP錯(cuò)誤作為某個(gè)探測(cè)分節(jié)的響應(yīng),則返回相應(yīng)錯(cuò)誤。
此選項(xiàng)一般由服務(wù)器使用。服務(wù)器使用它是為了檢測(cè)出半開(kāi)連接并終止他們。
SO_LINGER
此選項(xiàng)指定函數(shù)close對(duì)面向連接的協(xié)議如何操作(如TCP)。缺省close操作是立即返回,如果有數(shù)據(jù)殘留在套接口緩沖區(qū)中則系統(tǒng)將試著將這些數(shù)據(jù)發(fā)送給對(duì)方。
SO_LINGER選項(xiàng)用來(lái)改變此缺省設(shè)置。使用如下結(jié)構(gòu):
struct linger {
int l_onoff; /* 0 = off, nozero = on */
int l_linger; /* linger time */
};
有下列三種情況:
- l_onoff為0,則該選項(xiàng)關(guān)閉,l_linger的值被忽略,等于缺省情況,close立即返回;
- l_onoff為非0,l_linger為0,則套接口關(guān)閉時(shí)TCP夭折連接,TCP將丟棄保留在套接口發(fā)送緩沖區(qū)中的任何數(shù)據(jù)并發(fā)送一個(gè)RST給對(duì)方,而不是通常的四分組終止序列,這避免了TIME_WAIT狀態(tài);
- l_onoff 為非0,l_linger為非0,當(dāng)套接口關(guān)閉時(shí)內(nèi)核將拖延一段時(shí)間(由l_linger決定)。如果套接口緩沖區(qū)中仍殘留數(shù)據(jù),進(jìn)程將處于睡眠狀態(tài),直 到(a)所有數(shù)據(jù)發(fā)送完且被對(duì)方確認(rèn),之后進(jìn)行正常的終止序列(描述字訪問(wèn)計(jì)數(shù)為0)或(b)延遲時(shí)間到。此種情況下,應(yīng)用程序檢查close的返回值是 非常重要的,如果在數(shù)據(jù)發(fā)送完并被確認(rèn)前時(shí)間到,close將返回EWOULDBLOCK錯(cuò)誤且套接口發(fā)送緩沖區(qū)中的任何數(shù)據(jù)都丟失。close的成功返 回僅告訴我們發(fā)送的數(shù)據(jù)(和FIN)已由對(duì)方TCP確認(rèn),它并不能告訴我們對(duì)方應(yīng)用進(jìn)程是否已讀了數(shù)據(jù)。如果套接口設(shè)為非阻塞的,它將不等待close完 成。
l_linger的單位依賴于實(shí)現(xiàn),4.4BSD假設(shè)其單位是時(shí)鐘滴答(百分之一秒),但Posix.1g規(guī)定單位為秒。
讓客戶知道服務(wù)器已經(jīng)讀其數(shù)據(jù)的一個(gè)方法時(shí):調(diào)用shutdown(SHUT_WR)而不是調(diào)用close,并等待對(duì)方close連接的本地(服務(wù)器)端。
SO_OOBINLINE
此選項(xiàng)打開(kāi)時(shí),帶外數(shù)據(jù)將被保留在正常的輸入隊(duì)列中(即在線存放)。當(dāng)發(fā)生這種情況時(shí),接收函數(shù)的MSG_OOB標(biāo)志不能用來(lái)讀帶外數(shù)據(jù)。
SO_RCVBUF和SO_SNDBUF
每個(gè)套接口都有一個(gè)發(fā)送緩沖區(qū)和一個(gè)接收緩沖區(qū),使用這兩個(gè)套接口選項(xiàng)可以改變?nèi)笔【彌_區(qū)大小。
當(dāng)設(shè)置TCP套接口接收緩沖區(qū)的大小時(shí),函數(shù)調(diào)用順序是很重要的,因?yàn)門(mén)CP的窗口規(guī)模選項(xiàng)是在建立連接時(shí)用SYN與對(duì)方互換得到的。對(duì)于客戶,SO_RCVBUF選項(xiàng)必須在connect之前設(shè)置;對(duì)于服務(wù)器,SO_RCVBUF選項(xiàng)必須在listen前設(shè)置。
TCP套接口緩沖區(qū)的大小至少是連接的MSS的三倍,而必須是連接的MSS的偶數(shù)倍。
SO_RCVLOWAT和SO_SNDLOWAT
每個(gè)套接口有一個(gè)接收低潮限度和一個(gè)發(fā)送低潮限度,他們由函數(shù)select使用。這兩個(gè)選項(xiàng)可以修改他們。
接收低潮限度是讓select返回“可讀”而在套接口接收緩沖區(qū)中必須有的數(shù)據(jù)量,對(duì)于一個(gè)TCP或UDP套接口,此值缺省為1。發(fā)送低潮限度是讓select返回“可寫(xiě)”而在套接口發(fā)送緩沖區(qū)中必須有的可用空間,對(duì)于TCP套接口,此值常為2048。
SO_RCVTIMEO和SO_SNDTIMEO
使用這兩個(gè)選項(xiàng)可以給套接口設(shè)置一個(gè)接收和發(fā)送超時(shí)。通過(guò)設(shè)置參數(shù)的值為0秒和0微秒來(lái)禁止超時(shí)。缺省時(shí)兩個(gè)超時(shí)都是禁止的。
接收超時(shí)影響5個(gè)輸入函數(shù):read、readv、recv、recvfrom和recvmsg;發(fā)送超時(shí)影響5個(gè)輸出函數(shù):write、writev、send、sendto和sendmsg。
SO_REUSEADDR和SO_REUSEPORT
SO_REUSEADDR提供如下四個(gè)功能:
- SO_REUSEADDR允許啟動(dòng)一個(gè)監(jiān)聽(tīng)服務(wù)器并捆綁其眾所周知端口,即使以前建立的將此端口用做他們的本地端口的連接仍存在。這通常是重啟監(jiān)聽(tīng)服務(wù)器時(shí)出現(xiàn),若不設(shè)置此選項(xiàng),則bind時(shí)將出錯(cuò)。
- SO_REUSEADDR允許在同一端口上啟動(dòng)同一服務(wù)器的多個(gè)實(shí)例,只要每個(gè)實(shí)例捆綁一個(gè)不同的本地IP地址即可。對(duì)于TCP,我們根本不可能啟動(dòng)捆綁相同IP地址和相同端口號(hào)的多個(gè)服務(wù)器。
- SO_REUSEADDR允許單個(gè)進(jìn)程捆綁同一端口到多個(gè)套接口上,只要每個(gè)捆綁指定不同的本地IP地址即可。這一般不用于TCP服務(wù)器。
- SO_REUSEADDR允許完全重復(fù)的捆綁:當(dāng)一個(gè)IP地址和端口綁定到某個(gè)套接口上時(shí),還允許此IP地址和端口捆綁到另一個(gè)套接口上。一般來(lái)說(shuō),這個(gè)特性僅在支持多播的系統(tǒng)上才有,而且只對(duì)UDP套接口而言(TCP不支持多播)。
SO_REUSEPORT選項(xiàng)有如下語(yǔ)義:
- 此選項(xiàng)允許完全重復(fù)捆綁,但僅在想捆綁相同IP地址和端口的套接口都指定了此套接口選項(xiàng)才性。
- 如果被捆綁的IP地址是一個(gè)多播地址,則SO_REUSEADDR和SO_REUSEPORT等效。
使用這兩個(gè)套接口選項(xiàng)的建議:
- 在所有TCP服務(wù)器中,在調(diào)用bind之前設(shè)置SO_REUSEADDR套接口選項(xiàng);
- 當(dāng)編寫(xiě)一個(gè)同一時(shí)刻在同一主機(jī)上可運(yùn)行多次的多播應(yīng)用程序時(shí),設(shè)置SO_REUSEADDR選項(xiàng),并將本組的多播地址作為本地IP地址捆綁。
SO_TYPE
該選項(xiàng)返回套接口的類型,返回的整數(shù)值是一個(gè)諸如SOCK_STREAM或SOCK_DGRAM這樣的值。
SO_USELOOPBACK
該選項(xiàng)僅用于路由域(AF_ROUTE)的套接口,它對(duì)這些套接口的缺省設(shè)置為打開(kāi)(這是唯一一個(gè)缺省為打開(kāi)而不是關(guān)閉的SO_xxx套接口選項(xiàng))。當(dāng)此套接口打開(kāi)時(shí),套接口接收在其上發(fā)送的任何數(shù)據(jù)的一個(gè)拷貝。
禁止這些回饋拷貝的另一個(gè)方法是shutdown,第二個(gè)參數(shù)應(yīng)設(shè)為SHUT_RD。
IP_HDRINCL
如果一個(gè)原始套接口設(shè)置該選項(xiàng),則我們必須為所有發(fā)送到此原始套接口上的數(shù)據(jù)報(bào)構(gòu)造自己的IP頭部。
IP_OPTIONS
設(shè)置此選項(xiàng)允許我們?cè)贗Pv4頭部中設(shè)置IP選項(xiàng)。這要求掌握IP頭部中IP選項(xiàng)的格式信息。
IP_RECVDSTADDR
該選項(xiàng)導(dǎo)致所接收到的UDP數(shù)據(jù)報(bào)的目的IP地址由函數(shù)recvmsg作為輔助數(shù)據(jù)返回。
IP_RECVIF
該選項(xiàng)導(dǎo)致所接收到的UDP數(shù)據(jù)報(bào)的接口索引由函數(shù)recvmsg作為輔助數(shù)據(jù)返回。
IP_TOS
該選項(xiàng)使我們可以給TCP或UDP套接口在IP頭部中設(shè)置服務(wù)類型字段。如果我們給此選項(xiàng)調(diào)用getsockopt,則放到外出IP數(shù)據(jù)報(bào)頭部的TOS字段中的當(dāng)前值將返回(缺省為0)。還沒(méi)有辦法從接收到的IP數(shù)據(jù)報(bào)中取此值。
可以將TOS設(shè)置為如下的值:
- IPTOS_LOWDELAY:最小化延遲
- IPTOS_THROUGHPUT:最大化吞吐量
- IPTOS_RELIABILITY:最大化可靠性
- IPTOS_LOWCOST:最小化成本
IP_TTL
用次選項(xiàng),可以設(shè)置和獲取系統(tǒng)用于某個(gè)給定套接口的缺省TTL值(存活時(shí)間字段)。與TOS一樣,沒(méi)有辦法從接收到的數(shù)據(jù)報(bào)中得到此值。
ICMP6_FILTER
可獲取和設(shè)置一個(gè)icmp6_filter結(jié)構(gòu),他指明256個(gè)可能的ICMPv6消息類型中哪一個(gè)傳遞給在原始套接口上的進(jìn)程。
IPV6_ADDRFORM
允許套接口從IPv4轉(zhuǎn)換到IPv6,反之亦可。
IPV6_CHECKSUM
指定用戶數(shù)據(jù)中校驗(yàn)和所處位置的字節(jié)偏移。如果此值為非負(fù),則內(nèi)核將(1)給所有外出分組計(jì)算并存儲(chǔ)校驗(yàn)和;(2)輸入時(shí)檢查所收到的分組的校驗(yàn)和,丟棄 帶有無(wú)效校驗(yàn)和的分組。此選項(xiàng)影響出ICMPv6原始套接口外的所有IPv6套接口。如果指定的值為-1(缺省值),內(nèi)核在此原始套接口上將不給外出的分 組計(jì)算并存儲(chǔ)校驗(yàn)和,也不檢查所收到的分組的校驗(yàn)和。
IPV6_DSTOPTS
設(shè)置此選項(xiàng)指明:任何接收到的IPv6目標(biāo)選項(xiàng)都將由recvmsg作為輔助數(shù)據(jù)返回。此選項(xiàng)缺省為關(guān)閉。
IPV6_HOPLIMIT
設(shè)置此選項(xiàng)指明:接收到的跳限字段將由recvmsg作為輔助數(shù)據(jù)返回。
IPV6_HOPOPTS
設(shè)置此選項(xiàng)指明:任何接收到的步跳選項(xiàng)都將由recvmsg作為輔助數(shù)據(jù)返回。
IPV6_NEXTHOP
這不是一個(gè)套接口選項(xiàng),而是一個(gè)可指定個(gè)sendmsg的輔助數(shù)據(jù)對(duì)象的類型。此對(duì)象以一個(gè)套接口地址結(jié)構(gòu)指定某個(gè)數(shù)據(jù)報(bào)的下一跳地址。
IPV6_PKTINFO
設(shè)置此選項(xiàng)指明:下面關(guān)于接收到的IPv6數(shù)據(jù)報(bào)的兩條信息將由recvmsg作為輔助數(shù)據(jù)返回:目的IPv6地址和到達(dá)接口索引。
IPV6_PKTOPTIONS
大多數(shù)IPv6套接口選項(xiàng)假設(shè)UDP套接口使用recvmsg和sendmsg所用的輔助數(shù)據(jù)在內(nèi)核與應(yīng)用進(jìn)程間傳遞信息。TCP套接口使用IPV6_PKTOPTIONS來(lái)獲取和存儲(chǔ)這些值。
IPV6_RTHDR
設(shè)置此選項(xiàng)指明:接收到的IPv6路由頭部將由recvmsg作為輔助數(shù)據(jù)返回。
IPV6_UNICAST_HOPS
類似于IPv4的IP_TTL,它的設(shè)置指定發(fā)送到套接口上的外出數(shù)據(jù)報(bào)的缺省跳限,而它的獲取則返回內(nèi)核將用于套接口的跳限值。為了從接收到的IPv6數(shù)據(jù)報(bào)中得到真實(shí)的跳限字段,要求使用IPV6_HOPLIMIT套接口選項(xiàng)。
TCP_KEEPALIVE
它指定TCP開(kāi)始發(fā)送保持存活探測(cè)分節(jié)前以秒為單位的連接空閑時(shí)間。缺省值至少為7200秒,即2小時(shí)。該選項(xiàng)僅在SO_KEEPALIVE套接口選項(xiàng)打開(kāi)時(shí)才有效。
TCP_MAXRT
它指定一旦TCP開(kāi)始重傳數(shù)據(jù),在連接斷開(kāi)之前需經(jīng)歷的以秒為單位的時(shí)間總量。值0意味著使用系統(tǒng)缺省值,值-1意味著永遠(yuǎn)重傳數(shù)據(jù)。
TCP_MAXSEG
允許獲取或設(shè)置TCP連接的最大分節(jié)大小(MSS)。返回值是我們的TCP發(fā)送給另一端的最大數(shù)據(jù)量,他常常就是由另一端用SYN分節(jié)通告的MSS,除非 我們的TCP選擇使用一個(gè)比對(duì)方通告的MSS小的值。如果此選項(xiàng)在套接口連接之前取得,則返回值為未從另一端收到的MSS選項(xiàng)的情況下所用的缺省值。
TCP_NODELAY
如果設(shè)置,此選項(xiàng)禁止TCP的Nagle算法。缺省時(shí),該算法是使能的。
Nagle算法的目的是減少WAN上小分組的數(shù)目。
Nagle算法常常與另一個(gè)TCP算法聯(lián)合使用:延遲ACK(delayed ACK)算法。
解決多次寫(xiě)導(dǎo)致Nagle算法和延遲ACK算法負(fù)面影響的方法:
- 使用writev而不是多次write;
- 合并緩沖區(qū),對(duì)此緩沖區(qū)使用一次write;
- 設(shè)置TCP_NODELAY選項(xiàng),繼續(xù)調(diào)用write多次,這是最不可取的解決方法。
TCP_STDURG
它影響對(duì)TCP緊急指針的解釋。
19.處理套接口的fcntl函數(shù)
#include <fcntl.h>
int fcntl(int fd, int cmd, … /* arg */);
返回:依賴于參數(shù)cmd—成功,-1—失敗。
函數(shù)fcntl提供了如下關(guān)于網(wǎng)絡(luò)編程的特性:
- 非阻塞I/O:通過(guò)用F_SETFL命令設(shè)置O_NONBLOCK文件狀態(tài)標(biāo)志來(lái)設(shè)置套接口為非阻塞型。
- 信號(hào)驅(qū)動(dòng)I/O:用F_SETFL命令來(lái)設(shè)置O_ASYNC文件狀態(tài)標(biāo)志,這導(dǎo)致在套接口狀態(tài)發(fā)生變化時(shí)內(nèi)核生成信號(hào)SIGIO。
- F_SETOWN命令設(shè)置套接口屬主(進(jìn)程ID或進(jìn)程組ID),由它來(lái)接收信號(hào)SIGIO和SIGURG。SIGIO在設(shè)置套接口為信號(hào)驅(qū)動(dòng)I/O型時(shí)生成,SIGURG在新的帶外數(shù)據(jù)到達(dá)套接口時(shí)生成。
- F_GETOWN命令返回套接口的當(dāng)前屬主。
注意事項(xiàng):
- 設(shè)置某個(gè)文件狀態(tài)標(biāo)志時(shí),先取得當(dāng)前標(biāo)志,與新標(biāo)志路邏輯或后再設(shè)置標(biāo)志。
- 信號(hào)SIGIO和SIGURG與其他信號(hào)不同之處在于,這兩個(gè)信號(hào)只有在已使用命令F_SETOWN給套接口指派了屬主后才會(huì)生成。F_SETOWN命令的整參數(shù)arg既可以是一個(gè)正整數(shù),指明接收信號(hào)的進(jìn)程ID,也可以是一個(gè)負(fù)整數(shù),它的絕對(duì)值是接收信號(hào)的進(jìn)程組ID。
- 當(dāng)一個(gè)新的套接口由函數(shù)socket創(chuàng)建時(shí),他沒(méi)有屬主,但是當(dāng)一個(gè)新的套接口從一個(gè)監(jiān)聽(tīng)套接口創(chuàng)建時(shí),套接口屬主便由已連接套接口從監(jiān)聽(tīng)套接口繼承而來(lái)。
20.gethostbyname函數(shù)
#include <netdb.h>
struct hostent *gethostbyname(const char *hostname);
返回:非空指針—成功,空指針—出錯(cuò),同時(shí)設(shè)置h_errno。
函數(shù)返回的非空指針指向的結(jié)構(gòu)如下:
struct hostent {
char *h_name; /*規(guī)范主機(jī)名 */
char **h_aliases; /* 別名列表 */
int h_addrtype; /* AF_INET or AF_INET6 */
int h_length; /* 地址長(zhǎng)度 */
char **h_addr_list; /* IPv4或IPv6地址結(jié)構(gòu)列表 */
};
#define h_addr h_addr_list[0];
按照DNS的說(shuō)法,gethostbyname執(zhí)行一個(gè)對(duì)A記錄的查詢或?qū)AAA記錄的查詢,返回IPv4或IPv6地址。
h_addr的定義是為了兼容,在新代碼中不應(yīng)使用。
返回的h_name稱為主機(jī)的規(guī)范(canonical)名字。當(dāng)返回IPv6地址時(shí),h_addrtype被設(shè)置為AF_INET6,成員h_length被設(shè)置為16。
gethostbyname的特殊之處在于:當(dāng)發(fā)生錯(cuò)誤時(shí),他不設(shè)置errno,而是將全局整數(shù)h_errno設(shè)置為定義在頭文件<netdb.h>中的下列常值中的一個(gè):
- HOST_NOT_FOUND;
- TRY_AGAIN;
- NO_RECOVERY;
- NO_DATA(等同于NO_ADDRESS)。
有函數(shù)hstrerror(),它將h_errno的值作為唯一的參數(shù),返回一個(gè)指向相應(yīng)錯(cuò)誤說(shuō)明的const char *型指針。
DNS小常識(shí):
DNS中的條目稱為資源記錄RR(resource record),僅有少數(shù)幾類RR會(huì)影響我們的名字與地址轉(zhuǎn)換:
- A:A記錄將主機(jī)名映射為32位的IPv4地址;
- AAAA:“四A”記錄將主機(jī)名映射為128位的IPv6地址;
- PTR:PTR記錄(稱為“指針記錄”)將IP地址映射為主機(jī)名;
- MX:MX記錄指定一主機(jī)作為某主機(jī)的“郵件交換器”。
- CNAME:CNAME代表“canonical name(規(guī)范名字)”,其常見(jiàn)的用法是為常用服務(wù)如ftp和www指派一個(gè)CNAME記錄。
21.gethostbyname2函數(shù)
#include <netdb.h>
struct hostent *gethostbyname2(const char *hostname, int family);
返回:非空指針—成功,空指針—出錯(cuò),同時(shí)設(shè)置h_errno。
該函數(shù)允許指定地址族,其他與gethostbyname相似。
22.gethostbyaddr函數(shù)
#include <netdb.h>
struct hostent *gethostbyaddr(const char *addr, size_t len, int family);
返回:非空指針—成功,空指針—出錯(cuò),同時(shí)設(shè)置h_error。
函數(shù)根據(jù)一個(gè)二進(jìn)制的IP地址并試圖找出相應(yīng)于此地址的主機(jī)名,我們關(guān)心的是規(guī)范主機(jī)名h_name。
參數(shù)addr不是char *類型,而是一個(gè)真正指向含有IPv4或IPv6地址的結(jié)構(gòu)in_addr或in6_addr的指針;len是該結(jié)構(gòu)的大小,對(duì)于IPv4是4,對(duì)于IPv6是16;family或?yàn)锳F_INET或?yàn)锳F_INET6。
按照DNS的說(shuō)法,該函數(shù)查詢PTR記錄。
23.uname函數(shù)
#include <sys/utsname.h>
int uname(struct utsname *name);
返回:非負(fù)值—成功,-1—失敗。
返回當(dāng)前主機(jī)的名字,存放在如下的結(jié)構(gòu)里:
#define UTS_NAMESIZE 16
#define UTS_NODESIZE 256
struct utsname {
char sysname[UTS_NAMESIZE];
char nodename[UTS_NODESIZE];
char release[UTS_NAMESIZE];
char version[UTS_NAMESIZE];
char machine[UTS_NAMESIZE];
};
該函數(shù)經(jīng)常與gethostbyname一起用來(lái)確定本機(jī)的IP地址:先調(diào)用uname獲得主機(jī)名字,然后調(diào)用gethostbyname得到所有的IP地址。
獲得本機(jī)IP地址的另一個(gè)方法是ioctl的命令SIOCGIFCONF。
24.gethostname函數(shù)
#include <unistd.h>
int gethostname(char *name, size_t namelen);
返回:0—成功,-1—失敗。
返回當(dāng)前主機(jī)的名字。name是指向主機(jī)名存儲(chǔ)位置的指針,namelen是此數(shù)組的大小,如果有空間,主機(jī)名以空字符結(jié)束。
主機(jī)名的最大大小通常是頭文件<sys/param.h>定義的常值MAXHOSTNAMELEN。
25.getservbyname函數(shù)
#include <netdb.h>
struct servent *getservbyname(const char *servname, const char *protoname);
返回:非空指針—成功,空指針—失敗。
函數(shù)返回如下結(jié)構(gòu)的指針:
struct servent {
char *s_name;
char **s_aliases;
int s_port;
char *s_proto;
};
服務(wù)名servname必須指定,如果還指定了協(xié)議(protoname為非空指針),則結(jié)果表項(xiàng)必須有匹配的記錄。如果沒(méi)有指定協(xié)議名而服務(wù)支持多個(gè)協(xié)議,則返回哪個(gè)端口是依賴于實(shí)現(xiàn)的。
結(jié)構(gòu)中的端口號(hào)是以網(wǎng)絡(luò)字節(jié)序返回的,所以在將它存儲(chǔ)在套接口地址結(jié)構(gòu)時(shí),絕對(duì)不能調(diào)用htons。
26.getservbyport函數(shù)
#include <netdb.h>
struct servent *getservbyport(int port, const char *protname);
返回:非空指針—成功,空指針—出錯(cuò)。
port必須為網(wǎng)絡(luò)字節(jié)序。例如:
sptr = getservbyport(htons(53), “udp”);
27.recv和send
#include <sys/socket.h>
ssize_t recv(int sockfd, void *buf, size_t nbytes, int flags);
ssize_t send(int sockfd, void *buf, size_t nbytes, int flags);
返回:成功返回讀入或?qū)懗龅淖止?jié)數(shù),出錯(cuò)返回-1。
前三個(gè)參數(shù)與read和write相同,參數(shù)flags的值或?yàn)?,或由以下的一個(gè)或多個(gè)常值邏輯或構(gòu)成:
flags | 描述 | recv | send |
|
|
|
|
MSG_DONTROUTE | 不查路由表 |
| y |
MSG_DONTWAIT | 本操作不阻塞 | y | y |
MSG_OOB | 發(fā)送或接收帶外數(shù)據(jù) | y | y |
MSG_PEEK | 查看外來(lái)的消息 | y |
|
MSG_WAITALL | 等待所有數(shù)據(jù) | y |
|
下面說(shuō)明每個(gè)標(biāo)志的作用:
- MSG_DONTROUTE:這個(gè)標(biāo)志告訴內(nèi)核目的主機(jī)在直接連接的本地網(wǎng)絡(luò)上,不要查路由表。這是對(duì)提供這種特性的SO_DONTROUTE套接口選項(xiàng)的補(bǔ)充。該標(biāo)志可以對(duì)單個(gè)輸出操作提供這種特性,而套接口選項(xiàng)則針對(duì)某個(gè)套接口上的所有輸出操作。
- MSG_DONTWAIT:這個(gè)標(biāo)志將單個(gè)I/O操作設(shè)為非阻塞方式,而不需要在套接口上打開(kāi)非阻塞標(biāo)志,執(zhí)行I/O操作,然后關(guān)閉阻塞標(biāo)志。
- MSG_OOB:用send時(shí),這個(gè)標(biāo)志指明發(fā)送的是帶外數(shù)據(jù),用recv時(shí),該標(biāo)志指明要讀的是帶外數(shù)據(jù)而不是一般數(shù)據(jù)。
- MSG_PEEK:這個(gè)標(biāo)志可以讓我們查看可讀的數(shù)據(jù),在recv或recvfrom后系統(tǒng)不會(huì)將這些數(shù)據(jù)丟棄。
- MSG_WAITALL: 由4.3BSD Reno引入,他告訴內(nèi)核在沒(méi)有讀到請(qǐng)求的字節(jié)數(shù)之前不使讀操作返回。如果系統(tǒng)支持這個(gè)標(biāo)志,則可以去掉readn函數(shù)。即使設(shè)定了該標(biāo)志 ,如果發(fā)生如下情況:(1)捕獲了一個(gè)信號(hào);(2)連接被終止;(3)在套接口上發(fā)生錯(cuò)誤,這個(gè)函數(shù)返回的字節(jié)數(shù)仍會(huì)比請(qǐng)求的少。
28.readv和writev
#include <sys/uio.h>
ssize_t readv(int filedes, const struct iovec *iov, int iovcnt);
ssize_t writev(int filedes, const struct iovec *iov, int iovcnt);
返回:讀到或?qū)懗龅淖止?jié)數(shù),出錯(cuò)返回-1。
readv和writev可以讓我們?cè)谝粋€(gè)函數(shù)調(diào)用中讀或?qū)懚鄠€(gè)緩沖區(qū),這些操作被稱為分散讀和集中寫(xiě)。
iovec結(jié)構(gòu)定義如下:
struct iovec {
void *iov_base; /* starting address of buffer */
size_t iov_len; /* size of buffer */
};
在具體的實(shí)現(xiàn)中對(duì)iovec結(jié)構(gòu)數(shù)組的元素個(gè)數(shù)有限制,4.3BSD最多允許1024個(gè),而Solaris2.5上限是16。Posix.1g要求定義一個(gè)常值IOV_MAX,而且它的值不小于16。
readv和writev可用于任何描述字。writev是一個(gè)原子操作,可以避免多次寫(xiě)引發(fā)的Nagle算法。
29.readmsg和writemsg
#include <sys/socket.h>
ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags);
ssize_t sendmsg(int sockfd, struct msghdr *msg, int flags);
返回:成功時(shí)為讀入或?qū)懗龅淖止?jié)數(shù),出錯(cuò)時(shí)為-1。
這兩個(gè)函數(shù)是最通用的套接口I/O函數(shù),可以用recvmsg代替read、readv、recv和recvfrom,同樣,各種輸出函數(shù)都可以用sendmsg代替。
參數(shù)msghdr結(jié)構(gòu)的定義如下:
struct msghdr {
void *msg_name; /* protocol address */
socklen_t msg_namelen; /* size of protocol address */
struct iovec *msg_iov; /* scatter/gather array */
size_t msg_iovlen; /* elements in msg_iov */
void *msg_control; /* ancillary data; must be aligned for a cmsghdr structure */
socklen_t msg_controllen; /* length of ancillary data */
int msg_flags; /* flags returned by recvmsg() */
};
該結(jié)構(gòu)源自4.3BSD Reno,也是Posix.1g中所說(shuō)明的,有些系統(tǒng)仍使用一種老的msghdr結(jié)構(gòu),此種結(jié)構(gòu)中沒(méi)有msg_flags成員,而且 msg_control和msg_controllen成員分別被叫做msg_accrights和msg_accrightslen。老系統(tǒng)中支持的唯 一一種輔助數(shù)據(jù)形式是文件描述字(稱為訪問(wèn)權(quán)限)的傳遞。
msg_name和msg_namelen成員用于未經(jīng)連接的套接口,他們與 recvfrom和sendto的第五和第六個(gè)參數(shù)類似:msg_name指向一個(gè)套接口地址結(jié)構(gòu),如果不需要指明協(xié)議地址,msg_name應(yīng)被設(shè)置為 空指針,msg_namelen對(duì)sendmsg是一個(gè)值,而對(duì)recvmsg是一個(gè)值-結(jié)果參數(shù)。
msg_iov和msg_iovlen成員指明輸入或輸出的緩沖區(qū)數(shù)組。
msg_control和msg_controllen指明可選的輔助數(shù)據(jù)的位置和大小,msg_controllen對(duì)recvmsg是一個(gè)值-結(jié)果參數(shù)。
msg_flags只用于revmsg,調(diào)用recvmsg時(shí),flags參數(shù)被拷貝 到msg_flags成員,而且內(nèi)核用這個(gè)值進(jìn)行接收處理,接著它的值會(huì)根據(jù)recvmsg的結(jié)果而更新,sendmsg會(huì)忽略msg_flags成員, 因?yàn)樗谶M(jìn)行輸出處理時(shí)使用flags參數(shù)。
內(nèi)核檢查的flags和返回的msg_flags如下表所示:
標(biāo)志 | 在send flags、 sendto flags、 sendmsg flags中檢查 | 在recv flags、 recvfrom flags、 recvmsg flags中檢查 | 在recvmsg msg_flags 中返回 |
|
|
|
|
MSG_DONTROUTE | y |
|
|
MSG_DONTWAIT | y | y |
|
MSG_PEEK |
| y |
|
MSG_WAITALL |
| y |
|
MSG_EOR | y |
| y |
MSG_OOB | y | y | y |
MSG_BCAST |
|
| y |
MSG_MCAST |
|
| y |
MSG_TRUNC |
|
| y |
MSG_CTRUNC |
|
| y |
前四個(gè)標(biāo)志只檢查不返回,下兩個(gè)標(biāo)志既檢查又返回,最后四個(gè)只返回。返回的六個(gè)標(biāo)志含義如下:
- MSG_BCAST:當(dāng)收到的數(shù)據(jù)報(bào)是一個(gè)鏈路層的廣播或其目的IP地址為廣播地址時(shí),將返回此標(biāo)志。
- MSG_MCAST:當(dāng)收到的數(shù)據(jù)報(bào)是鏈路層的多播時(shí),將返回該標(biāo)志。
- MSG_TRUNC:這個(gè)標(biāo)志在數(shù)據(jù)報(bào)被截?cái)鄷r(shí)返回。
- MSG_CTRUNC:這個(gè)標(biāo)志在輔助數(shù)據(jù)被截?cái)鄷r(shí)返回。
- MSG_EOR:如果返回的數(shù)據(jù)不是一個(gè)邏輯記錄的結(jié)尾,該標(biāo)志被清位,反之則置位。TCP不使用這個(gè)標(biāo)志,因?yàn)樗且环N字節(jié)流協(xié)議。
- MSG_OOB:這個(gè)標(biāo)志不是為T(mén)CP的帶外數(shù)據(jù)返回的,它用于其他協(xié)議族(譬如OSI協(xié)議等)。
具體的實(shí)現(xiàn)可能會(huì)在msg_flags中返回一些輸入的flags的標(biāo)志,所以我們應(yīng)該只檢查那些感興趣的標(biāo)志的值。
30.socketpair函數(shù)
#include <sys/socket.h>
int socketpair(int family, int type, int protocol, int sockfd[2]);
返回:成功返回0,出錯(cuò)返回-1。
family必須為AF_LOCAL,protocol必須為0,type可以是SOCK_STREAM或SOCK_DGRAM。新創(chuàng)建的兩個(gè)套接口描述字作為sockfd[0]和sockfd[1]返回。
這兩個(gè)描述字相互連接,沒(méi)有名字,即沒(méi)有涉及隱式bind。
以SOCK_STREAM作為type調(diào)用所得到的結(jié)果稱為流管道(stream pipe)。這與一般的UNIX管道類似,但流管道是全雙工的,兩個(gè)描述字都是可讀寫(xiě)的。
31.套接口ioctl函數(shù)
#include <unistd.h>
int ioctl(int fd, int request, … /* void *arg */ );
返回:成功返回0,出錯(cuò)返回-1。
第三個(gè)參數(shù)總是一個(gè)指針,但指針的類型依賴于request。
ioctl和網(wǎng)絡(luò)有關(guān)的請(qǐng)求可分為如下6類:
類別 | request | 描述 | 數(shù)據(jù)類型 |
|
|
|
|
套接口 | SIOCATMARK | 在帶外標(biāo)志上嗎 | int |
| SIOCSPGRP | 設(shè)置套接口的進(jìn)程ID或進(jìn)程組ID | int |
| SIOCGPGRP | 獲取套接口的進(jìn)程ID或進(jìn)程組ID | int |
|
|
|
|
文件 | FIONBIO | 設(shè)置/清除非阻塞標(biāo)志 | int |
| FIOASYNC | 設(shè)置/清除異步I/O標(biāo)志 | int |
| FIONREAD | 獲取接收緩沖區(qū)中的字節(jié)數(shù) | int |
| FIOSETOWN | 設(shè)置文件的進(jìn)程ID或進(jìn)程組ID | int |
| FIOGETOWN | 獲取文件的進(jìn)程ID或進(jìn)程組ID | int |
|
|
|
|
接口 | SIOCGIFCONF | 獲取所有接口的列表 | struct ifconf |
| SIOCSIFADDR | 設(shè)置接口地址 | struct ifreq |
| SIOCGIFADDR | 獲取接口地址 | struct ifreq |
| SIOCSIFFLAGS | 設(shè)置接口標(biāo)志 | struct ifreq |
| SIOCGIFFLAGS | 獲取接口標(biāo)志 | struct ifreq |
| SIOCSIFDSTADDR | 設(shè)置點(diǎn)到點(diǎn)地址 | struct ifreq |
| SIOCGIFDSTADDR | 獲取點(diǎn)到點(diǎn)地址 | struct ifreq |
| SIOCGIFBRDADDR | 獲取廣播地址 | struct ifreq |
| SIOCSIFBRDADDR | 設(shè)置廣播地址 | struct ifreq |
| SIOCGIFNETMASK | 獲取子網(wǎng)掩碼 | struct ifreq |
| SIOCSIFNETMASK | 設(shè)置子網(wǎng)掩碼 | struct ifreq |
| SIOCGIFMETRIC | 獲取接口的測(cè)度(metric) | struct ifreq |
| SIOCSIFMETRIC | 設(shè)置接口的測(cè)度(metric) | struct ifreq |
| SIOCxxx | (有很多,依賴于實(shí)現(xiàn)) |
|
|
|
|
|
ARP | SIOCSARP | 創(chuàng)建/修改ARP項(xiàng) | struct arpreq |
| SIOCGARP | 獲取ARP項(xiàng) | struct arpreq |
| SIOCDARP | 刪除ARP項(xiàng) | struct arpreq |
|
|
|
|
路由 | SIOCADDRT | 增加路徑 | struct rtentry |
| SIOCDELRT | 刪除路徑 | struct rtentry |
|
|
|
|
流 | I_xxx |
|
|
(1)套接口操作
- SIOCATMARK:如果套接口的讀指針當(dāng)前在帶外標(biāo)志上,則通過(guò)第三個(gè)參數(shù)指向的整數(shù)返回一個(gè)非零值,否則返回零。Posix.1g用sockatmark代替了這種請(qǐng)求。
- SIOCGPGRP:通過(guò)第三個(gè)參數(shù)指向的整數(shù)返回為接收來(lái)自這個(gè)套接口的SIGIO或SIGURG信號(hào)而設(shè)置的進(jìn)程ID或進(jìn)程組ID。這和fcntl的F_GETOWN相同。
- SIOCSPGRP:用第三個(gè)參數(shù)指向的整數(shù)設(shè)置進(jìn)程ID或進(jìn)程組ID以接收這個(gè)套接口的SIGIO或SIGURG信號(hào)。這和fcntl的F_SETOWN相同。
(2)文件操作
- FIONBIO:套接口的非阻塞標(biāo)志會(huì)根據(jù)第三個(gè)參數(shù)指向的值是否為零而清除或設(shè)置。等價(jià)于fcntl的F_SETFL設(shè)置/清除O_NONBLOCK標(biāo)志。
- FIOASYNC:根據(jù)第三個(gè)參數(shù)指向的值是否為零決定清除或接收套接口上的異步I/O信號(hào)。等價(jià)于fcntl的F_SETFL設(shè)置和清除O_AYNC標(biāo)志。
- FIONREAD:在第三個(gè)參數(shù)指向的整數(shù)中返回套接口接收緩沖區(qū)中當(dāng)前的字節(jié)數(shù)。
- FIOSETOWN:在套接口上等價(jià)于SIOCSPGRP。
- FIOGETOWN:在套接口上等價(jià)于SIOCGPGRP。
(3)接口配置
SIOCGIFCONF:從內(nèi)核中獲取系統(tǒng)中配置的所有接口。它使用了結(jié)構(gòu)ifconf,ifconf又使用了ifreq結(jié)構(gòu)。
結(jié)構(gòu)定義如下:
struct ifconf {
int ifc_len; /* size of buffer, value-result */
union {
caddr_t ifcu_buf; /* input from user->kernel */
struct ifreq *ifcu_req; /* return from kernel->user */
}ifc_ifcu;
};
#define ifc_buf ifc_ifcu.ifcu_buf
#define ifc_req ifc_ifcu.ifcu_req
#define IFNAMSIZ 16
struct ifreq {
char ifr_name[IFNAMSIZ];
union {
struct sockaddr ifru_addr;
struct sockaddr ifru_dstaddr;
struct sockaddr ifru_broadaddr;
short ifru_flags;
int ifru_metric;
caddr_t ifru_data;
}ifr_ifru;
};
#define ifr_addr ifr_ifru.ifru_addr
#define ifr_dstaddr ifr_ifru.ifru_dstaddr
#define ifr_broadaddr ifr_ifru.broadaddr
#define ifr_flags ifr_ifru.ifru_flags
#define ifr_metric ifr_ifru.ifru_metric
#define ifr_data ifr_ifru.ifru_data
在調(diào)用ioctl之前分配一個(gè)緩沖區(qū)和一個(gè)ifconf結(jié)構(gòu),然后初始化后者,iotctl的第三個(gè)參數(shù)指向ifconf結(jié)構(gòu)。
一個(gè)實(shí)現(xiàn)獲取所有接口的程序,可參見(jiàn)unpv12e:lib/get_ifi_info.c
(4)接口操作
- SIOCGIFCONF:從內(nèi)核中獲取系統(tǒng)中配置的所有接口。
(5)ARP高速緩存操作
(6)路由表操作
http://www.cnblogs.com/riky/archive/2006/11/24/570713.aspx