索引:
1.
字節序函數2.
字節操作函數3.
地址轉換函數4.
readn、writen和readline5.
測試描述符類型6.
socket函數7.
connect函數8.
bind函數9.
listen函數10.
accept函數11.
close函數12.
getsockname和getpeername13.
select函數14.
shutdown函數15.
pselect函數16.
poll函數17.
getsockopt和setsockopt18.
套接口選項列表19.
處理套接口的fcntl函數20.
gethostbyname函數21.
gethostbyname2函數22.
ethostbyaddr函數23.
uname函數24.
gethostname函數25.
getservbyname函數26.
getservbyport函數27.
recv和send28.
readv和writev29.
readmsg和writemsg30.
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);
返回:主機字節序值
一個測試本機字節序的程序,可參見見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。
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。
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>
struct hostent *gethostbyname(const char *hostname);
返回:非空指針—成功,空指針—出錯,同時設置h_errno。
函數返回的非空指針指向的結構如下:
struct hostent {
char *h_name; /*規范主機名 */
char **h_aliases; /* 別名列表 */
int h_addrtype; /* AF_INET or AF_INET6 */
int h_length; /* 地址長度 */
char **h_addr_list; /* IPv4或IPv6地址結構列表 */
};
#define h_addr h_addr_list[0];
按照DNS的說法,gethostbyname執行一個對A記錄的查詢或對AAAA記錄的查詢,返回IPv4或IPv6地址。
h_addr的定義是為了兼容,在新代碼中不應使用。
返回的h_name稱為主機的規范(canonical)名字。當返回IPv6地址時,h_addrtype被設置為AF_INET6,成員h_length被設置為16。
gethostbyname的特殊之處在于:當發生錯誤時,他不設置errno,而是將全局整數h_errno設置為定義在頭文件<netdb.h>中的下列常值中的一個:
- HOST_NOT_FOUND;
- TRY_AGAIN;
- NO_RECOVERY;
- NO_DATA(等同于NO_ADDRESS)。
有函數hstrerror(),它將h_errno的值作為唯一的參數,返回一個指向相應錯誤說明的const char *型指針。
DNS小常識:
DNS中的條目稱為資源記錄RR(resource record),僅有少數幾類RR會影響我們的名字與地址轉換:
- A:A記錄將主機名映射為32位的IPv4地址;
- AAAA:“四A”記錄將主機名映射為128位的IPv6地址;
- PTR:PTR記錄(稱為“指針記錄”)將IP地址映射為主機名;
- MX:MX記錄指定一主機作為某主機的“郵件交換器”。
- CNAME:CNAME代表“canonical name(規范名字)”,其常見的用法是為常用服務如ftp和www指派一個CNAME記錄。
21.gethostbyname2函數
#include <netdb.h>
struct hostent *gethostbyname2(const char *hostname, int family);
返回:非空指針—成功,空指針—出錯,同時設置h_errno。
該函數允許指定地址族,其他與gethostbyname相似。
22.gethostbyaddr函數
#include <netdb.h>
struct hostent *gethostbyaddr(const char *addr, size_t len, int family);
返回:非空指針—成功,空指針—出錯,同時設置h_error。
函數根據一個二進制的IP地址并試圖找出相應于此地址的主機名,我們關心的是規范主機名h_name。
參數addr不是char *類型,而是一個真正指向含有IPv4或IPv6地址的結構in_addr或in6_addr的指針;len是該結構的大小,對于IPv4是4,對于IPv6是16;family或為AF_INET或為AF_INET6。
按照DNS的說法,該函數查詢PTR記錄。
23.uname函數
#include <sys/utsname.h>
int uname(struct utsname *name);
返回:非負值—成功,-1—失敗。
返回當前主機的名字,存放在如下的結構里:
#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];
};
該函數經常與gethostbyname一起用來確定本機的IP地址:先調用uname獲得主機名字,然后調用gethostbyname得到所有的IP地址。
獲得本機IP地址的另一個方法是ioctl的命令SIOCGIFCONF。
24.gethostname函數
#include <unistd.h>
int gethostname(char *name, size_t namelen);
返回:0—成功,-1—失敗。
返回當前主機的名字。name是指向主機名存儲位置的指針,namelen是此數組的大小,如果有空間,主機名以空字符結束。
主機名的最大大小通常是頭文件<sys/param.h>定義的常值MAXHOSTNAMELEN。
25.getservbyname函數
#include <netdb.h>
struct servent *getservbyname(const char *servname, const char *protoname);
返回:非空指針—成功,空指針—失敗。
函數返回如下結構的指針:
struct servent {
char *s_name;
char **s_aliases;
int s_port;
char *s_proto;
};
服務名servname必須指定,如果還指定了協議(protoname為非空指針),則結果表項必須有匹配的記錄。如果沒有指定協議名而服務支持多個協議,則返回哪個端口是依賴于實現的。
結構中的端口號是以網絡字節序返回的,所以在將它存儲在套接口地址結構時,絕對不能調用htons。
26.getservbyport函數
#include <netdb.h>
struct servent *getservbyport(int port, const char *protname);
返回:非空指針—成功,空指針—出錯。
port必須為網絡字節序。例如:
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);
返回:成功返回讀入或寫出的字節數,出錯返回-1。
前三個參數與read和write相同,參數flags的值或為0,或由以下的一個或多個常值邏輯或構成:
flags | 描述 | recv | send |
|
|
|
|
MSG_DONTROUTE | 不查路由表 |
| y |
MSG_DONTWAIT | 本操作不阻塞 | y | y |
MSG_OOB | 發送或接收帶外數據 | y | y |
MSG_PEEK | 查看外來的消息 | y |
|
MSG_WAITALL | 等待所有數據 | y |
|
下面說明每個標志的作用:
- MSG_DONTROUTE:這個標志告訴內核目的主機在直接連接的本地網絡上,不要查路由表。這是對提供這種特性的SO_DONTROUTE套接口選項的補充。該標志可以對單個輸出操作提供這種特性,而套接口選項則針對某個套接口上的所有輸出操作。
- MSG_DONTWAIT:這個標志將單個I/O操作設為非阻塞方式,而不需要在套接口上打開非阻塞標志,執行I/O操作,然后關閉阻塞標志。
- MSG_OOB:用send時,這個標志指明發送的是帶外數據,用recv時,該標志指明要讀的是帶外數據而不是一般數據。
- MSG_PEEK:這個標志可以讓我們查看可讀的數據,在recv或recvfrom后系統不會將這些數據丟棄。
- MSG_WAITALL: 由4.3BSD Reno引入,他告訴內核在沒有讀到請求的字節數之前不使讀操作返回。如果系統支持這個標志,則可以去掉readn函數。即使設定了該標志 ,如果發生如下情況:(1)捕獲了一個信號;(2)連接被終止;(3)在套接口上發生錯誤,這個函數返回的字節數仍會比請求的少。
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);
返回:讀到或寫出的字節數,出錯返回-1。
readv和writev可以讓我們在一個函數調用中讀或寫多個緩沖區,這些操作被稱為分散讀和集中寫。
iovec結構定義如下:
struct iovec {
void *iov_base; /* starting address of buffer */
size_t iov_len; /* size of buffer */
};
在具體的實現中對iovec結構數組的元素個數有限制,4.3BSD最多允許1024個,而Solaris2.5上限是16。Posix.1g要求定義一個常值IOV_MAX,而且它的值不小于16。
readv和writev可用于任何描述字。writev是一個原子操作,可以避免多次寫引發的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);
返回:成功時為讀入或寫出的字節數,出錯時為-1。
這兩個函數是最通用的套接口I/O函數,可以用recvmsg代替read、readv、recv和recvfrom,同樣,各種輸出函數都可以用sendmsg代替。
參數msghdr結構的定義如下:
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() */
};
該結構源自4.3BSD Reno,也是Posix.1g中所說明的,有些系統仍使用一種老的msghdr結構,此種結構中沒有msg_flags成員,而且 msg_control和msg_controllen成員分別被叫做msg_accrights和msg_accrightslen。老系統中支持的唯 一一種輔助數據形式是文件描述字(稱為訪問權限)的傳遞。
msg_name和msg_namelen成員用于未經連接的套接口,他們與 recvfrom和sendto的第五和第六個參數類似:msg_name指向一個套接口地址結構,如果不需要指明協議地址,msg_name應被設置為 空指針,msg_namelen對sendmsg是一個值,而對recvmsg是一個值-結果參數。
msg_iov和msg_iovlen成員指明輸入或輸出的緩沖區數組。
msg_control和msg_controllen指明可選的輔助數據的位置和大小,msg_controllen對recvmsg是一個值-結果參數。
msg_flags只用于revmsg,調用recvmsg時,flags參數被拷貝 到msg_flags成員,而且內核用這個值進行接收處理,接著它的值會根據recvmsg的結果而更新,sendmsg會忽略msg_flags成員, 因為它在進行輸出處理時使用flags參數。
內核檢查的flags和返回的msg_flags如下表所示:
標志 | 在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 |
前四個標志只檢查不返回,下兩個標志既檢查又返回,最后四個只返回。返回的六個標志含義如下:
- MSG_BCAST:當收到的數據報是一個鏈路層的廣播或其目的IP地址為廣播地址時,將返回此標志。
- MSG_MCAST:當收到的數據報是鏈路層的多播時,將返回該標志。
- MSG_TRUNC:這個標志在數據報被截斷時返回。
- MSG_CTRUNC:這個標志在輔助數據被截斷時返回。
- MSG_EOR:如果返回的數據不是一個邏輯記錄的結尾,該標志被清位,反之則置位。TCP不使用這個標志,因為它是一種字節流協議。
- MSG_OOB:這個標志不是為TCP的帶外數據返回的,它用于其他協議族(譬如OSI協議等)。
具體的實現可能會在msg_flags中返回一些輸入的flags的標志,所以我們應該只檢查那些感興趣的標志的值。
30.socketpair函數
#include <sys/socket.h>
int socketpair(int family, int type, int protocol, int sockfd[2]);
返回:成功返回0,出錯返回-1。
family必須為AF_LOCAL,protocol必須為0,type可以是SOCK_STREAM或SOCK_DGRAM。新創建的兩個套接口描述字作為sockfd[0]和sockfd[1]返回。
這兩個描述字相互連接,沒有名字,即沒有涉及隱式bind。
以SOCK_STREAM作為type調用所得到的結果稱為流管道(stream pipe)。這與一般的UNIX管道類似,但流管道是全雙工的,兩個描述字都是可讀寫的。
31.套接口ioctl函數
#include <unistd.h>
int ioctl(int fd, int request, … /* void *arg */ );
返回:成功返回0,出錯返回-1。
第三個參數總是一個指針,但指針的類型依賴于request。
ioctl和網絡有關的請求可分為如下6類:
類別 | request | 描述 | 數據類型 |
|
|
|
|
套接口 | SIOCATMARK | 在帶外標志上嗎 | int |
| SIOCSPGRP | 設置套接口的進程ID或進程組ID | int |
| SIOCGPGRP | 獲取套接口的進程ID或進程組ID | int |
|
|
|
|
文件 | FIONBIO | 設置/清除非阻塞標志 | int |
| FIOASYNC | 設置/清除異步I/O標志 | int |
| FIONREAD | 獲取接收緩沖區中的字節數 | int |
| FIOSETOWN | 設置文件的進程ID或進程組ID | int |
| FIOGETOWN | 獲取文件的進程ID或進程組ID | int |
|
|
|
|
接口 | SIOCGIFCONF | 獲取所有接口的列表 | struct ifconf |
| SIOCSIFADDR | 設置接口地址 | struct ifreq |
| SIOCGIFADDR | 獲取接口地址 | struct ifreq |
| SIOCSIFFLAGS | 設置接口標志 | struct ifreq |
| SIOCGIFFLAGS | 獲取接口標志 | struct ifreq |
| SIOCSIFDSTADDR | 設置點到點地址 | struct ifreq |
| SIOCGIFDSTADDR | 獲取點到點地址 | struct ifreq |
| SIOCGIFBRDADDR | 獲取廣播地址 | struct ifreq |
| SIOCSIFBRDADDR | 設置廣播地址 | struct ifreq |
| SIOCGIFNETMASK | 獲取子網掩碼 | struct ifreq |
| SIOCSIFNETMASK | 設置子網掩碼 | struct ifreq |
| SIOCGIFMETRIC | 獲取接口的測度(metric) | struct ifreq |
| SIOCSIFMETRIC | 設置接口的測度(metric) | struct ifreq |
| SIOCxxx | (有很多,依賴于實現) |
|
|
|
|
|
ARP | SIOCSARP | 創建/修改ARP項 | struct arpreq |
| SIOCGARP | 獲取ARP項 | struct arpreq |
| SIOCDARP | 刪除ARP項 | struct arpreq |
|
|
|
|
路由 | SIOCADDRT | 增加路徑 | struct rtentry |
| SIOCDELRT | 刪除路徑 | struct rtentry |
|
|
|
|
流 | I_xxx |
|
|
(1)套接口操作
- SIOCATMARK:如果套接口的讀指針當前在帶外標志上,則通過第三個參數指向的整數返回一個非零值,否則返回零。Posix.1g用sockatmark代替了這種請求。
- SIOCGPGRP:通過第三個參數指向的整數返回為接收來自這個套接口的SIGIO或SIGURG信號而設置的進程ID或進程組ID。這和fcntl的F_GETOWN相同。
- SIOCSPGRP:用第三個參數指向的整數設置進程ID或進程組ID以接收這個套接口的SIGIO或SIGURG信號。這和fcntl的F_SETOWN相同。
(2)文件操作
- FIONBIO:套接口的非阻塞標志會根據第三個參數指向的值是否為零而清除或設置。等價于fcntl的F_SETFL設置/清除O_NONBLOCK標志。
- FIOASYNC:根據第三個參數指向的值是否為零決定清除或接收套接口上的異步I/O信號。等價于fcntl的F_SETFL設置和清除O_AYNC標志。
- FIONREAD:在第三個參數指向的整數中返回套接口接收緩沖區中當前的字節數。
- FIOSETOWN:在套接口上等價于SIOCSPGRP。
- FIOGETOWN:在套接口上等價于SIOCGPGRP。
(3)接口配置
SIOCGIFCONF:從內核中獲取系統中配置的所有接口。它使用了結構ifconf,ifconf又使用了ifreq結構。
結構定義如下:
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
在調用ioctl之前分配一個緩沖區和一個ifconf結構,然后初始化后者,iotctl的第三個參數指向ifconf結構。
一個實現獲取所有接口的程序,可參見unpv12e:lib/get_ifi_info.c
(4)接口操作
- SIOCGIFCONF:從內核中獲取系統中配置的所有接口。
(5)ARP高速緩存操作
(6)路由表操作
http://www.cnblogs.com/riky/archive/2006/11/24/570713.aspx