。。。。。。。(先把理論放一下)node
struct sockaddr_in
{
__SOCKADDR_COMMON (sin_);
in_port_t sin_port; /* Port number. */
struct in_addr sin_addr; /* Internet address. */
/* Pad to size of `struct sockaddr'. */
unsigned char sin_zero[sizeof (struct sockaddr) -
__SOCKADDR_COMMON_SIZE -
sizeof (in_port_t) -
sizeof (struct in_addr)];
};
確實符合POSIX標準,不過沒有增長額外字段linux
struct osockaddr
{
//uint8_t sa_len;書上還有這一句話
unsigned short int sa_family;
unsigned char sa_data[14];
};
struct sockaddr_in serv;
/* fill in serv{} */
bind(sockd, (struct sockadd *) &serv, sizeof(serv));
/* IPv6 address */
struct in6_addr
{
union
{
uint8_t __u6_addr8[16];
#ifdef __USE_MISC
uint16_t __u6_addr16[8];
uint32_t __u6_addr32[4];
#endif
} __in6_u;
#define s6_addr __in6_u.__u6_addr8
#ifdef __USE_MISC
# define s6_addr16 __in6_u.__u6_addr16
# define s6_addr32 __in6_u.__u6_addr32
#endif
};
/* Ditto, for IPv6. */
struct sockaddr_in6
{
__SOCKADDR_COMMON (sin6_);
in_port_t sin6_port; /* Transport layer port # */
uint32_t sin6_flowinfo; /* IPv6 flow information */
struct in6_addr sin6_addr; /* IPv6 address */
uint32_t sin6_scope_id; /* IPv6 scope-id */
};
struct sockaddr-un cli; /* Unix domain */
socklen_t len;
len= sizeof(cli);
/ len is a value */
getpeername( unixfd,(SA *) &cli, &len);
/* len may have changed */
注:當函數被調用時,結構大小是一個值(此值告訴內核該結構的大小,使內核在寫此結構時不至於越界),當函數返回時,結構大小又是一個結果(它告訴進程內核在此結構中確切存儲了多少信息),這種參數類型叫值結果參數
### 3.4字節排序函數
+ 大端:高字節放起始地址,如0x0102,內存中放的是0x0102
+ 小端:高字節放高地址,低字節放低地址如0x0102,內存中放的是0x0201
+ 網際協議使用大端字節序傳送多字節整數
git
#include <netinet/in h>
uint16_t htons (uint16t host16bitvalue): //h:host
uint32_t htonl (uint32t host32bitvalue); //n:network
uint16_t ntohs (uint16t net16bitvalue); //s:short
uint32_t ntohl (uint32t net32bitvalue); //l:long
#ifdef __USE_MISC
/* Copy N bytes of SRC to DEST (like memmove, but args reversed). */
extern void bcopy (const void *__src, void *__dest, size_t __n)
__THROW __nonnull ((1, 2));
/* Set N bytes of S to 0. */
extern void bzero (void *__s, size_t __n) __THROW __nonnull ((1));
/* Compare N bytes of S1 and S2 (same as memcmp). */
extern int bcmp (const void *__s1, const void *__s2, size_t __n)
__THROW __attribute_pure__ __nonnull ((1, 2));
之後不要用memset了,bzero()好記多了
github
__BEGIN_NAMESPACE_STD
/* Set N bytes of S to C. */
extern void *memset (void *__s, int __c, size_t __n) __THROW __nonnull ((1));
/* Compare N bytes of S1 and S2. */
extern int memcmp (const void *__s1, const void *__s2, size_t __n)
__THROW __attribute_pure__ __nonnull ((1, 2));
__BEGIN_NAMESPACE_STD
/* Copy N bytes of SRC to DEST. */
extern void *memcpy (void *__restrict __dest, const void *__restrict __src,
size_t __n) __THROW __nonnull ((1, 2));
/* Copy N bytes of SRC to DEST, guaranteeing
correct behavior for overlapping strings. */
extern void *memmove (void *__dest, const void *__src, size_t __n)
__THROW __nonnull ((1, 2));
__END_NAMESPACE_STD
/* Copy no more than N bytes of SRC to DEST, stopping when C is found.
Return the position in DEST one byte past where C was copied,
or NULL if C was not found in the first N bytes of SRC. */
#if defined __USE_MISC || defined __USE_XOPEN
extern void *memccpy (void *__restrict __dest, const void *__restrict __src,
int __c, size_t __n)
__THROW __nonnull ((1, 2));
#endif /* Misc || X/Open. */
/* include inet_pton */
int
inet_pton(int family, const char *strptr, void *addrptr)
{
if (family == AF_INET) {
struct in_addr in_val;
if (inet_aton(strptr, &in_val)) { //調用了上一節講的函數,返回的是C字符串形式的IP地址
memcpy(addrptr, &in_val, sizeof(struct in_addr));
return (1);
}
return(0);
}
errno = EAFNOSUPPORT;
return (-1);
}
/* end inet_pton */
/* include sock_ntop */
char *sock_ntop(const struct sockaddr *sa, socklen_t salen)
{
char portstr[8];
static char str[128]; /* Unix domain is largest */
switch (sa->sa_family) {
case AF_INET: {
struct sockaddr_in *sin = (struct sockaddr_in *) sa;
//地址轉換:成功則返回c字符串形式的IP地址,str指定轉換格式
if (inet_ntop(AF_INET, &sin->sin_addr, str, sizeof(str)) == NULL)
return(NULL);
//字節排序:網絡轉換爲主機的字節序
if (ntohs(sin->sin_port) != 0) {
snprintf(portstr, sizeof(portstr), ":%d", ntohs(sin->sin_port));
strcat(str, portstr);
}
return(str);
}
//。。。。。。
ssize_t /* Read "n" bytes from a descriptor. */
readn(int fd, void *vptr, size_t n)
{
size_t nleft;
ssize_t nread;
char *ptr;
ptr = vptr;
nleft = n;
while (nleft > 0) {
//若是讀取失敗
if ( (nread = read(fd, ptr, nleft)) < 0) {
if (errno == EINTR) /* 查找EINTR錯誤,表示系統被一個捕獲信號中斷 */
nread = 0; /* and call read() again */
else
return(-1);
//若是讀成功了
} else if (nread == 0)
break; /* EOF */
//計算漏讀的字節數,再讀文件
nleft -= nread;
ptr += nread;
}
return(n - nleft); /* return >= 0 */
}
/* end readn */
int socket(int family, int type, int protocol)
int connect(int sockfd, const struct sockaddr *servaddr, socklen_t addrlen);
int bind(int sockfd, const struct sockaddr *myaddr, socklen_t addrlen);
getsockname()
來返回協議地址把一個未鏈接的套接字轉換成一個被動套接字,即從CLOSE轉換到LISTEN狀態算法
int listen(int sockfd, int backlog);
int accept(int sockfd, struct sockaddr *cliaddr, socklen_t *addrlen);
已鏈接套接字描述符
,它的第一個參數叫監聽套接字描述符pid_t fork();
:這個函數再《UNIX環境高級編程》有更詳細的敘述execX(...)
:見書本90頁 int close(sockfd);
:能夠用來關閉套接字,並終止TCP鏈接int getsockname(int sockfd, struct sockaddr *localaddr, socklen_t *addrlen);
:返回本地IP和端口號 int getpeername(int sockfd, struct sockaddr *peeraddr, socklen_t *addrlen);
6.查看:netstat -ant | grep 9877 ,獲得下面的輸出結果(-代替空格)編程
tcp—0—-0 0.0.0.0:9877———-0.0.0.0:*————-LISTEN
tcp—-0—-0 127.0.0.1:39960—-127.0.0.1:9877—-TIME_WAIT api
7.查看進程:ps -ef –forest -o pid,ppid,tty,stat,args,wchan | sed -n ‘/tcp/p’數組
110361 109527 pts/18 S \_ ./tcpserv01 XDG_VTNR=7 inet_csk_accept
110366 110361 pts/18 Z | \_ [tcpserv01] <defunc -
110517 109527 pts/18 S+ \_ sed -n /tcp/p XDG_VTNR= pipe_wait
能夠看到僵死進程緩存
SIGCLD:在一個進程終止或者中止時,將SIGCHLD信號發送給其父進程;設置僵死進程是爲了維護子進程信息,不處理可能致使耗盡資源服務器
#include<sys/wait.h>
pid_t wait(int *statloc);
pid_t waitpid(pid_t pid, int *statloc, int option);
+ 服務器:已由TCP排隊,等待進程調用accept()卻受到RST,稍後執行accept()
+ 處理方法:
+ Berkeley:徹底在內核中處理已終止的鏈接,服務器進程根本看不到
+ 大多數SVR4:返回一個EPROTO(protocol error:協議錯誤)
+ POSIX:errno值必須是ECONNABORTED(software caused connection abort,軟件引發的鏈接終止),由於流子系統其餘一些致命協議相關事件時也會返回EPROTO
殺死服務器子進程(模擬服務器進程崩潰),server發送FIN,然而client阻塞在fgets(同時應對兩個文件描述符,套接字和用戶輸入),從未沒法返回ACK,此時鍵入一行文本,客戶端將發送到服務器,此時有兩種狀況:
+ 1.調用readline(),而後收到RET,因爲前面的FIN,readline()馬上返回0,收到未預期的EOF,提示出錯信息:server terminated prematurely,(服務器過早終止)
+ 先收到RET,調用readline()時返回一個ECONNRESET(connection reset by peer),對方復位鏈接錯誤
上節狀況下的客戶端不理會readline()的錯誤反而寫入更多的數據到服務器,即一個進程向某個已收到RST的套接字執行寫操做,內核會向進程發送一個SIGPIPE信號,默認行爲是終止進程。不管是捕獲仍是忽略該信號都將返回EPIPE錯誤。沒有特殊事情要作就設置成SIG_IGN(忽略)
+ 須要注意:若是使用多個套接字,該信號的提交無法告訴咱們是哪個套接字出錯了
- 因此要麼不理會
- 要麼從信號函數返回後再處理來自wirite的EPIPE
輸入操做:1.等待數據準備好,2.從內核向進程複製數據
int select(int maxfdp1,fd_set *readset, fd_set *writeset, fd_set *exceptset, const struct timeval *timeout);
struct timeval {
long tv_sec; /* seconds */
long tv_usec; /* microseconds ,許多UNIX向上舍10ms整數倍,再加調度延遲,時間更不許確*/
}
fd_set rset;
FD_ZERO(&rset);
FD_SET(1, &rset);
FD_SET(4, &rset);
FD_SET(5, &rset);
int shutdown(int sockfd, int howto)
見代碼註釋:github連接
#include<sys/select.h>
#include<signal.h>
#include<time.h>
int pselect(int maxfdp1, fd_set *readset, fd_set *writeset, fd_set *execptset, const struct timespec *timeout,const sigset_t *sigmask);
相比較於select:
+ 1.使用timespec 而不用timeval
struct timespec {
time_t tv_sec; //秒
long tv_nsec; //注意:這個表示納秒
}
功能與select相似,不過在處理流設備時能提供更多的信息
#include <poll.h>
int poll(struct pollfd *fdarray, unsigned long nfds, int timeout);
struct pollfd {
int fd ; //須要檢查的描述符,負數將被忽略
short events; //測試條件
short revents; //返回的描述符狀態
}
見代碼註釋:github連接
#include <sys/socket.h>
int getsockopt(int sockfd, int level, int optname, void *optname, void *optval, socklen_t *optlen);
int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen);
均返回:成功0,出錯-1
#include <sys/socket.h>
int getsockopt(int sockfd, int level, int optname, void *optname, void *optval, socklen_t *optlen);
int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen);
均返回:成功0,出錯-1
見註釋:代碼連接
accept一直要到三次握手完成之後才返回給服務器已鏈接的套接字,想在三次握手完成時確保這些套接字選項中的某一個是給 已鏈接套接字 設置的,必須先設置 監聽套接字
7.5.1 SO_BROADCAST
- 本選項開啓或禁止進程發送廣播(僅數據報套接字支持,且需在支持廣播消息的網絡上如以太網和令牌環網)
- 能夠防止沒有設置成廣播時發送廣播數據:如UDP發送一個的目的地址是一個廣播地址,可是該選項沒設置,就會返回EACCES錯誤
7.5.2 SO_DEBUG
僅由TCP支持,選項開啓時內核將爲TCP在該套接字發送和接收全部分組保留詳細信息,可用trpt查看
7.5.3 SO_DONTROUTE
規定外出分組將繞過底層協議的正常路由機制,用來繞過路由表,以強制將分組從特定的接口發出
7.5.4 SO_ERROR(可獲取不能設置)
套接字發生錯誤時,將套接字的so_error變量設置爲爲Unix Exxx值中的一個,也叫待處理錯誤(pending error),能夠用下面兩種方式中的一種當即通知進程
+ 1.阻塞在select時,返回設置RW中一個或兩個條件
+ 2.信號驅動IO模型:產生SIGIO信號,進程經過訪問SO_ERROR獲取so_error的值getsockopt()返回的整個數值就是待處理錯誤,處理後復位爲0
+ 阻塞在read()且沒有數據且so_error非0,返回-1,error設置爲so_error的值,so_error設置爲0。
+ 阻塞在read()但有數據,則返回數據
+ 調用write時so_error非0,返回-1,error設置爲so_error的值,so_error設置爲07.5.5 SO_KEEPALIVE
設置保活選項後,2小時後(期間沒有數據)TCP自動發送保活探測分節(keep-alive probe),會致使三種狀況
+ 1.以指望ACK響應,進程得不到通知
+ 2.響應RST,表對端已崩潰並重啓,套接字的待處理錯誤設置爲ECONNRESET
+ 3.沒有任何響應,間隔75s再發8個探測分節,11m15s後放棄且帶錯李錯誤設置爲ETIMEOUT.若是收到ICMP錯誤就返回相應錯誤。
+這是一個清理通向不可達客戶的半開鏈接的好方法
7.5.6 SO_LINGER
本選項指定close()函數對面向鏈接的協議如何操做,默認當即返回,若是有數據殘留將嘗試把這些數據發送給對端
![]()
要求傳送給內核以下結構:struct linger { int l_onoff; /*0=off,l_linger被忽略,>nonzero=on*/ int l_linger; /*linger time*/ }
- linger=0:丟棄緩衝區的任何數據,發送RST給對端,沒有四分節終止序列,可避免TCP的TIME_WAIT狀態。可能引起錯誤:在2MSL內建立另外一個化身,剛終止的鏈接上的舊的分節不被正確的傳遞到新的化身上
- linger!=0,套接字關閉時內核拖延一段時間:進程將睡眠到全部數據已發送並被確認或延滯時間到
- 套接字是非阻塞型的,延滯時間到以前數據沒發送完返回EWOULDBLOCK錯誤
- close()成功返回只能說明發送的數據和FIN已有對端確認,但不表明進程已經讀取,因此改用shutdown好一點,固然也能用應用級ACK
7.5.7 SO_OOBINLINE
- 帶外數據將被留存在正常的輸入隊列中(即在線留存),此時接收函數的MSG_OOB標誌不能用來讀取帶外數據
7.5.8 SO_CVBUF和SO_SNDBUF
- 套接字接收緩衝區中可用空間大小限定了TCP通告對端窗口的大小
- 注意順序:窗口規模選項是在創建鏈接時用SYN分節獲得的,因此客戶需在connect以前,serv需在listen以前
- 根據快恢復算法,緩衝區大小至少應是MSS值的四倍,最好是偶數倍
7.5.9 SO_RCVLOWAT和SO_SNDLOWAT(低水位標記)
- 接收低水位標記:select返回可讀時接收緩衝區所需的數據量,T/UDP、SCTP默認爲1
- 發送緩衝區:select()返回可寫時發送緩衝區所需的可用空間。tcp默認2048,UDP的發送緩衝區的可用字節數從不改變(不保留副本)
7.5.10 SO_RCVTIMEO和SO_SNDTIMEO
設置超時值,默認設置爲0,即禁止超時
7.5.11 SO_REUSEADDR和SO_REUSEPORT(重用地址端口)
- 監聽服務器終止,子進程繼續處理鏈接,重啓監聽服務器時會出錯,而開啓了SO_REUSEADDR就不會。
- SO_REUSEADDR容許同一個端口的多個服務器實例(只要不一樣的本地IP地址便可),通配地址捆綁通常放到最後
- SO_REUSEADDR容許同一個端口捆綁同一個進程的不一樣套接字
- SO_REUSEADDR容許UDP徹底重複捆綁(通常來講),用於多播
7.5.12 SO_TYPE
- 本選項返回套接字類型,返回值是一個諸如SOCK_STREAM或SOCK_DGRAM之類的值,一般由啓動時繼承了套接字的進程使用
7.5.12 SO_USELOOPBACK
僅用於路由域(AF_ROUTE)套接字,默認打開。開啓時,相應套接字將接收在其上發送的任何數據報的一個副本。
級別爲IPPROTO_IP(get/setsockopt()的第二個參數)
7.6.1 IP_HDRINCL
- 若是是給原始IP套接字設置的,必須本身構造首部,下列狀況例外:
- 見Page168
7.6.2 IP_OPTIONS
容許在IPv4首部總設置IP選項
7.6.3 IP_RECVDSTADDR
- 開啓致使所收到的UDP數據報的目的地址由recvmsg做爲輔助函數返回
7.6.4 IP_RECVIF
- 開啓致使所收到的UDP數據報的接收接口索引由recvmsg函數做爲輔助函數返回
7.6.5 IP_TOS
- 本套接字選項容許咱們爲TCP、UDP、SCTP設置IP首部中的服務類型字段。。。
7.6.6 IP_TTL
- 用於設置或獲取系統用在從某個給定套接字發送的單播分組上的默認TTL值
- 級別爲IPPROTO_ICMPV6
- ICMP6_FILTER:用於獲取或設置一個icmp6_filter結構
- 級別爲:IPPORTO_IPV6
7.8.1 IPV6_CHECKSUM
- 指定用戶數據中校驗和所處位置的字節偏移
7.8.2 IPV6_DONTFRAG
- 禁止爲UDP套接字或原始套接字自動插入分片首部,將外出分組中大小超過發送接口MTU的那些分組將被丟棄
7.8.3 IPV6_NEXTHOP
- 將外出數據報的嚇一跳地址指定爲一個套接字地址結構
7.8.4 IPV6_PATHMTU
- 返回路徑MTU發現功能肯定的當前MTU
7.8.5 IPV6_RECVDSTOPTS
- 任何接收的IPv6地址都將由recvmsg做爲輔助函數返回,默認關閉
7.8.6 IPV6_RECVHOPLIMIT
- 開啓後,任何接收的跳限字段都將由recvmsg做爲輔助函數返回
7.8.7 IPV6_RECVHOPOPTS
- 開啓時,任何接收的IPv6跳選項做爲輔助函數返回
。。。不懂這節在說什麼
級別:IPPROTO_TCP
- 該選項容許設置或獲取TCP鏈接的最大分節大小,返回值是TCP能夠發送給對端的最大數據量
7.9.2 TCP_NODELAY
- 本選項將禁止TCP的Nagle算法(防止一個鏈接在任什麼時候刻有多個小分組待確認)
- Nagle算法一般和ACK延滯算法(delayed ACK algorithm),該算法使得TCP在接收到數據後延滯一段時間(50-200ms)再確認
+對於客戶以若干小片發送數據向服務器發送單個邏輯請求:首選方法是調用writev(),次一點是兩次數據放到緩衝區而後調用write(),最次方法是先設置TCP_NODELAY再調用兩次write()
級別:IPPROTO_SCTP
………………………………………(後面再看)
#include <fcntl.h>
int fcntl(int fd, int cmd, .../* int arg */);
O_NONBLOCK
(非阻塞式IO),O_ASYNC
(信號驅動式IO)int flag;
/* Set a socket as nonblocking */
if((flag=fcntl(fd, F_GETFL, 0)) < 0){ //必需要先獲取其餘文件標誌
err_sys("F_GETFL, error");
}
flag |=O_NONBLOCK; //或運算,打開非阻塞標誌
if(fcntl(fd, F_SETFL, flags) <0 ){
err_sys("F_SETFL error");
}
flag &=~O_NONBLOCK; //與運算,關閉非阻塞標誌
if(fcntl(fd, F_SETFL, flags) <0 ){
err_sys("F_SETFL error");
}
#include <sys/socket.h>
ssize_t recvfrom(int sockfd, void *buff, size_t nbytes, int flag, struct sockaddr *from, socklen_t *addrlen);
ssize_t sendto(int sockfd, const void *buff, size_t nbytes, int flag, const struct sockaddr *to, socklen_t addrlen);
僅在進程已將其UDP套接字鏈接到偏偏一個對端後,這個異步 錯誤才返回給進程
這裏的connect()不一樣於TCP,只檢查時候存在當即可知的錯誤,記錄對端IP地址和端口號,而後返回給進程。鏈接後主要發生三點變化:
顯式
connect()好一點/etc/resolv.conf:保存服務器主機IP的文件
代碼是書上的
+ 當鏈接一個沒有運行的udp服務器的程序時,鏈接不會出錯,可是發送數據時會返回一個目的端口不可達的ICMP錯誤,被內核映射成ECONNREFUSED,UnixWare內核不會返回這種錯誤(page200)
netstat -s
:顯示丟失的數據包8.1
recvfrom返回2048個字節的數據,它不會返回大於一個數據報大小的數據
#include <netinet/sctp.h>
int sctp_bindx(int sockfd, const struct sockaddr *addrs, int addrcnt, int flags);
#include <netinet/sctp.h>
int sctp_connectx(int sockfd, const struct sockaddr *addrs, int addrcnt);
Returns: 0 for success, –1 on error
#include <netinet/sctp.h>
int sctp_getpaddrs(int sockfd, sctp_assoc_t id, struct sockaddr **addrs);
Returns: the number of peer addresses stored in addrs, –1 on error
#include <netinet/sctp.h>
void sctp_freepaddrs(struct sockaddr *addrs);
#include <netinet/sctp.h>
int sctp_getladdrs(int sockfd, sctp_assoc_t id, struct sockaddr **addrs);
Returns: the number of local addresses stored in addrs, –1 on error
#include <netinet/sctp.h>
void sctp_freeladdrs(struct sockaddr *addrs);
ssize_t sctp_sendmsg(int sockfd, const void *msg, size_t msgsz, const struct sockaddr *to, socklen_t tolen, uint32_t ppid, uint32_t flags, uint16_t stream, uint32_t timetolive, uint32_t context);
Returns: the number of bytes written, –1 on error
ssize_t sctp_recvmsg(int sockfd, void *msg, size_t msgsz, struct sockaddr *from, socklen_t *fromlen, struct sctp_sndrcvinfo *sinfo, int *msg_flags);
Returns: the number of bytes read, –1 on error
int sctp_opt_info(int sockfd, sctp_assoc_t assoc_id, int opt void *arg, socklen_t *siz);
Returns: 0 for success, –1 on error
int sctp_peeloff(int sockfd, sctp_assoc_t id);
Returns: a new socket descriptor on success, –1 on error
struct sctp_tlv {
u_int16_t sn_type;
u_int16_t sn_flags;
u_int32_t sn_length;
};
/* notification event */
union sctp_notification {
struct sctp_tlv sn_header; //用於解釋類型值
struct sctp_assoc_change sn_assoc_change;
struct sctp_paddr_change sn_paddr_change;
struct sctp_remote_error sn_remote_error;
struct sctp_send_failed sn_send_failed;
struct sctp_shutdown_event sn_shutdown_event;
struct sctp_adaption_event sn_adaption_event;
struct sctp_pdapi_event sn_pdapi_event;
};
.。。。先放一下,留給個人時間很少了。
lsmod | grep sctp
:查看是否安裝了sctp#include <netdb.h>
//函數的侷限是隻能返回IPv4地址
struct hostent *gethostbyname (const char *hostname);
Returns: non-null pointer if OK,NULL on error with h_errno set
struct hostent {
char *h_name; /* 主機的規範名字 */
char **h_aliases; /* 別名 */
int h_addrtype; /* host address type: AF_INET */
int h_length; /* length of address: 4 */
char **h_addr_list; /* ptr to array of ptrs with IPv4 addrs */
};
#include <netdb.h>
struct hostent *gethostbyaddr (const char *addr, socklen_t len, int family);
Returns: non-null pointer if OK, NULL on error with h_errno set
/etc/services
:名字到端口的映射#include <netdb.h>
struct servent *getservbyname (const char *servname, const char *protoname);
Returns: non-null pointer if OK, NULL on error
struct servent {
char *s_name; /* official service name */
char **s_aliases; /* alias list */
int s-port; /* port number, network-byte order:網絡字節序 */
char *s_proto; /* protocol to use */
};
struct servent *sptr;
sptr = getservbyname("domain", "udp"); /* DNS using UDP */
sptr = getservbyname("ftp", "tcp"); /* FTP using TCP */
sptr = getservbyname("ftp", NULL); /* FTP using TCP */
sptr = getservbyname("ftp", "udp"); /* this call will fail */
#include <netdb.h>
struct servent *getservbyport (int port, const char *protoname);
Returns: non-null pointer if OK, NULL on error
struct servent *sptr;
sptr = getservbyport (htons (53), "udp"); /* DNS using UDP */
sptr = getservbyport (htons (21), "tcp"); /* FTP using TCP */
sptr = getservbyport (htons (21), NULL); /* FTP using TCP */
sptr = getservbyport (htons (21), "udp"); /* this call will fail */
#include <netdb.h>
int getaddrinfo (const char *hostname, const char *service, const struct addrinfo *hints, struct addrinfo **result) ;
Returns: 0 if OK, nonzero on error (see Figure 11.7)
struct addrinfo {
int ai_flags; /* AI_PASSIVE, AI_CANONNAME */
int ai_family; /* AF_xxx */
int ai_socktype; /* SOCK_xxx */
int ai_protocol; /* 0 or IPPROTO_xxx for IPv4 and IPv6 */
socklen_t ai_addrlen; /* length of ai_addr */
char *ai_canonname; /* ptr to canonical name for host */
struct sockaddr *ai_addr; /* ptr to socket address structure */
struct addrinfo *ai_next; /* ptr to next structure in linked list */
};
。。。後面的太多先放一下
#include <netdb.h>
const char *gai_strerror (int error);
Returns: pointer to string describing error message
#include <netdb.h>
void freeaddrinfo (struct addrinfo *ai);
#include "unp.h"
struct addrinfo *host_serv (const char *hostname, const char *service, int family, ints socktype);
Returns: pointer to addrinfo structure if OK, NULL on error
#include "unp.h"
int tcp_connect (const char *hostname, const char *service);
Returns: connected socket descriptor if OK, no return on error
#include "unp.h"
int tcp_listen (const char *hostname, const char *service, socklen_t *addrlenp);
Returns: connected socket descriptor if OK, no return on error
#include "unp.h"
int udp_client (const char *hostname, const char *service, struct sockaddr **saptr, socklen_t *lenp);
Returns: unconnected socket descriptor if OK, no return on error
#include "unp.h"
int udp_connect (const char *hostname, const char *service);
Returns: connected socket descriptor if OK, no return on error
#include "unp.h"
int udp_server (const char *hostname, const char *service, socklen_t *lenptr);
Returns: unconnected socket descriptor if OK, no return on error
int getnameinfo (const struct sockaddr *sockaddr, socklen_t addrlen, char *host, socklen_t hostlen, char *serv, socklen_t servlen, int flags) ;
Returns: 0 if OK, nonzero on error (see Figure 11.7)
注意如下幾點:
+ gethostbyname, gethostbyaddr, getservbyname, and get servbyport are not re-entrant 由於 all return a pointer to 同一個 static structure.
+ inet_pton and inet_ntop are always 可重入(re-entrant).
+ Historically(歷史緣由), inet_ntoa is not re-entrant, but some implementations(一些實現) that support threads provide a re-entrant version that uses thread-specific data(線程特定數據).
+ ……
+ 在信號處理函數中應儘可能不使用任何可重入函數
#include <netdb.h>
struct hostent *gethostbyname_r (const char *hostname, struct hostent *result, char *buf, int buflen, int *h_errnop) ;
struct hostent *gethostbyaddr_r (const char *addr, int len, int type, struct hostent *result, char *buf, int buflen, int *h_errnop) ;
Both return: non-null pointer if OK, NULL on error
守護進程:後臺啓動且不與任何控制終端關聯
#include <syslog.h>
void syslog(int priority, const char *message, ... );
syslog(LOG_INFO|LOG_LOCAL2, "rename(%s, %s): %m", file1, file2);
#include <syslog.h>
void openlog(const char *ident, int options, int facility);//首次調用syslog前調用
void closelog(void);//在進程不須要發送日誌消息時調用
/etc/inetd.conf
設置由inetd啓動的程序步驟:
+ 1.在/etc/services
文件中服務名和端口
+ 2.添加程序路徑:/etc/inetd.conf
,問題是找不到這個文件
+ 3.給inet發送sIGHUP信號
+ 4.日誌存放文件:/var/adm/messages(根據/etc/syslog.conf),這兩個文件也找不到
#include <sys/socket.h>
ssize_t recv(int sockfd, void *buff, size_t nbytes, int flags);
ssize_t send(int sockfd, const void *buff, size_t nbytes, int flags);
Both return: number of bytes read or written if OK, –1 on error
#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);
Both return: number of bytes read or written, –1 on error
struct iovec {
void *iov_base; /* starting address of buffer */
size_t iov_len; /* size of buffer */
};
#include <sys/socket.h>
ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags);
ssize_t sendmsg(int sockfd, struct msghdr *msg, int flags);
Both return: number of bytes read or written if OK, –1 on error
struct msghdr {
void *msg_name; /* protocol address,指向一個套接字的地址結構 */
socklen_t msg_namelen; /* size of protocol address */
struct iovec *msg_iov; /* scatter/gather array,指定輸出緩衝區數組*/
int msg_iovlen; /* # elements in msg_iov */
void *msg_control; /* ancillary data (cmsghdr struct),指定可選輔助數據 */
socklen_t msg_controllen; /* length of ancillary data */
int msg_flags; /* flags returned by recvmsg() */
};
struct msghdr msg;
struct cmsghdr *cmsgptr;
/* fill in msg structure */
/* call recvmsg() */
for (cmsgptr = CMSG_FIRSTHDR(&msg); cmsgptr != NULL;
cmsgptr = CMSG_NXTHDR(&msg, cmsgptr)) {
if (cmsgptr->cmsg_level == ... &&
cmsgptr->cmsg_type == ... ) {
u_char *ptr;
ptr = CMSG_DATA(cmsgptr);
/* process data pointed to by ptr */
}
}
獲悉已排隊的數據量:
- 1,使用非阻塞IO
- 2,使用MSG_PEEK標誌
- 3,ioctl的第三個參數
使用標準IO容易引發緩衝問題,能夠用setvbuf迫使變爲行緩衝
/dev/poll
#include <sys/types.h>
#include <sys/event.h>
#include <sys/time.h>
int kqueue(void);
int kevent(int kq, const struct kevent *changelist, int nchanges, struct kevent *eventlist, int nevents, const struct timespec *timeout) ;
void EV_SET(struct kevent *kev, uintptr_t ident, short filter, u_short flags, u_int fflags, intptr_t data, void *udata);
儘管總的來講應該避免編寫不可移植的代碼,然而對於一個任務繁重的網絡應用程序來講,使用各類可能得方式爲他在特定的主機上進行優化也至關廣泛