一般說函數返回某個錯誤值,其實是函數返回值爲-1,而全局變量errno被置爲指定的常值(即稱函數返回這個錯誤值)。linux
exit終止進程,Unix在一個進程終止時老是關閉該進程全部打開的描述符。數組
TCP三次握手完畢,accept返回,其返回值是一個稱爲已鏈接描述符(connected descriptor)的新描述符。調用close關閉與客戶的鏈接。服務器
套接字API。cookie
TCP狀態轉移圖,11種狀態。網絡
MSS,maximum segment size,最大分節大小。一般客戶端主動打開時發送SYN的同時會發送MSS,服務端應答(SYN、ACK、MSS)時一樣也發送一個MSS,不一樣方向上MSS能夠不一樣。app
SCTP多宿特性,單個SCTP端點能支持多個IP地址。dom
SCTP的四路握手。驗證標記、狀態cookie。socket
IPv4數據報最大長度65535,包括IP首部,由於總長度字段是16位;IPv6數據報最大長度65575,包括40字節IPv6首部,由於淨長度字段是16位。tcp
IPv4首部20字節(固定長度),另外還有最多40字節可選部分。ide
套接字地址結構在內核和進程間傳遞,從進程傳到內核,從內核傳到進程,不是很是理解,應該就是參數傳遞進去和被寫回吧?另外有一個值-結果參數之前不多碰到。
每一個文件或套接字都有一個引用計數,引用計數在文件表項中維護。
存放在硬盤上的可執行程序文件能被Unix執行的惟一方法是:由一個現有進程調用六個exec函數中的一個。(理解)
六個exec函數區別在於:待執行程序由文件名仍是路徑名指定;參數是一一列出仍是指針數組;把調用進程環境傳遞給新程序仍是指定新環境。
子進程調用exec時,子進程內存映像被替換成新程序文件,只有文件描述符(在socket中是已鏈接套接字描述符)跨exec繼續保持開放。
一個簡單echo客戶/服務器程序涉及到的一些細節(很是重要):
僵死子進程,經過捕獲SIGCHLD信號加以處理;
信號處理函數必須調用waitpid而不是wait,由於unix信號是不排隊的,當有多個子進程時wait函數只能處理第一個,後面的將變成僵死進程;
另外一個問題是服務器進程終止時,客戶進程沒被告知,由於客戶進程阻塞於等待用戶輸入而未接收到通知,這須要select或poll函數來處理,它們等待多個描述符中的任何一個就緒而不是阻塞於某個描述符。
select函數中間的三個參數(讀、寫、異常描述符集)都是值—結果參數,返回時,未就緒描述符對應的被置0,其餘是1,從新調用時,將所關心位重置1。
close函數有兩個限制,shutdown函數能夠克服,它關閉一半tcp鏈接(可參數選讀或者寫)。
pselect時間參數結構不一樣;另外一個不一樣點是多了一個指向信號掩碼的指針,從而運行程序先禁止提交某些信號。
拒絕服務(denial of service,DoS)型攻擊。
UDP也可使用connect函數,成爲已鏈接UDP套接字,而後可使用write和read函數讀寫,而不是sento和recvfrom。
DNS中的條目稱爲資源記錄(resource record,RR)。常見RR類型有:A、AAAA、PTR、MX、CNAME。
gethostbyname和gehostbyaddr的返回值均爲一個hostent(host entry縮寫)結構:
struct hostent { const char *h_name; // 主機的正式名稱。
char **h_aliases; // 空字節-地址的預備名稱的指針。
short h_addrtype; // 地址類型; 一般是AF_INET。
short h_length; // 地址的比特長度
char **h_addr_list; // 主機網絡地址指針(只有IPV4)
#define h_addr h_addr_list[0] // h_addr 爲 h_addr_list中的第一地址。 };
gethostbyname只能返回IPV4地址,而getaddrinfo可以同時處理IPV4和IPV6地址。
gethostbyname和gehostbyaddr用於主機名和地址的映射,而getservbyname和getservbyport則用於服務與端口之間的映射。
getaddrinfo能處理名字到地址、服務到端口的轉換(由參數中列表中第一個爲主機名,第二個爲服務名可看出):
int getaddrinfo( const char *hostname, const char *service, const struct addrinfo *hints, struct addrinfo **result );//返回結果保存到這裏
其中,保存返回結果的struct addrinfo結構體詳細信息以下:
表頭文件: #include<netdb.h> struct addrinfo { int ai_flags; int ai_family; //AF_INET,AF_INET6,UNIX etc int ai_socktype; //STREAM,DATAGRAM,RAW int ai_protocol; //IPPROTO_IP, IPPROTO_IPV4, IPPROTO_IPV6 etc size_t ai_addrlen;//length of ai_addr char* ai_canonname; //full hostname struct sockaddr* ai_addr; //addr of host struct addrinfo* ai_next; } value of ai_falgs: AI_PASSIVE: Socket address is intended for `bind'. AI_CANONNAME:Request for canonical name. AI_NUMERICHOST: Don't use name resolution. AI_V4MAPPED: IPv4 mapped addresses are acceptable. AI_ALL: Return IPv4 mapped and IPv6 addresses. AI_ADDRCONFIG:Use configuration of this host to choose 定義函數: int getaddrinfo( const char *hostname, const char *service, const struct addrinfo *hints, struct addrinfo **result ); 函數說明: getaddrinfo函數可以處理名字到地址以及服務到端口這兩種轉換,返回的是一個sockaddr 結構的鏈而 不是一個地址清單。它具備協議無關性。 hostname:一個主機名或者地址串(IPv4的點分十進制串或者IPv6的16進制串) service:一個服務名或者10進制端口號數串。 hints:能夠是一個空指針,也能夠是一個指向某個addrinfo結構的指針,調用者在這個結構中填入關於指望返回的信息類型的暗示。舉例來講:若是指定的服務既支持TCP也支持UDP,那麼調用者能夠把hints結構中的ai_socktype成員設置成SOCK_DGRAM使得返回的僅僅是適用於數據報套接口的信息。返回0: 成功,返回非0: 出錯。 定義函數:const char *gai_strerror( int error ); 函數說明: 該函數以getaddrinfo返回的非0錯誤值的名字和含義爲他的惟一參數,返回一個指向對應的出錯信息串的指針。 定義函數: void freeaddrinfo( struct addrinfo *ai ); 函數說明: 由getaddrinfo返回的全部存儲空間都是動態獲取的,這些存儲空間必須經過調用freeaddrinfo返回給系統。
與getaddrinfo函數對應的是getnameinfo函數。
在linux環境下,結構體struct sockaddr在/usr/include/linux/socket.h中定義,具體以下: typedef unsigned short sa_family_t; struct sockaddr { sa_family_t sa_family; /* address family, AF_xxx */ char sa_data[14]; /* 14 bytes of protocol address */ 在linux環境下,結構體struct sockaddr_in在/usr/include/netinet/in.h中定義,具體以下: /* Structure describing an Internet socket address. */ 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)]; /* 字符數組sin_zero[8]的存在是爲了保證結構體struct sockaddr_in的大小和結構體struct sockaddr的大小相等 */ }; struct sockaddr是通用的套接字地址,而struct sockaddr_in則是internet環境下套接字的地址形式,兩者長度同樣,都是16個字節。兩者是並列結構,指向sockaddr_in結構的指針也能夠指向sockaddr。通常狀況下,須要把sockaddr_in結構強制轉換成sockaddr結構再傳入系統調用函數中。 下面是struct sockaddr_in中用到兩個數據類型,具體定義以下: /* Type to represent a port. */ typedef uint16_t in_port_t; struct in_addr其實就是32位IP地址 struct in_addr { unsigned long s_addr; }; BSD網絡軟件中包含了兩個函數,用來在二進制地址格式和點分十進制字符串格式之間相互轉換,可是這兩個函數僅僅支持IPv4。 in_addr_t inet_addr(const char *cp); char *inet_ntoa(struct in_addr in); 功能類似的兩個函數同時支持IPv4和IPv6 const char *inet_ntop(int domain, const void *addr, char *str, socklen_t size); int inet_pton(int domain, const char *str, void *addr); 一般的用法是: int sockfd; struct sockaddr_in my_addr; sockfd = socket(AF_INET, SOCK_STREAM, 0); my_addr.sin_family = AF_INET; /* 主機字節序 */ my_addr.sin_port = htons(MYPORT); /* short, 網絡字節序 */ my_addr.sin_addr.s_addr = inet_addr("192.168.0.1"); bzero(&(my_addr.sin_zero), 8); /* zero the rest of the struct */ //memset(&my_addr.sin_zero, 0, 8); bind(sockfd, (struct sockaddr *)&my_addr, sizeof(struct sockaddr)); #define UNIX_PATH_MAX 108 struct sockaddr_un { sa_family_t sun_family; /*PF_UNIX或AF_UNIX */ char sun_path[UNIX_PATH_MAX]; /* 路徑名 */ }; struct sockaddr結構類型是用來保存socket信息的: struct sockaddr { unsigned short sa_family; /* 地址族, AF_xxx */——地址的格式 char sa_data[14]; /* 14 字節的協議地址 */——地址值(IP和端口號) }; Sockfd是調用socket函數返回的socket描述符,my_addr是一個指向包含有本機IP地址及端口號等信息的sockaddr類型的指針;addrlen常被設置爲sizeof(struct sockaddr)。 struct sockaddr結構類型是用來保存socket信息的: struct sockaddr { unsigned short sa_family; /* 地址族, AF_xxx */ char sa_data[14]; /* 14 字節的協議地址 */ }; sa_family通常爲AF_INET,表明Internet(TCP/IP)地址族;sa_data則包含該socket的IP地址和端口號。 另外還有一種結構類型: struct sockaddr_in { short int sin_family; /* 地址族 */ unsigned short int sin_port; /* 端口號 */ struct in_addr sin_addr; /* IP地址 */ unsigned char sin_zero[8]; /* 填充0 以保持與struct sockaddr一樣大小 */ }; 這個結構更方便使用。sin_zero用來將sockaddr_in結構填充到與struct sockaddr一樣的長度,能夠用bzero()或memset()函數將其置爲零。指向sockaddr_in 的指針和指向sockaddr的指針能夠相互轉換,這意味着若是一個函數所需參數類型是sockaddr時,你能夠在函數調用的時候將一個指向 sockaddr_in的指針轉換爲指向sockaddr的指針;或者相反。 你只要記住,填值的時候使用sockaddr_in結構,而做爲函數的 參數傳入的時候轉換成sockaddr結構就好了,畢竟都是16個字符 長。 struct in_addr { union { struct { u_char s_b1,s_b2,s_b3,s_b4; } S_un_b; struct { u_short s_w1,s_w2; } S_un_w; u_long S_addr; } S_un };
inet_pton函數將點分十進制格式IP地址轉換爲二進制整數,IPV4和IPV6都支持:
inet_pton:將「點分十進制」 -> 「二進制整數」 int inet_pton(int af, const char *src, void *dst); 這個函數轉換字符串到網絡地址,第一個參數af是地址簇,第二個參數*src是來源地址,第三個參數* dst接收轉換後的數據。 inet_pton 是inet_addr的擴展,支持的多地址族有下列: af = AF_INET src爲指向字符型的地址,即ASCII的地址的首地址(ddd.ddd.ddd.ddd格式的),函數將該地址轉換爲in_addr的結構體,並複製在*dst中。 af = AF_INET6 src爲指向IPV6的地址,函數將該地址轉換爲in6_addr的結構體,並複製在*dst中。 若是函數出錯將返回一個負值,並將errno設置爲EAFNOSUPPORT,若是參數af指定的地址族和src格式不對,函數將返回0。