文章目錄
- 前言:
- 1. 預備知識
- 1.1 理解源IP地址和目的IP地址
- 1.2 認識端口號
- 1.3 理解"端口號"和"進程ID"
- 1.4 理解源端口號和目的端口號
- 1.5 認識TCP協議
- 1.6 認識UDP協議
- 1.6 TCP vs UDP 可靠性
- 1.7 網絡字節序
- 2. socket 編程接口
- 2.1 socket 常見API
- 2.2 sockaddr結構
- 3. 簡單的UDP網絡程序
- 3.1 UDP實現簡易聊天室:
- 總結:
前言:
在現代信息技術飛速發展的今天,網絡通信已經成為我們日常生活和工作中不可或缺的一部分。無論是通過電子郵件、社交媒體還是在線會議,網絡通信都扮演著至關重要的角色。而在這背后,是復雜的網絡協議和編程技術支撐著這一切的運行。本文旨在深入探討網絡編程的基礎知識,特別是UDP和TCP這兩種常用的傳輸層協議,以及它們在socket編程中的應用。通過本文,讀者將能夠理解源IP地址、目的IP地址、端口號等概念,并學習如何使用socket編程接口來創建網絡應用程序。此外,本文還將介紹網絡字節序的概念,以及如何通過轉換函數確保網絡程序的可移植性。最后,通過一個簡單的UDP網絡程序實例,我們將展示如何實現一個回聲服務器和簡易聊天室,讓讀者對網絡編程有更直觀的認識。
1. 預備知識
1.1 理解源IP地址和目的IP地址
在IP數據包頭部中, 有兩個IP地址, 分別叫做源IP地址, 和目的IP地址。
思考: 我們光有IP地址就可以完成通信了嘛? 想象一下發qq消息的例子, 有了IP地址能夠把消息發送到對方的機器上,但是還需要有一個其他的標識來區分出, 這個數據要給哪個程序進行解析。
1.2 認識端口號
- 我們上網,無非兩種動作:a. 把遠端的數據拉取到本地 b. 把我的數據發送到遠端
- 大部分的網絡通信行為,都是用戶觸發的。計算機中,誰表示用戶呢?進程!(客戶端服務,服務端服務)
- 把數據發送到目標主機,不是目的,是手段。正真的目的,是把數據交給這個主機上的某一個服務(進程)(服務必須具有唯一的標識:端口號)
- 網絡通信的本質,其實是進程幫我們進行網絡通信,無論是對于C還是S
- IP(唯一的一臺主機)+ port(該主機的唯一的一個進程) = 互聯網中唯一的一個進程
- client -> server: client進程 -> server進程
client進程 = client ip + client port = client是互聯網中唯一的一個進程
server進程 = server ip + server port = server是互聯網中唯一的一個進程
唯一的找到彼此(src ip, src port; dst ip, dst port)(socket通信)
結論: 網絡通信的本質:其實就是進程間通信!
進程間通信:看到公共的資源(網絡)
1.3 理解"端口號"和"進程ID"
我們之前在學習系統編程的時候, 學習了 pid 表示唯一一個進程; 此處我們的端口號也是唯一表示一個進程. 那么這兩者之間是怎樣的關系?
- PID 是操作系統用來標識一個進程的唯一編號。
- 端口號 是網絡通信中用來標識主機上特定服務的數字。
它們之間沒有直接關系,PID用于操作系統內部管理,而端口號用于網絡通信。另外, 一個進程可以綁定多個端口號; 但是一個端口號不能被多個進程綁定。
1.4 理解源端口號和目的端口號
傳輸層協議(TCP和UDP)的數據段中有兩個端口號, 分別叫做源端口號和目的端口號. 就是在描述 “數據是誰發的, 要發給誰”;
1.5 認識TCP協議
此處我們先對TCP(Transmission Control Protocol 傳輸控制協議)有一個直觀的認識; 后面我們再詳細討論TCP的一些細節問題.
- 傳輸層協議
- 有連接
- 可靠傳輸
- 面向字節流
1.6 認識UDP協議
此處我們也是對UDP(User Datagram Protocol 用戶數據報協議)有一個直觀的認識; 后面再詳細討論.
- 傳輸層協議
- 無連接
- 不可靠傳輸
- 面向數據報
1.6 TCP vs UDP 可靠性
TCP 要保證可靠性,就需要做更多的工作——TCP協議一定更復雜——接口會更多一些。
UDP 協議一定更簡單。
1.7 網絡字節序
我們已經知道,內存中的多字節數據相對于內存地址有大端和小端之分, 磁盤文件中的多字節數據相對于文件中的偏移地址也有大端小端之分, 網絡數據流同樣有大端小端之分. 那么如何定義網絡數據流的地址呢?
- 發送主機通常將發送緩沖區中的數據按內存地址從低到高的順序發出;
- 接收主機把從網絡上接到的字節依次保存在接收緩沖區中,也是按內存地址從低到高的順序保存;
- 因此,網絡數據流的地址應這樣規定:先發出的數據是低地址,后發出的數據是高地址.
- TCP/IP協議規定,網絡數據流應采用大端字節序,即低地址高字節.
- 不管這臺主機是大端機還是小端機, 都會按照這個TCP/IP規定的網絡字節序來發送/接收數據;
- 如果當前發送主機是小端, 就需要先將數據轉成大端; 否則就忽略, 直接發送即可;
為使網絡程序具有可移植性,使同樣的C代碼在大端和小端計算機上編譯后都能正常運行,可以調用以下庫函數做網絡字節序和主機字節序的轉換。
- 這些函數名很好記,h表示host,n表示network,l表示32位長整數,s表示16位短整數。
- 例如htonl表示將32位的長整數從主機字節序轉換為網絡字節序,例如將IP地址轉換后準備發送。
- 如果主機是小端字節序,這些函數將參數做相應的大小端轉換然后返回 ;
- 如果主機是大端字節序,這些 函數不做轉換,將參數原封不動地返回。
2. socket 編程接口
2.1 socket 常見API
// 創建 socket 文件描述符 (TCP/UDP, 客戶端 + 服務器)
int socket(int domain, int type, int protocol);
// 綁定端口號 (TCP/UDP, 服務器)
int bind(int socket, const struct sockaddr *address, socklen_t address_len);
// 開始監聽socket (TCP, 服務器)
int listen(int socket, int backlog);
// 接收請求 (TCP, 服務器)
int accept(int socket, struct sockaddr* address,socklen_t* address_len);
// 建立連接 (TCP, 客戶端)
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
2.2 sockaddr結構
socket編程,是有不同種類的,有的是專門用來進行本地通信的(unix socket),有的是用來專門跨網絡通信的(inet socket),有的是用來進行網絡管理的(raw socket)。
統一接口 -> C語言寫的 -> 統一類型 -> struct sockaddr
3. 簡單的UDP網絡程序
bind: socket = ip + port; 文件信息和網絡信息給關聯起來
#include <sys/types.h> /* See NOTES */#include <sys/socket.h>int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
填充信息:
sin_addr —— 16位地址類型: AF_INET、 32位IP地址
sin_port —— 16位端口號
sin_zero —— 8字節填充
如何理解 “192.168.1.2” <==> 4字節IP地址之間互相轉換
// 4字節 轉 字符串
struct IP
{uint8_t p1;uint8_t p2;uint8_t p3;uint8_t p4;
}struct IP *temp = (struct IP*)&ipaddr;
to_string(temp->p1) + "." + to_string(temp->p2) + ...
// 字符串 轉 4字節
struct IP temp;
temp.p1 = stoi(substr("."));
uint32_t ipint = (int)temp;
127.0.0.1
: 本地環回:
./udpserver 127.0.0.1 8888
127.0.0.1
: 本地環回,可以實現本地通信,常用于進行代碼測試
recvfrom(接收消息):
// 接收信息ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,struct sockaddr *src_addr, socklen_t *addrlen);// buf: 輸出型緩沖區// len: 期望長度// 返回值: 實際讀到的長度// src_addr: 輸出型參數(ip 與 port)// addrlen: 輸出型參數
有人給你發了消息,你想不想知道誰給你發的? 為什么? 因為我還要給別人回消息
你通過什么信息,只到只到對方是誰? socket 對方的 IP 和 port!
sendto(發送消息):
// 發送信息ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,const struct sockaddr *dest_addr, socklen_t addrlen);
使用公網ip,我們的服務器,無法直接bind公網ip(云服務器不允許),也嚴重不推薦,bind公網ip,或者任何一個確定的ip。
實現一個回聲服務器:
終端輸出也是一個文件,不同的終端窗口是不同的文件。
3.1 UDP實現簡易聊天室:
gitee:https://gitee.com/q-haodong/test_-linux/tree/master/20240702_udp_echo_sever
總結:
本文從網絡通信的基本概念出發,逐步深入到TCP和UDP協議的細節,并探討了它們在socket編程中的應用。通過預備知識的介紹,我們理解了源IP地址、目的IP地址、端口號等在網絡通信中的重要性。同時,我們也學習了網絡字節序的概念,以及如何通過特定的函數進行主機字節序和網絡字節序之間的轉換,以確保程序的兼容性和可移植性。在socket編程接口部分,我們介紹了創建socket文件描述符、綁定端口號、監聽socket、接收請求、建立連接等常見API的使用。最后,通過實現一個UDP回聲服務器和簡易聊天室的示例,我們展示了UDP協議在實際網絡編程中的應用。通過本文的學習,讀者不僅能夠掌握網絡編程的基礎知識,還能夠獲得實際編程的經驗,為進一步深入學習和探索網絡編程打下堅實的基礎。