計算機通訊過程
兩臺計算機通過TCP/IP協(xié)議通訊的過程如下所示:
上圖對應兩臺計算機在同一網(wǎng)段中的情況,如果兩臺計算機在不同的網(wǎng)段中,那么數(shù)據(jù)從一臺計算機到另一臺計算機傳輸過程中要經(jīng)過一個或多個路由器,如下圖所示:
鏈路層有以太網(wǎng)、令牌環(huán)網(wǎng)等標準,鏈路層負責網(wǎng)卡設備的驅動、幀同步(即從網(wǎng)線上檢測到什么信號算作新幀的開始)、沖突檢測(如果檢測到?jīng)_突就自動重發(fā))、數(shù)據(jù)差錯校驗等工作。交換機是工作在鏈路層的網(wǎng)絡設備,可以在不同的鏈路層網(wǎng)絡之間轉發(fā)數(shù)據(jù)幀(比如十兆以太網(wǎng)和百兆以太網(wǎng)之間、以太網(wǎng)和令牌環(huán)網(wǎng)之間),由于不同鏈路層的幀格式不同,交換機要將進來的數(shù)據(jù)包拆掉鏈路層首部重新封裝之后再轉發(fā)。
網(wǎng)絡層的IP協(xié)議是構成Internet的基礎。Internet上的主機通過IP地址來標識,Inter-net上有大量路由器負責根據(jù)IP地址選擇合適的路徑轉發(fā)數(shù)據(jù)包,數(shù)據(jù)包從Internet上的源主機到目的主機往往要經(jīng)過十多個路由器。路由器是工作在第三層的網(wǎng)絡設備,同時兼有交換機的功能,可以在不同的鏈路層接口之間轉發(fā)數(shù)據(jù)包,因此路由器需要將進來的數(shù)據(jù)包拆掉網(wǎng)絡層和鏈路層兩層首部并重新封裝。IP協(xié)議不保證傳輸?shù)目煽啃裕瑪?shù)據(jù)包在傳輸過程中可能丟失,可靠性可以在上層協(xié)議或應用程序中提供支持。
網(wǎng)絡層負責點到點(ptop,point-to-point)的傳輸(這里的“點”指主機或路由器),而傳輸層負責端到端(etoe,end-to-end)的傳輸(這里的“端”指源主機和目的主機)。傳輸層可選擇TCP或UDP協(xié)議。
TCP是一種面向連接的、可靠的協(xié)議,有點像打電話,雙方拿起電話互通身份之后就建立了連接,然后說話就行了,這邊說的話那邊保證聽得到,并且是按說話的順序聽到的,說完話掛機斷開連接。也就是說TCP傳輸?shù)碾p方需要首先建立連接,之后由TCP協(xié)議保證數(shù)據(jù)收發(fā)的可靠性,丟失的數(shù)據(jù)包自動重發(fā),上層應用程序收到的總是可靠的數(shù)據(jù)流,通訊之后關閉連接。
UDP是無連接的傳輸協(xié)議,不保證可靠性,有點像寄信,信寫好放到郵筒里,既不能保證信件在郵遞過程中不會丟失,也不能保證信件寄送順序。使用UDP協(xié)議的應用程序需要自己完成丟包重發(fā)、消息排序等工作。
目的主機收到數(shù)據(jù)包后,如何經(jīng)過各層協(xié)議棧最后到達應用程序呢?其過程如下圖所示:
以太網(wǎng)驅動程序首先根據(jù)以太網(wǎng)首部中的“上層協(xié)議”字段確定該數(shù)據(jù)幀的有效載荷(payload,指除去協(xié)議首部之外實際傳輸?shù)臄?shù)據(jù))是IP、ARP還是RARP協(xié)議的數(shù)據(jù)報,然后交給相應的協(xié)議處理。假如是IP數(shù)據(jù)報,IP協(xié)議再根據(jù)IP首部中的“上層協(xié)議”字段確定該數(shù)據(jù)報的有效載荷是TCP、UDP、ICMP還是IGMP,然后交給相應的協(xié)議處理。假如是TCP段或UDP段,TCP或UDP協(xié)議再根據(jù)TCP首部或UDP首部的“端口號”字段確定應該將應用層數(shù)據(jù)交給哪個用戶進程。IP地址是標識網(wǎng)絡中不同主機的地址,而端口號就是同一臺主機上標識不同進程的地址,IP地址和端口號合起來標識網(wǎng)絡中唯一的進程。
雖然IP、ARP和RARP數(shù)據(jù)報都需要以太網(wǎng)驅動程序來封裝成幀,但是從功能上劃分,ARP和RARP屬于鏈路層,IP屬于網(wǎng)絡層。雖然ICMP、IGMP、TCP、UDP的數(shù)據(jù)都需要IP協(xié)議來封裝成數(shù)據(jù)報,但是從功能上劃分,ICMP、IGMP與IP同屬于網(wǎng)絡層,TCP和UDP屬于傳輸層。
網(wǎng)絡編程基礎
什么是socket?
socket可以看成是用戶進程與內核網(wǎng)絡協(xié)議棧的編程接口。
socket不僅可以用于本機的進程間通信,還可以用于網(wǎng)絡上不同主機的進程間通信。
IPv4套接口地址結構通常也稱為“網(wǎng)際套接字地址結構”,它以“sockaddr_in”命名,定義在頭文件<netinet/in.h>中
struct?sockaddr_in?{
uint8_t? sin_len;?//4
sa_family_t? sin_family;?//4
in_port_t? sin_port;?//2
struct?in_addr? sin_addr;?//4
char?sin_zero[8];?//8
};?
sin_len:整個sockaddr_in結構體的長度,在4.3BSD-Reno版本之前的第一個成員是sin_family.
sin_family:指定該地址家族,在這里必須設為AF_INET
sin_port:端口
sin_addr:IPv4的地址;
sin_zero:暫不使用,一般將其設置為0
通用地址結構用來指定與套接字關聯(lián)的地址。
struct?sockaddr?{
??uint8_t? sin_len;
??sa_family_t? sin_family;
??char?sa_data[14];?//14
};?
sin_len:整個sockaddr結構體的長度
sin_family:指定該地址家族
sa_data:由sin_family決定它的形式。
網(wǎng)絡字節(jié)序
大端字節(jié)序(Big Endian)
最高有效位(MSB:Most Significant Bit)存儲于最低內存地址處,最低有效位(LSB:Lowest Significant Bit)存儲于最高內存地址處。
小端字節(jié)序(Little Endian)
最高有效位(MSB:Most Significant Bit)存儲于最高內存地址 處,最低有效位(LSB:Lowest Significant Bit)存儲于最低內存地址處。
主機字節(jié)序
不同的主機有不同的字節(jié)序,如x86為小端字節(jié)序,Motorola 6800為大端字節(jié)序,ARM字節(jié)序是可配置的。
網(wǎng)絡字節(jié)序
網(wǎng)絡字節(jié)序規(guī)定為大端字節(jié)序。
字節(jié)序轉換函數(shù)
uint32_t?htonl(uint32_t?hostlong);
uint16_t?htons(uint16_t?hostshort);
uint32_t?ntohl(uint32_t?netlong);
uint16_t?ntohs(uint16_t?netshort);
說明:在上述的函數(shù)中,h代表host;n代表network s代表short;l代表long。
地址轉換函數(shù)
#include?<netinet/in.h>
#include?<arpa/inet.h>
int?inet_aton(const?char?*cp,?struct?in_addr *inp);
in_addr_t?inet_addr(const?char?*cp);
char?*inet_ntoa(struct?in_addr in);
套接字類型
流式套接字(SOCK_STREAM)
提供面向連接的、可靠的數(shù)據(jù)傳輸服務,數(shù)據(jù)無差錯,無重復的發(fā)送,且按發(fā)送順序接收。
數(shù)據(jù)報式套接字(SOCK_DGRAM)
提供無連接服務。不提供無錯保證,數(shù)據(jù)可能丟失或重復,并且接收順序混亂。