協(xié)議
為進行網(wǎng)絡中的數(shù)據(jù)交換而建立的規(guī)則、標準或約定(=語義+語法+規(guī)則)。
不同層具有不同的協(xié)議
OSI/ISO七層參考模型
OSI(Open Systern Interconnection)參考模型將網(wǎng)絡的不同功能劃分為7層。
?????????????????? 分層???????????????????????????????????????????????????????????????????????????????????????????????????協(xié)議
應用層?????????————處理網(wǎng)絡應用????????????? 遠程登錄協(xié)議Telnet、文件傳輸協(xié)議FTP?、
????????????????????????????????????????????????????????????????????????超文本傳輸協(xié)議HTTP、域名服務DNS、
????????????????????????????????????????????????????????????????????????簡單郵件傳輸協(xié)議SMTP、郵局協(xié)議POP3等
表示層???????? ————數(shù)據(jù)表示
會話層??????? ?————?主機間通信
傳輸層????????————端到端的連接?????????????傳輸控制協(xié)議TCP(面向連接的可靠連接協(xié)議)、
?????????????????????????????????????????????????????????????????用戶數(shù)據(jù)報協(xié)議UDP?(無連接的不可靠連接協(xié)議)??????????????????????????
網(wǎng)絡層???????————尋址和最短路徑??????????????網(wǎng)際協(xié)議IP、Internet互聯(lián)網(wǎng)控制報文協(xié)議ICMP、
??????????????????????????????????????????????????????????????????????????????Internet組管理協(xié)議IGMP
數(shù)據(jù)鏈路層?————?介質(zhì)訪問(接入)
物理層?????? ??————二進制傳輸
通信實體的對等層之間不允許直接通信。
各層之間是嚴格單向依賴。
上層(Server User)使用下層提供的服務。
下層(Server? Provider)向上層提供服務。
對等層通信的實質(zhì):
對等層之間虛擬通信。
下層向上層提供服務,實際通信在最底層完成。
數(shù)據(jù)封裝
一臺計算機要發(fā)送數(shù)據(jù)到另一臺計算機,數(shù)據(jù)首先必須打包,
打包的過程稱為封裝。
封裝就是在數(shù)據(jù)前面加上特定的協(xié)議頭部,或者在數(shù)據(jù)后面加上特定的協(xié)議尾部。
OSI參考模型中。對等層協(xié)議之間交換的信息單元統(tǒng)稱為協(xié)議數(shù)據(jù)單元(PDU,Protocal? Data Unit)
OSI參考模型中每一層都要依靠下一層提供的服務。
為了提供服務,下層把上層的PDU作為本層的數(shù)據(jù)來封裝,然后加入本層的頭部(和尾部)。
頭部中含有完成數(shù)據(jù)傳輸所需的控制信息。
這樣,數(shù)據(jù)自上而下遞交的過程實際上就是不斷封裝的過程。
到達目的地后自下而上遞交的過程,就是不斷拆封的過程。
由此可知,在物理線路上傳輸?shù)臄?shù)據(jù),其外在實際上被包封了多層“信封”。
但是,某一層只能識別由對等層封裝的“信封”,
而對于被封裝在“信封”內(nèi)部的數(shù)據(jù)僅僅是拆封后將其提交給上層,
本層不做任何處理。
TCP/IP模型
TCP/IP模型包含4個層次:
?應用層
?傳輸層
?網(wǎng)絡層
?網(wǎng)絡接口
TCP/IP與OSI參考模型的對應關系
TCP/IP模型??????????????????????????????????????????????????????????????????????????????????????????OSI模型
?應用層????????????????????????????????????????????????????????????????????????????????????????????????????應用層
??????????????????????????????????????????????????????????????????????????????????????????????????????????????????表示層
??????????????????????????????????????????????????????????????????????????????????????????????????????????????????會話層
?傳輸層?????????????????????????????????????????????????????????????????????????????????????????????????????傳輸層
?網(wǎng)絡層?????????????????????????????????????????????????????????????????????????????????????????????????????網(wǎng)絡層
?網(wǎng)絡接口????????????????????????????????????????????????????????????????????????????????????????????????數(shù)據(jù)鏈路層
??????????????????????????????????????????????????????????????????????????????????????????????????????????????????物理層
端口
按照OSI七層模型的描述,傳輸層提供進程(應用程序)通信的能力。
為了能標示通信實體中進行通信的進程,TCP/IP協(xié)議提出了協(xié)議端口(protocal port,簡稱端口)的概念。
端口是一種抽象的軟件結構(包括一些數(shù)據(jù)結構和I/O緩沖區(qū))。
應用程序通過系統(tǒng)調(diào)用與某端口建立連接(binding)后,傳輸層傳給該端口的數(shù)據(jù)都被相應的進程所接收,
相應進程發(fā)給傳輸層的數(shù)據(jù)都通過該端口輸出。
端口用一個整數(shù)型標識符來表示,即端口號。
端口號和協(xié)議相關,TCP/IP傳輸層的2個協(xié)議TCP和UDP是完全獨立的2個軟件模塊,因此各自的端口號相互獨立。
端口使用一個16位的數(shù)字來表示,它的范圍是0~65535
數(shù)字比1024小的端口保留給預定義的服務。例如:http使用80端口。
套接字(socket)的引入
為了能方便的開發(fā)網(wǎng)絡應用軟件,由美國伯克利大學在Unix上推出了一種應用程序訪問通信協(xié)議的操作系統(tǒng)調(diào)用socket(套接字)。socket的出現(xiàn),讓程序員可以很方便的訪問TCP/IP,從而開發(fā)各種網(wǎng)絡應用程序。
隨著Unix的應用推廣,套接字在編寫網(wǎng)絡軟件中得到了極大的普及。
后來,套接字又被引入到Windows等操作系統(tǒng),成為開發(fā)網(wǎng)絡應用程序的非常有效快捷的工具。
套接字存在于通信區(qū)域中。
通信區(qū)域也叫地址族,它是一個抽象的概念,主要用于將通過套接字通信的進程的共有特性綜合在一起。
套接字通常只和同一區(qū)域的套接字交換數(shù)據(jù)(也可能跨區(qū)域通信,但只在執(zhí)行了某種轉(zhuǎn)換進程后才可以)。
Windows? Sockets只支持一個通信區(qū)域:網(wǎng)際域(AF_INET),這個域被使用網(wǎng)際協(xié)議簇通信的進程使用。
網(wǎng)絡字節(jié)順序
不同的計算機存放多字節(jié)值的順序不同,
有的機器在起始地址存放低位字節(jié)(低位先存),比如基于Inter的CPU,即我們常用的PC機。
有的機器在起始地址存放高位字節(jié)(高位先存)。
為保證傳輸數(shù)據(jù)的正確性,在網(wǎng)絡協(xié)議中需指定網(wǎng)絡字節(jié)順序。
TCP/IP協(xié)議使用16位整數(shù)和32位整數(shù)的高位先存格式。
客戶機/服務器模式
在TCP/IP網(wǎng)絡應用中,通信的2個進程間相互作用的主要模式是客戶機/服務器模式(client/server),
即客戶向服務器提出請求,服務器接收到請求后,提供相應的服務。
客戶機/服務器模式的建立基于2點:
1。建立網(wǎng)絡的原因是網(wǎng)絡中軟硬件資源、運算能力和信息不對稱等,需要共享,
從而造就擁有眾多資源的主機提供服務,資源較少的客戶請求服務這一非對等作用。
2.網(wǎng)間進程通信完全是異步的,相互通信的進程間即不存在父子關系,又不共享內(nèi)存緩沖區(qū),
因此需要一種機制為希望通信的進程建立聯(lián)系,為2者的數(shù)據(jù)交換提供同步,
這就是基于客戶機/服務器模式的TCP/IP。
客戶機/服務器模式在操作過程中采用的是主動請求的方式。
服務器:
首先服務器方要先啟動,并根據(jù)請求提供相應的服務:
1.打開一個通信通道并告知本地主機,它愿意在某一地址及端口上接收客戶請求。
2.等待客戶請求到達該端口。
3.接收到重復服務請求,處理該請求并發(fā)送應答信號。
接收到并發(fā)服務請求,要激活一個新的進程(或線程)來處理這個客戶的請求。
新進程(或線程)處理此客戶請求,并不需要對其它請求作出應答。
服務完成后,關閉此新進程與客戶的通信鏈路,并終止。
4.返回第2步,等待另一客戶請求。
5.關閉服務器。
客戶機:
1.打開一個通信信道,并連接到服務器所在主機的指定端口。
2.向服務器發(fā)請求報文,等待并接收應答;繼續(xù)提出請求。
3.請求結束后,關閉通信信道并終止。
Windows Sockets的實現(xiàn)
Windows Sockets是Microsoft Windows的網(wǎng)絡程序設計接口,
它是從Berkeley Sockets展而來的,以動態(tài)鏈接庫的形式提供給我們使用。
Windows Sockets在繼承了Berkeley Sockets主要特征的基礎上,又對它進行了擴充。
這些擴充主要是提供了一些異步函數(shù),并增加了符合Windows消息驅(qū)動特性的網(wǎng)絡事件異步選擇機制。
Windows??Sockets 1.1和Berkeley Sockets都是基于TCP/IP的。
Windows??? Sockets? 2 從Windows Sockets? 1.1發(fā)展而來,與協(xié)議無關并向下兼容,
可以使用任何底層傳輸協(xié)議提供的通信能力,來為上層應用程序完成網(wǎng)絡數(shù)據(jù)通訊,
而不關心底層網(wǎng)絡鏈路的通信情況,真正實現(xiàn)了底層網(wǎng)絡通訊對應用程序的透明(?)。
套接字的類型
1.流式套接字(SOCK_STREAM)
??????提供面向連接、可靠的數(shù)據(jù)傳輸服務,數(shù)據(jù)無差錯無重復的發(fā)送,且按發(fā)送順序接收。
2.數(shù)據(jù)報套接字(SOCK_DGRAM)
??????提供無連接服務。數(shù)據(jù)包以獨立包形式發(fā)送,數(shù)據(jù)可能丟失、重復,且接收順序混亂。
3.原始套接字(SOCK_RAM)
基于TCP(面向連接)的socket編程
服務器端程序:
1.創(chuàng)建套接字(socket)。
2.將套接字綁定到一個本地地址和端口上(bind)。?
3.將套接字設為監(jiān)聽模式,準備接收客戶請求(listen)。
4.等待客戶請求到來;當請求到來后,接收連接請求,返回一個新的對應于此次連接的套接字(accept)。
5.用返回的套接字和客戶端進行通信(send/recv)。
6.返回,等待另一客戶請求。
7.關閉套接字(close)。???
客戶端程序:
1.創(chuàng)建套接字(socket)。
2.向服務器發(fā)出連接請求(connect)。
3.和服務器端進行通信(send/recv)。
4.關閉套接字?(close)。
基于(UDP)面向無連接的socket編程
服務器端程序:
1.創(chuàng)建套接字(socket)。
2.將套接字綁定到一個本地地址和端口上(bind)。
3.等待接收數(shù)據(jù)(recvfrom)。
4.關閉套接字(close)。
客戶端程序:
1.創(chuàng)建套接字(socket)。
2.向服務器發(fā)送數(shù)據(jù)(sendto)。
3.關閉套接字(close)。
相關函數(shù)說明
int? WSAStartup(WORD wVersionRequested? , LPWSADATA?????? ?lpWSAData);
wVersionRequested參數(shù)用于指定準備加載的Winsock庫的版本。
高位字節(jié)指定所需要的Winsock庫的副版本,而低字節(jié)則是主版本。
可用MAKEWORD(?x , y )(其中x是高位字節(jié),y是低位字節(jié))方便地獲得wVersionRequested的正確值。
lpWSAData參數(shù)是指向WSADATA結構的指針,
WSAStartup用其加載的庫版本有關的信息填在這個結構中。
WSADATA結構定義如下:
typedef struct WSADate
{
??????WORD??? wVersion;
??????WORD???? wHighVersion;
??????char??????szDescription [WSADESCRIPTION_LEN+1];
??????char????? szSystemStatus[WSASYS_STATUS_LEN+1];
??????unsigned?? short? iMaxSockets;
??????unsigned???short? iMaxUdpDg;
??????char??? FAR*?? lpVendorInfo;
}WSADATA, *LPWSADATA;
??????WSAStartup把第一個字段wVersion設成打算使用的Winsock的版本,wHighVersion參數(shù)容納的是現(xiàn)有的Winsock庫的最高版本。記住,兩個字段中,高位字節(jié)代表的是Winsock副版本,而低字節(jié)代表的是Winsock庫的主版本。szDescription和szSystemStatus這兩個字段由特定的Winsock實施方案設定,事實上沒有用。不要使用下面這兩個字段:iMaxSocks和iMaxUdpDg.,它們是假定同時最多可打開多少套接字和數(shù)據(jù)報的最大長度。然而,要知道數(shù)據(jù)報的最大長度應該通過WSAEnumProtocols關。最后,lpvendorInfo字段是為Winsock實施方案有關的指定廠商信息預留的。任何一個Win32平臺上都沒有使用這個字段。
?????????如果Winsock.dll或底層網(wǎng)絡子系統(tǒng)沒有被正確初始化或沒有被找到,WSAStartup將返回WSASYSNOTREADY。此外這個函數(shù)允許你的應用程序協(xié)商使用某種版本的Winsock規(guī)范,如果請求的版本等于或高于DLL所支持的最高版本與請求版本中較小的那個。反之,如果請求的版本低于DLL所支持的最低版本,WSAStartup將返回WSAVERNOTSUPPORTED。關于WSAStartup更詳細的信息,請查閱MSDN中的相關部分。
????????對于每一個WSAStartup的成功調(diào)用(成功加載Winsock DLL后),在最后都對應一個WSACleanUp調(diào)用,以便釋放為該應用程序分配的資源。
SOCKET? socket(int af, int? type,? int? protocal);
該函數(shù)接收3個參數(shù)。
第一個參數(shù)af指定地址族,對于TCP/IP協(xié)議的套接字,它只能是AF_INET(也可寫成PF_INET)。
第二個參數(shù)指定Socket類型,對于1.1版本的Socket,它只支持2種類型的套接字,SOCK_STREAM指定產(chǎn)生流式套接字,SOCK_DGRAM產(chǎn)生數(shù)據(jù)報套接字。
第三個參數(shù)是與特定地址家族相關的協(xié)議,自動為你選擇一個合適的協(xié)議。這是推薦使用的一種選擇協(xié)議的方法。
如果這個函數(shù)調(diào)用成功,它將返回一個新的SOCKET數(shù)據(jù)類型的套接字描述符。如果調(diào)用失敗,這個函數(shù)就會返回一個INVALID_SOCKET,錯誤信息可以通過WSAGEtLastError函數(shù)返回。
int bind(SOCKET? s,?? const?? struct?? sockaddr? FAR? *? name,? int?? namelen);
第一個參數(shù)s指定要綁定的套接字,
第二個參數(shù)指定了該套接字的本地地址信息,是指向sockaddr結構的指針變量,由于該地質(zhì)結構是為所有的地址家族準備的,這個結構可能(通常會)隨所使用的網(wǎng)絡協(xié)議不同而不同,所以,
要用第三個參數(shù)指定該地址結構的長度。
sockaddr結構定義如下:
struct? sockaddr
{
??????u_short?? sa_family;
??????char??????? sa_data[14];
}
sockaddr的第一個字段sa_family指定該地址家族,在這里必須設為
AF_INET.sa_data僅僅是表示要求一塊內(nèi)存分配區(qū),起到占位的作用,該區(qū)域中指定與協(xié)議相關的具體地址信息。由于實際要求的只是內(nèi)存區(qū),所以對于不同的協(xié)議家族,用不同的結構來替換 sockaddr。除了sa_family外,sockaddr是按網(wǎng)絡字節(jié)順序表示的。在TCP/IP中,我們可以用sockaddr_in結構替換sockaddr,以方便我們填寫地址信息。
sockaddr_in的定義如下:
struct?? sockaddr_in
{
??????short???????????????????????sin_family;
????????unsigned????short?????sin_port;
?????????struct?????????in_addr??????sin_addr;
?????????char?? sin_size[8];
}
?????????其中,sin_family表示地址族,對于IP地址,sin_family成員將一直是AF_INET。成員sin_port指定的是將要分配給套接字的端口。成員sin_addr給出的是套接字的主機IP地址。而成員sin_zero只是一個填充數(shù),以使sockaddr_in結構和sockaddr結構的長度一樣。如果這個函數(shù)調(diào)用成功,它將返回0。如果調(diào)用失敗,這個函數(shù)就會返回一個SOCKET_ERROR,錯誤信息可以通過WSAGetLastError函數(shù)返回。
??????將IP地址指定為INADDR_ANY,允許套接字向任何分配給本地機器的IP地址發(fā)送或接收數(shù)據(jù)。多數(shù)情況下,每個機器只有一個IP地址,但有的機器可能會有多個網(wǎng)卡,每個網(wǎng)卡都可有自己的IP,用INADDR_ANY可以簡化應用程序的編寫。將地址指定為INADDR_ANY,允許一個獨立應用接受發(fā)自多個接口的回應,如果我們只想讓套接字使用多個IP中的一個,就必須指定實際地址,要做到這一點,可以用inet_addr()函數(shù),這個函數(shù)需要一個字符串做為其參數(shù),該字符串指定了以點分十進制格式表示的IP地址(如:192.168.0.16).而且inet_addr()函數(shù)會返回一個適合分配給S_addr的u_long類型的數(shù)值。inet_ntoa()函數(shù)會完成相反的轉(zhuǎn)換,它接受一個in_addr結構類型的參數(shù)并返回一個以點分十進制格式表示的IP地址字符串。