套接字位址結構
typedef uint32_t in_addr_t;
struct in_addr
;struct sockaddr_in
;
在使用結構前總是把整個結構置零,是由於sin_zero欄位沒有任何作用,只需置零即可。
為了是套接字函式在處理位址時具有一定的通用性,套接字函式還定義了通用套接字位址結構,定義位於/usr/include/x86_64-linux-gnu/bits/socket.h
struct sockaddr
;
通過上述定義可以發現通用位址結構就是通過字元陣列進行佔位,但對於其中的資料型別並沒有進行規定。
由於網路中的計算機體系結構不同,這些不同的體系結構之間乙個比較明顯的區別就在於其「位元組序」不同,有關於位元組序的詳細內容在此也就不深入分析了。但不同的機器之間位元組序不同,造成了機器之間資訊不能直接傳輸。其實不同位元組序的機器的資料轉換也很簡單,只要根據不同的型別逐字節反轉就行了。不過這種轉換函式庫函式已經幫我們實現好了,定義如下,還是位於/usr/include/netinet/in.h中
extern uint32_t ntohl (uint32_t __netlong) __throw __attribute__ ((__const__));
extern uint16_t ntohs (uint16_t __netshort)
__throw __attribute__ ((__const__));
extern uint32_t htonl (uint32_t __hostlong)
__throw __attribute__ ((__const__));
extern uint16_t htons (uint16_t __hostshort)
__throw __attribute__ ((__const__));
讓我們仔細分析一下這幾個函式,其中n(network)代表網路位元組序(大端),h(host)代表主機位元組序(與機器具體情況相關)。l代表long(例如ipv4位址),s代表short(例如tcp、udp的埠號)。以ntohl為例,這個函式的功能就是將ipv4(此處以ipv4位址為例)由網路位元組序轉換為主機位元組序。
在套接字位址結構中存放的位址是uint_32型別的,但我們常用的位址描述方式是點分十進位制,因此套接字給出了在點分十進位制與套接字位址結構之間的轉換函式。
#include extern int inet_aton (const char *__cp, struct in_addr *__inp) __throw; //成功返回1,失敗返回0
extern in_addr_t inet_addr (const char *__cp) __throw; //返回值為32位的網路位元組序二進位制值
以上兩個函式用於將點分十進位制字串轉換為套接字位址結構。
同樣存在功能相反的函式,即將網路位元組序轉換為點分十進位制字串
#include extern char *inet_ntoa (struct in_addr __in) __throw;
上述三個函式僅適用於轉換ipv4位址,並不適用於ipv6位址,因此套接字函式定義了更加通用的函式。
#include extern int inet_pton (int __af, const char *__restrict __cp,
void *__restrict __buf) __throw;
extern const char *inet_ntop (int __af, const void *__restrict __cp,
char *__restrict __buf, socklen_t __len)
__throw;
為了執行網路i/o,乙個程序必須做的第一件事就是呼叫socket函式。
#include extern int socket (int __domain, int __type, int __protocol) __throw;
其中__domin代表協議族,可能的取值有af_inet(ipv4協議族),af_inet6(ipv6協議族)。
__type代表套接字型別,可能的取值有sock_stream(位元組流套接字),sock_dgram(資料報套接字)。
__protocol應設為某個型別常值,或直接設為0,以選擇所給定的__domain與__type組合的系統預設值。
這裡還要提一點的就是在我的機器上af_***與pf_***定義是相同的,相關定義位於/usr/include/x86_64-linux-gnu/bits/socket.h檔案中。
tcp客戶用connect函式來建立與tcp伺服器的連線。呼叫connect函式將激發tcp的三路握手過程。
#include extern int connect (int __fd, __const_sockaddr_arg __addr, socklen_t __len);
其中出錯返回可能有以下幾種情況
若tcp沒有收到syn分節的響應,則返回etimedout錯誤。
若對客戶的syn的響應是rst(表示復位),則表明該伺服器主機在我們指定的埠上沒有程序在等待與之連線。這是一種硬錯誤(hard error)。客戶一收到rst就馬上返回econnrefused錯誤。
若客戶發出的syn在中間的某個路由器上引發了乙個「destination unreachable」(目的地不可達)icmp錯誤。這是一種軟錯誤(soft error)。客戶主機核心先儲存該錯誤,並等待一段時間後繼續傳送syn,若在某個規定的時間後仍未收到響應,則把儲存的訊息(即icmp錯誤)作為ehostunreach或enetunreach錯誤返回給程序。
若connect失敗則該套接字不再可用,必須關閉,我們不能對這樣的套接字再次呼叫connect函式。在每次connect函式失敗後,都必須close當前的套接字描述符並重新呼叫socket。
bind函式把乙個本地協議位址賦予乙個套接字。
#include extern int bind (int __fd, __const_sockaddr_arg __addr, socklen_t __len)
__throw;
若tcp客戶呼叫bind函式,就為在該套接字上傳送的ip資料報指派了源ip位址。
若tcp服務呼叫bind函式,就限定該套接字只接收那些目的地為這個ip位址的客戶連線。
如果指定埠號為0,那麼核心就在bind被呼叫時選擇乙個臨時埠。
如果指定ip位址為通配位址,那麼核心將等到套接字已連線或已在套接字上發出資料報時才選擇乙個本地ip位址。
listen函式僅用於tcp伺服器呼叫,listen函式把乙個未連線套接字轉換為乙個被動套接字,指示核心應接受指向該套接字的連線請求,呼叫listen導致套接字從closed轉換為listen狀態。
函式原型如下:
#include extern int listen (int __fd, int __n) __throw;
核心為乙個給定的監聽套接字維護兩個佇列:
未完成佇列:已由某個客戶發出並到達伺服器,而伺服器正在等待完成相應的tcp三路握手過程,這些套接字處於syn_recv狀態。
已完成佇列:每個已完成的tcp三路握手過程的客戶對應其中一項。這些套接字處於established狀態。
以上兩個佇列的和不超過引數_n的和。
#include extern int accept (int __fd, __sockaddr_arg __addr,
socklen_t *__restrict __addr_len);
若accept函式成功,則返回乙個全新的描述符,這一描述符被稱為已連線套接字。而accept函式中的__fd被稱為監聽套接字。
這裡對於accept函式還有乙個概念——「呼入連線請求佇列」。在伺服器端中有乙個固定長度的連線佇列,該佇列中的連線已被tcp接受(即三次握手已經完成),但還沒有被應用層接受,這個佇列就是「呼入連線請求佇列」。注意區分tcp接受乙個連線是將其放入這個佇列,而應用層接受連線是將其從該佇列中移出。應用層將指明該佇列的最大長度,這個通常被稱為積壓值(backlog)。
對於新的連線請求,該tcp監聽的端點的連線佇列中還有空間,tcp模組將對該syn進行確認並完成連線的建立。
close函式用於關閉套接字,並終止tcp連線。
#include extern int close (int __fd);
對於併發伺服器而言,frok返回後在程序的位址空間中同時存在兩個套接字,分別是監聽套接字與已連線套接字,這兩個套接字中已連線套接字一定處於established狀態,但監聽套接字的狀態不是非常確定。父程序首先會關閉已連線套接字,但close函式只是使描述符引用計數減1,但此時子程序的已連線套接字並未關閉,因此不會引發tcp的連線終止序列。監聽套接字的情況類似。
Unix網路程式設計讀書筆記(四)
unix下共有5種i o模型 阻塞式i o 非阻塞式i o i o復用 select和poll 訊號驅動式i o sigio 非同步i o posix的aio 系列函式 同步i o與非同步i o 同步i o操作導致請求程序阻塞,直到i o操作完成。前4種模型都是同步i o模型,因為其中真正的i o操...
Unix網路程式設計讀書筆記(五)
使用udp編寫的一些常見的應用程式有 dns 網域名稱系統 nfs 網路檔案系統 和snmp 簡單網路關係協議 udp協議的通訊函式分別是sendto與recvfrom函式,函式原型如下 include extern ssize t recvfrom int fd,void restrict buf...
讀書筆記 unix網路程式設計 20170703
終於看完了unix網路程式設計卷一,其實不能說看完了,只能說溜了一遍,中間有好多不明白的,還需要繼續理解。這裡寫一下發現的疑問,可能有些不是書上的,是其他地方發現的,也一起寫到這裡了 筆記內容 1 i o復用典型使用在下列網路應用場合 122頁 a 當客戶處理多個描述符 b 乙個客戶同時處理多個套接...