linux網絡編程(一)網絡傳輸基礎知識
- 一、什么是協議?
- 二、使用步驟
- 典型協議
- 2.網絡應用程序設計模式
- C/S模式
- B/S模式
- 優缺點
- 3.分層模型
- 4.TCP/IP四層模型
- 通信過程
- 5.協議格式
- 數據包封裝
- 以太網幀格式
- ARP數據報格式
- IP段格式
- UDP數據報格式
- TCP數據報格式
- 6.TCP協議
- 三次握手
- 四次握手
- 服務器
- 客戶端
一、什么是協議?
示例:pandas 是基于NumPy 的一種工具,該工具是為了解決數據分析任務而創建的。
二、使用步驟
從應用的角度出發,協議可理解為“規則”,是數據傳輸和數據的解釋的規則.
假設,A、B雙方欲傳輸文件。規定:
第一次,傳輸文件名,接收方接收到文件名,應答OK給傳輸方;
第二次,發送文件的尺寸,接收方接收到該數據再次應答一個OK;
第三次,傳輸文件內容。同樣,接收方接收數據完成后應答OK表示文件內容接收成功。
由此,無論A、B之間傳遞何種文件,都是通過三次數據傳輸來完成。A、B之間形成了一個最簡單的數據傳輸規則。雙方都按此規則發送、接收數據。A、B之間達成的這個相互遵守的規則即為協議。
這種僅在A、B之間被遵守的協議稱之為原始協議。當此協議被更多的人采用,不斷的增加、改進、維護、完善。最終形成一個穩定的、完整的文件傳輸協議,被廣泛應用于各種文件傳輸過程中。該協議就成為一個標準協議。最早的ftp協議就是由此衍生而來。
典型協議
傳輸層 常見協議有TCP/UDP協議。
應用層 常見的協議有HTTP協議,FTP協議。
網絡層 常見協議有IP協議、ICMP協議、IGMP協議。
網絡接口層 常見協議有ARP協議、RARP協議。
TCP傳輸控制協議(Transmission Control Protocol)是一種面向連接的、可靠的、基于字節流的傳輸層通信協議。
UDP用戶數據報協議(User Datagram Protocol)是OSI參考模型中一種無連接的傳輸層協議,提供面向事務的簡單不可靠信息傳送服務。
HTTP超文本傳輸協議(Hyper Text Transfer Protocol)是互聯網上應用最為廣泛的一種網絡協議。
FTP文件傳輸協議(File Transfer Protocol)
IP協議是因特網互聯協議(Internet Protocol)
ICMP協議是Internet控制報文協議(Internet Control Message Protocol)它是TCP/IP協議族的一個子協議,用于在IP主機、路由器之間傳遞控制消息。
IGMP協議是 Internet 組管理協議(Internet Group Management Protocol),是因特網協議家族中的一個組播協議。該協議運行在主機和組播路由器之間。
ARP協議是正向地址解析協議(Address Resolution Protocol),通過已知的IP,尋找對應主機的MAC地址。
RARP是反向地址轉換協議,通過MAC地址確定IP地址。
2.網絡應用程序設計模式
C/S模式
傳統的網絡應用設計模式,客戶機(client)/服務器(server)模式。需要在通訊兩端各自部署客戶機和服務器來完成數據通信
B/S模式
瀏覽器/服務器(server)模式。只需在一端部署服務器,而另外一端使用每臺PC都默認配置的瀏覽器即可完成數據的傳輸。
優缺點
C/S 優點:1.協議靈活可自定義 2.緩存數據
缺點:1.對用戶的安全構成威脅 2.開發工作量較大,調試困難
B/S 優點:1.跨平臺 2.無需緩存數據3.開發量小
缺點:1.只能采用http協議
代碼如下(示例):
3.分層模型
- 物理層:主要定義物理設備標準,如網線的接口類型、光纖的接口類型、各種傳輸介質的傳輸速率等。它的主要作用是傳輸比特流(就是由1、0轉化為電流強弱來進行傳輸,到達目的地后再轉化為1、0,也就是我們常說的數模轉換與模數轉換)。這一層的數據叫做比特。
- 數據鏈路層:定義了如何讓格式化數據以幀為單位進行傳輸,以及如何讓控制對物理介質的訪問。這一層通常還提供錯誤檢測和糾正,以確保數據的可靠傳輸。如:串口通信中使用到的115200、8、N、1
- 網絡層:在位于不同地理位置的網絡中的兩個主機系統之間提供連接和路徑選擇。Internet的發展使得從世界各站點訪問信息的用戶數大大增加,而網絡層正是管理這種連接的層。
- 傳輸層:定義了一些傳輸數據的協議和端口號(WWW端口80等),如:TCP(傳輸控制協議,傳輸效率低,可靠性強,用于傳輸可靠性要求高,數據量大的數據),UDP(用戶數據報協議,與TCP特性恰恰相反,用于傳輸可靠性要求不高,數據量小的數據,如QQ聊天數據就是通過這種方式傳輸的)。 主要是將從下層接收的數據進行分段和傳輸,到達目的地址后再進行重組。常常把這一層數據叫做段。
- 會話層:通過傳輸層(端口號:傳輸端口與接收端口)建立數據傳輸的通路。主要在你的系統之間發起會話或者接受會話請求(設備之間需要互相認識可以是IP也可以是MAC或者是主機名)。
- 表示層:可確保一個系統的應用層所發送的信息可以被另一個系統的應用層讀取。例如,PC程序與另一臺計算機進行通信,其中一臺計算機使用擴展二一十進制交換碼(EBCDIC),而另一臺則使用美國信息交換標準碼(ASCII)來表示相同的字符。如有必要,表示層會通過使用一種通格式來實現多種數據格式之間的轉換。
- 應用層:是最靠近用戶的OSI層。這一層為用戶的應用程序(例如電子郵件、文件傳輸和終端仿真)提供網絡服務。
4.TCP/IP四層模型
通信過程
兩臺計算機通過TCP/IP協議通訊的過程如下所示:
上圖對應兩臺計算機在同一網段中的情況,如果兩臺計算機在不同的網段中,那么數據從一臺計算機到另一臺計算機傳輸過程中要經過一個或多個路由器,如下圖所示:
5.協議格式
數據包封裝
傳輸層及其以下的機制由內核提供,應用層由用戶進程提供(后面將介紹如何使用socket API編寫應用程序),應用程序對通訊數據的含義進行解釋,而傳輸層及其以下處理通訊的細節,將數據從一臺計算機通過一定的路徑發送到另一臺計算機。應用層數據通過協議棧發到網絡上時,每層協議都要加上一個數據首部(header),稱為封裝(Encapsulation),如下圖所示:
不同的協議層對數據包有不同的稱謂,在傳輸層叫做段(segment),在網絡層叫做數據報(datagram),在鏈路層叫做幀(frame)。數據封裝成幀后發到傳輸介質上,到達目的主機后每層協議再剝掉相應的首部,最后將應用層數據交給應用程序處理
以太網幀格式
其中的源地址和目的地址是指網卡的硬件地址(也叫MAC地址),長度是48位,是在網卡出廠時固化的。可在shell中使用ifconfig命令查看,“HWaddr 00:15:F2:14:9E:3F”部分就是硬件地址。協議字段有三種值,分別對應IP、ARP、RARP。幀尾是CRC校驗碼。
以太網幀中的數據長度規定最小46字節,最大1500字節,ARP和RARP數據包的長度不夠46字節,要在后面補填充位。最大值1500稱為以太網的最大傳輸單元(MTU),不同的網絡類型有不同的MTU,如果一個數據包從以太網路由到撥號鏈路上,數據包長度大于撥號鏈路的MTU,則需要對數據包進行分片(fragmentation)。ifconfig命令輸出中也有“MTU:1500”。注意,MTU這個概念指數據幀中有效載荷的最大長度,不包括幀頭長度。
ARP數據報格式
在網絡通訊時,源主機的應用程序知道目的主機的IP地址和端口號,卻不知道目的主機的硬件地址,而數據包首先是被網卡接收到再去處理上層協議的,如果接收到的數據包的硬件地址與本機不符,則直接丟棄。因此在通訊前必須獲得目的主機的硬件地址。ARP協議就起到這個作用。源主機發出ARP請求,詢問“IP地址是192.168.0.1的主機的硬件地址是多少”,并將這個請求廣播到本地網段(以太網幀首部的硬件地址填FF:FF:FF:FF:FF:FF表示廣播),目的主機接收到廣播的ARP請求,發現其中的IP地址與本機相符,則發送一個ARP應答數據包給源主機,將自己的硬件地址填寫在應答包中。
源MAC地址、目的MAC地址在以太網首部和ARP請求中各出現一次,對于鏈路層為以太網的情況是多余的,但如果鏈路層是其它類型的網絡則有可能是必要的。硬件類型指鏈路層網絡類型,1為以太網,協議類型指要轉換的地址類型,0x0800為IP地址,后面兩個地址長度對于以太網地址和IP地址分別為6和4(字節),op字段為1表示ARP請求,op字段為2表示ARP應答
看一個具體的例子。
請求幀如下(為了清晰在每行的前面加了字節計數,每行16個字節):
以太網首部(14字節)
0000: ff ff ff ff ff ff 00 05 5d 61 58 a8 08 06
ARP幀(28字節)
0000: 00 01
0010: 08 00 06 04 00 01 00 05 5d 61 58 a8 c0 a8 00 37
0020: 00 00 00 00 00 00 c0 a8 00 02
填充位(18字節)
0020: 00 77 31 d2 50 10
0030: fd 78 41 d3 00 00 00 00 00 00 00 00
以太網首部:目的主機采用廣播地址,源主機的MAC地址是00:05:5d:61:58:a8,上層協議類型0x0806表示ARP。
ARP幀:硬件類型0x0001表示以太網,協議類型0x0800表示IP協議,硬件地址(MAC地址)長度為6,協議地址(IP地址)長度為4,op為0x0001表示請求目的主機的MAC地址,源主機MAC地址為00:05:5d:61:58:a8,源主機IP地址為c0 a8 00 37(192.168.0.55),目的主機MAC地址全0待填寫,目的主機IP地址為c0 a8 00 02(192.168.0.2)。
由于以太網規定最小數據長度為46字節,ARP幀長度只有28字節,因此有18字節填充位,填充位的內容沒有定義,與具體實現相關。
應答幀如下:
以太網首部
0000: 00 05 5d 61 58 a8 00 05 5d a1 b8 40 08 06
ARP幀
0000: 00 01
0010: 08 00 06 04 00 02 00 05 5d a1 b8 40 c0 a8 00 02
0020: 00 05 5d 61 58 a8 c0 a8 00 37
填充位
0020: 00 77 31 d2 50 10
0030: fd 78 41 d3 00 00 00 00 00 00 00 00
以太網首部:目的主機的MAC地址是00:05:5d:61:58:a8,源主機的MAC地址是00:05:5d:a1:b8:40,上層協議類型0x0806表示ARP。
ARP幀:硬件類型0x0001表示以太網,協議類型0x0800表示IP協議,硬件地址(MAC地址)長度為6,協議地址(IP地址)長度為4,op為0x0002表示應答,源主機MAC地址為00:05:5d:a1:b8:40,源主機IP地址為c0 a8 00 02(192.168.0.2),目的主機MAC地址為00:05:5d:61:58:a8,目的主機IP地址為c0 a8 00 37(192.168.0.55)。
IP段格式
IP數據報的首部長度和數據長度都是可變長的,但總是4字節的整數倍。對于IPv4,4位版本字段是4。4位首部長度的數值是以4字節為單位的,最小值為5,也就是說首部長度最小是4x5=20字節,也就是不帶任何選項的IP首部,4位能表示的最大值是15,也就是說首部長度最大是60字節。8位TOS字段有3個位用來指定IP數據報的優先級(目前已經廢棄不用),還有4個位表示可選的服務類型(最小延遲、最大?吐量、最大可靠性、最小成本),還有一個位總是0。總長度是整個數據報(包括IP首部和IP層payload)的字節數。每傳一個IP數據報,16位的標識加1,可用于分片和重新組裝數據報。3位標志和13位片偏移用于分片。TTL(Time to live)是這樣用的:源主機為數據包設定一個生存時間,比如64,每過一個路由器就把該值減1,如果減到0就表示路由已經太長了仍然找不到目的主機的網絡,就丟棄該包,因此這個生存時間的單位不是秒,而是跳(hop)。協議字段指示上層協議是TCP、UDP、ICMP還是IGMP。然后是校驗和,只校驗IP首部,數據的校驗由更高層協議負責。IPv4的IP地址長度為32位。
UDP數據報格式
TCP數據報格式
與UDP協議一樣也有源端口號和目的端口號,通訊的雙方由IP地址和端口號標識。32位序號、32位確認序號、窗口大小稍后詳細解釋。4位首部長度和IP協議頭類似,表示TCP協議頭的長度,以4字節為單位,因此TCP協議頭最長可以是4x15=60字節,如果沒有選項字段,TCP協議頭最短20字節。URG、ACK、PSH、RST、SYN、FIN是六個控制位,本節稍后將解釋SYN、ACK、FIN、RST四個位,其它位的解釋從略。16位檢驗和將TCP協議頭和數據都計算在內。緊急指針和各種選項的解釋從略。
6.TCP協議
三次握手
建立連接(三次握手)的過程:
- 客戶端發送一個帶SYN標志的TCP報文到服務器。這是三次握手過程中的段1。
客戶端發出段1,SYN位表示連接請求。序號是1000,這個序號在網絡通訊中用作臨時的地址,每發一個數據字節,這個序號要加1,這樣在接收端可以根據序號排出數據包的正確順序,也可以發現丟包的情況,另外,規定SYN位和FIN位也要占一個序號,這次雖然沒發數據,但是由于發了SYN位,因此下次再發送應該用序號1001。mss表示最大段尺寸,如果一個段太大,封裝成幀后超過了鏈路層的最大幀長度,就必須在IP層分片,為了避免這種情況,客戶端聲明自己的最大段尺寸,建議服務器端發來的段不要超過這個長度。 - 服務器端回應客戶端,是三次握手中的第2個報文段,同時帶ACK標志和SYN標志。它表示對剛才客戶端SYN的回應;同時又發送SYN給客戶端,詢問客戶端是否準備好進行數據通訊。
服務器發出段2,也帶有SYN位,同時置ACK位表示確認,確認序號是1001,表示“我接收到序號1000及其以前所有的段,請你下次發送序號為1001的段”,也就是應答了客戶端的連接請求,同時也給客戶端發出一個連接請求,同時聲明最大尺寸為1024。 - 客戶必須再次回應服務器端一個ACK報文,這是報文段3。
客戶端發出段3,對服務器的連接請求進行應答,確認序號是8001。在這個過程中,客戶端和服務器分別給對方發了連接請求,也應答了對方的連接請求,其中服務器的請求和應答在一個段中發出,因此一共有三個段用于建立連接,稱為“三方握手(three-way-handshake)”。在建立連接的同時,雙方協商了一些信息,例如雙方發送序號的初始值、最大段尺寸等。
四次握手
由于TCP連接是全雙工的,因此每個方向都必須單獨進行關閉。這原則是當一方完成它的數據發送任務后就能發送一個FIN來終止這個方向的連接。收到一個 FIN只意味著這一方向上沒有數據流動,一個TCP連接在收到一個FIN后仍能發送數據。首先進行關閉的一方將執行主動關閉,而另一方執行被動關閉。
- 客戶端發出段7,FIN位表示關閉連接的請求。
- 服務器發出段8,應答客戶端的關閉連接請求。
- 服務器發出段9,其中也包含FIN位,向客戶端發送關閉連接請求。
- 客戶端發出段10,應答服務器的關閉連接請求。
服務器
#include <sys/types.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <ctype.h>
#include <stdio.h>
#include <memory.h>#define SERV_PORT 6666
#define SERV_IP "127.0.0.1"int main()
{int sid,ret,cid,len;struct sockaddr_in addr,clie_addr;char buf[BUFSIZ] = {0};char clie_IP[BUFSIZ];socklen_t clie_len,clie_ip_len;//服務器創建套接字sid=socket(AF_INET, SOCK_STREAM,0);if (sid == -1){perror("server error:");exit(1);}bzero(&addr,sizeof(addr));addr.sin_family = AF_INET;addr.sin_port = htons(SERV_PORT);addr.sin_addr.s_addr =htonl( INADDR_ANY);//綁定ret=bind(sid,(struct sockaddr*)&addr,sizeof(addr));if (ret == -1){perror("bind error:");exit(1);}ret=listen(sid,30);if (ret == -1){perror("listen error:");exit(1);}clie_len = sizeof(clie_addr);cid=accept(sid, (struct sockaddr*)&clie_addr, &clie_printf("client IP:%s,client port:%d\n",inet_ntop(AF_INET,&clie_addr.sin_addr.s_addr,cli, ntohs(clie_addr.sin_port));while (1){//讀取客戶端信息len = read(cid, buf, sizeof(buf));if (len < 0){perror("read error");exit(1);}write(STDOUT_FILENO, buf, sizeof(buf));for (int i = 0; i < len; i++){buf[i] = toupper(buf[i]);}write(cid, buf, sizeof(buf));memset(buf,0,sizeof(buf));}close(sid);close(cid);return 0;
}
客戶端
#include <sys/types.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <ctype.h>
#include <stdio.h>
#include <memory.h>#define CLIE_PORT 6666
#define CLIE_IP "118.178.192.222"int main()
{char buf[BUFSIZ] = {0};struct in_addr s; // IPv4地址結構體int cfd,ret,n;struct sockaddr_in addr;//客服端創建套接字cfd = socket(AF_INET,SOCK_STREAM,0);if (cfd == -1){perror("client error:");exit(1);}memset(&addr, 0, sizeof(addr));addr.sin_family = AF_INET;addr.sin_port = htons(CLIE_PORT);inet_pton(AF_INET, CLIE_IP,&addr.sin_addr.s_addr);ret =connect(cfd,(struct sockaddr *)&addr,sizeof(addr));if (ret <0){perror("connect error:");exit(1);}while (1){//從終端寫入fgets(buf,sizeof(buf),stdin);write(cfd,buf,strlen(buf));n=read(cfd, buf, sizeof(buf));write(STDOUT_FILENO, buf, n);memset(buf, 0, sizeof(buf));}close(cfd);return 0;
}