參數family指明協議族(family),該參數也往往被稱為協議域(domain)。所以有的書上聲明如下:
而且對于socket函數第一個參數,在不同書籍上可能會看到不同前綴的取值常量列表,如下兩圖所示:
AF_xxx與PF_xxx:
AF_前綴表地址族,PF_前綴表示協議族。歷史上曾有這樣的想法:單個協議族可以支持多個地址族,PF_值用來創建套接口,而AF_值用于套接口地址結構。但實際上,支持多個地址族的協議族從來就未實現過,而且頭文件<sys/socket.h>中為一給定協議定義的PF_值總是與此協議的AF_值相等。
2、connect函數(TCP客戶端調用)客戶在調用函數connect前不必非得調用bind函數,因為如果需要的話,內核會確定源ip地址,并選擇一個臨時端口作為源端口。
如果是TCP套接口,調用connect函數將激發TCP的三路握手過程,而且僅在連接建立成功或出錯時才返回,其中出錯返回可能有以下幾種情況:
(1)若TCP客戶沒有收到SYN分節的響應,則返回ETIMEDOUT錯誤。
(2)若對客戶的SYN的響應是RST(表示復位),則表明該服務器主機在我們指定的端口上沒有進程在等待與之連接(例如服務器進程也許沒在運行)。這是一種硬錯誤(hard error),客戶一接收到RST就馬上返回ECONNREFUSED錯誤。
(3)若客戶發出的SYN在中間的某個路由器上引發了一個“destination unreachable”(目的地不可達)ICMP錯誤,則認為是一種軟錯(soft error)。客戶主機內核保存該消息,并按一定時間間隔延遲重發SYN。若在某個規定的時間(4.4BSD規定為75秒)后仍未收到響應,則 把保存的消息(即ICMP錯誤)作為EHOSTUNREACH或ENETUNREACH錯誤返回給進程。
若connect失敗則該套接口不再可用,必須關閉,我們不能對這樣的套接口再次調用connect函數。當循環調用函數connect嘗試給定主機的各個IP地址直到有一個成功時,每次connect失敗后,都必須close當前的套接口描述字,重新調用socket。
3、bind函數(服務器調用、客戶端可以調用也可不調用)進程可以把一個特定的IP地址捆綁到它的套接口上,不過這個IP地址必須屬于其所在主機的網絡接口之一。對于TCP客戶,這就為在該套接口上發送的IP數據報指派了源IP地址。對于TCP服務器,這就限定該套接口只接收那些目的地為這個IP地址的客戶連接。
TCP客戶通常不把IP地址捆綁到它的套接口上。當連接套接口時,內核將根據所用外出網絡接口來選擇源IP地址,而所用外出接口則取決于到達服務器所需的路徑。
如果指定端口號為0,那么內核就在bind被調用時選擇一個臨時端口;
如果指定IP地址為通配地址,那么內核將等到套接口已連接(TCP)或已在套接口上發出數據報(UDP)時才選擇一個本地IP地址。
對于IPv4來說,通配地址由常值INADDR_ANY來指定,其值一般為0. 它告知內核去選擇IP地址。
無論是網絡字節序還是主機字節序,INADDR_ANY的值(為0)都一樣,因此使用htonl并非必須。不過既然頭文件<netinet/in.h>中定義的所有INADDR_常值都是按照主機字節序定義的,我們應該對所有這些常值都使用htonl。
4、listen函數(TCP服務器調用)listen函數做兩件事情:
(1)當socket函數創建一個套接口時,它被假設為一個主動套接口,也就是說,它是一個將調用connect發起連接的客戶套接口。listen函數把一個未連接的套接口轉換成一個被動套接口,指示內核應該接受指向該套接口的連接請求。調用listen導致套接口從CLOSED狀態轉換到LISTEN狀態。
(2)backlog參數規定了內核應該為相應套接口排隊的最大連接個數。
為了理解其中的backlog參數,我們必須認識到內核為任何一個給定的監聽套接口維護兩個隊列:
(1)未完成連接隊列(incomplete connection queue),每個這樣的SYN分節對應其中一項:已由客戶發出并到達服務器,而服務器正在等待完成相應的TCP三路握手過程。這些套接口處于SYN_RCVD狀態。
(2)已完成連接隊列(completed connection queue),每個已完成TCP三路握手過程的客戶對應其中一項。這些套接口處于ESTABLISHED狀態。
當進程調用accept時,已完成連接隊列中的隊頭項將返回給進程,或者如果該隊列為空,那么進程將被投入睡眠,直到TCP在該隊列中放入一項才喚醒它。
不要把backlog定義為0,因為不同的實現對此有不同的解釋。如果不想讓客戶連接到你的監聽套接口上,那就關掉該監聽套接口。
5、accept函數(TCP服務器調用)accept函數由TCP服務器調用,用于從已完成連接隊列隊頭返回下一個已完成連接。如果已完成連接隊列為空,那么進程被投入睡眠(假定套接口為缺省的阻塞方式)。
參數cliaddr和addrlen用來返回已連接的對端進程(客戶)的協議地址。
如果accept成功,那么其返回值是由內核自動生成的一個全新描述字,代表與所返回客戶的TCP連接。
在討論accept函數時,我們稱它的第一個參數為監聽套接口(listening socket)描述字(由socket創建,隨后用作bind和listen的第一個參數的描述字),稱它的返回值為已連接套接口(connected socket)描述字。區分這兩個套接口非常重要。一個服務器通常僅僅創建一個監聽套接口,它在服務器的生命期內一直存在。內核為每個由服務器進程接受的客戶連接創建一個已連接套接口(也就是說對于它的TCP三路握手過程已經完成)。當服務器完成對于某個給定客戶的服務時,相應的已連接套接口就被關閉。
本函數最多返回三個值:一個既可能是新套接口描述字也可能是出錯指示的整數、客戶進程的協議地址(由cliaddr指針所指)以及該地址的大?。ㄓ蒩ddrlen指針所指)。如果我們對返回客戶協議地址不感興趣,那么可以把cliaddr和addrlen均設置為空指針。
6、fork和exec函數(構建并發服務器)fork函數:http://www.CUOXin.com/nufangrensheng/p/3509492.html。
exec函數:http://www.CUOXin.com/nufangrensheng/p/3510821.html。
7、close函數UNIX通常的close函數也用來關閉套接口,并終止TCP連接。
close一個TCP套接口的缺省行為是把該套接口標記成已關閉,然后立即返回到調用進程。該套接口描述字不能再由調用進程使用,也就是說它不能再作為read或write的第一個參數。然而TCP將嘗試發送已排隊等待發送到對端的任何數據,發送完畢后發生的是正常的TCP連接終止序列。
8、getsockname和getpeername函數getsockname:返回與某個套接口關聯的本地協議地址。
getpeername:返回與某個套接口關聯的遠地協議地址。
這兩個函數返回與某個網絡連接的兩端中任何一端相關聯的協議地址,對于IPv4和IPv6來說,就是IP地址和端口號的組合。
需要這兩個函數的理由如下:
(1)在沒有調用bind的TCP客戶上,connect成功返回后,getsockname用于返回由內核賦予該連接的本地IP地址和本地端口號。
(2)在以端口號0調用bind(告知內核去選擇本地端口號)后,getsockname用于返回由內核賦予的本地端口號。
(3)getsockname可用于獲取某個套接口的地址族。
(4)在一個以通配IP地址調用bind之后的TCP服務器上,與某個客戶的連接一旦建立(accept成功返回),getsockname就可以用于返回由內核賦予該連接的本地IP地址。在這樣的調用中,套接口描述字必須是已連接套接口的描述字,而不是監聽套接口的描述字。
(5)當一個服務器是由調用過accept的某個進程通過調用exec更換了程序時,它能夠獲取客戶身份的唯一途徑便是調用getpeername。
總結:所有客戶和服務器都從調用socket開始,它返回一個套接口描述字。客戶隨后調用connect,服務器則調用bind、listen和accept。套接口通常使用標準的close函數關閉。
新聞熱點
疑難解答