【計算機網絡】Socket網絡編程

目錄

一、主機字節序列和網絡字節序列

二、套接字地址結構

1、IPv4 地址結構 (sockaddr_in)

2、IPv6 地址結構 (sockaddr_in6)

3、通用套接字地址結構 (sockaddr)

4、Unix域套接字地址結構 (sockaddr_un)

5、專用 socket 地址結構

6、套接字地址結構的轉換

字符串轉二進制地址

二進制地址轉字符串

7、端口號的轉換

三、網絡編程接口

socket

bind

listen

accept

TCP 數據讀寫:

UDP 數據讀寫:

close

connect

四、TCP編程流程

代碼示例:

服務端

客戶端


一、主機字節序列和網絡字節序列

主機字節序列(Host Byte Order)指的是計算機在內存中存儲多字節數據時的順序。分為大端字節序和小端字節序,不同的主機采用的字節序列可能不同。

  • 大端字節序是指一個整數的高位字節存儲在內存的低地址處,低位字節存儲在內存的高地址處。
  • 小端字節序則是指整數的高位字節存儲在內存的高地址處,而低位字節則存儲在內存的低地址處。

在兩臺使用不同字節序的主機之間傳遞數據時,可能會出現沖突。所以,在將數據發送到網絡時規定整形數據使用大端字節序,所以也把大端字節序成為網絡字節序列。對方接收到數據后,可以根據自己的字節序進行轉換。

Linux 系統提供如下 4 個函數來完成主機字節序和網絡字節序之間的轉換:

#include <arpa/inet.h>
#include <netinet/in.h>uint32_t htonl(uint32_t hostlong); // 主機到網絡(長整型)長整型的主機字節序轉網絡字節序
uint16_t htons(uint16_t hostshort); // 主機到網絡(短整型)短整形的主機字節序轉網絡字節序
uint32_t ntohl(uint32_t netlong);   // 網絡到主機(長整型)長整型的網絡字節序轉主機字節序
uint16_t ntohs(uint16_t netshort);  // 網絡到主機(短整型)短整型的網絡字節序轉主機字節序

不同架構的處理器可能使用不同的字節序。x86架構通常是小端序,而網絡協議(如TCP/IP)要求使用大端序。因此,跨平臺通信時必須進行字節序轉換。

二、套接字地址結構

套接字地址結構用于在網絡編程中存儲通信所需的地址信息。常見的套接字地址結構包括IPv4、IPv6和Unix域套接字地址結構。

1、IPv4 地址結構 (sockaddr_in)

IPv4套接字地址結構定義在<netinet/in.h>中,用于存儲IPv4地址和端口號。

struct sockaddr_in {sa_family_t    sin_family; // 地址族,如AF_INETin_port_t      sin_port;   // 16位端口號,網絡字節序struct in_addr sin_addr;   // 32位IPv4地址,網絡字節序char           sin_zero[8]; // 填充字段,通常置零
};struct in_addr {uint32_t s_addr; // IPv4地址,網絡字節序
};

2、IPv6 地址結構 (sockaddr_in6)

IPv6套接字地址結構用于存儲IPv6地址和端口號。

struct sockaddr_in6 {sa_family_t     sin6_family;   // 地址族,如AF_INET6in_port_t       sin6_port;     // 16位端口號,網絡字節序uint32_t        sin6_flowinfo; // 流信息struct in6_addr sin6_addr;     // 128位IPv6地址,網絡字節序uint32_t        sin6_scope_id; // 范圍ID
};struct in6_addr {unsigned char s6_addr[16]; // IPv6地址,網絡字節序
};

3、通用套接字地址結構 (sockaddr)

socket 網絡編程接口中表示 socket 地址的是結構體 sockaddr,通用套接字地址結構用于在函數參數中傳遞不同類型的地址結構。其定義如下:

struct sockaddr {sa_family_t sa_family; // 地址族char        sa_data[14]; // 協議地址
};
sa_family 成員是地址族類型(sa_family_t)的變量。地址族類型通常與協議族類型對應。常見的協議族和對應的地址族如下圖所示:

4、Unix域套接字地址結構 (sockaddr_un)

Unix域套接字用于本地進程間通信,其地址結構定義在<sys/un.h>中。

struct sockaddr_un {sa_family_t sun_family; // 地址族,AF_UNIXchar        sun_path[108]; // 文件路徑名
};

5、專用 socket 地址結構

TCP/IP 協議族有 sockaddr_in 和 sockaddr_in6 兩個專用 socket 地址結構體,它們分別用于 IPV4 和 IPV6:
/*
sin_family: 地址族 AF_INET
sin_port: 端口號,需要用網絡字節序表示
sin_addr: IPV4 地址結構:s_addr 以網絡字節序表示 IPV4 地址
*/struct in_addr{u_int32_t s_addr;
};
struct sockaddr_in{sa_family_t sin_family;u_int16_t sin_port;struct in_addr sin_addr;
};
struct in6_addr{unsigned char sa_addr[16]; // IPV6 地址,要用網絡字節序表示
};
struct sockaddr_in6{sa_family_t sin6_family; // 地址族:AF_INET6u_inet16_t sin6_port; // 端口號:用網絡字節序表示u_int32_t sin6_flowinfo; // 流信息,應設置為 0struct in6_addr sin6_addr; // IPV6 地址結構體u_int32_t sin6_scope_id; // scope ID,尚處于試驗階段
};

6、套接字地址結構的轉換

在網絡編程中,通常需要將人類可讀的IP地址和端口號轉換為網絡字節序的二進制形式。

字符串轉二進制地址

使用inet_pton函數將點分十進制字符串轉換為二進制地址。

#include <arpa/inet.h>const char *ip_str = "192.168.1.1";
struct in_addr addr;
inet_pton(AF_INET, ip_str, &addr);
二進制地址轉字符串

使用inet_ntop函數將二進制地址轉換為點分十進制字符串。

char ip_str[INET_ADDRSTRLEN];
inet_ntop(AF_INET, &addr, ip_str, INET_ADDRSTRLEN);

7、端口號的轉換

端口號需要轉換為網絡字節序(大端序)后使用。

#include <arpa/inet.h>uint16_t port = 8080;
uint16_t net_port = htons(port); // 主機字節序轉網絡字節序
uint16_t host_port = ntohs(net_port); // 網絡字節序轉主機字節序

三、網絡編程接口

網絡編程接口(API,Application Programming Interface)是不同軟件系統間進行通信和數據交換的標準化方式。通過網絡API,開發者可以調用遠程服務、獲取數據或執行特定操作,而無需了解底層實現細節。
  • socket

socket()創建套接字,成功返回套接字的文件描述符,失敗返回-1

domain: 設置套接字的協議簇, AF_UNIX AF_INET AF_INET6 ;type: 設置套接字的服務類型 SOCK_STREAM SOCK_DGRAM ;protocol: 一般設置為 0,表示使用默認協議

int socket(int domain, int type, int protocol);
  • bind

bind()將 sockfd 與一個 socket 地址綁定,成功返回 0,失敗返回-1

sockfd 是網絡套接字描述符 ;addr 是地址結構 ;addrlen 是 socket 地址的長度

int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
  • listen

listen()創建一個監聽隊列以存儲待處理的客戶連接,成功返回 0,失敗返回-1

sockfd 是被監聽的 socket 套接字 ;backlog 表示處于完全連接狀態的 socket 的上限

int listen(int sockfd, int backlog);
  • accept

accept()從 listen 監聽隊列中接收一個連接,成功返回一個新的連接 socket,該 socket 唯一地標識了被接收的這個連接,失敗返回-1

sockfd 是執行過 listen 系統調用的監聽 socket ;addr 參數用來獲取被接受連接的遠端 socket 地址 ;addrlen 指定該 socket 地址的長度

int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
  • TCP 數據讀寫:

recv()讀取 sockfd 上的數據,buff 和 len 參數分別指定讀緩沖區的位置和大小

send()往 socket 上寫入數據,buff 和 len 參數分別指定寫緩沖區的位置和數據長度

flags 參數為數據收發提供了額外的控制

ssize_t recv(int sockfd, void *buff, size_t len, int flags);ssize_t send(int sockfd, const void *buff, size_t len, int flags);
  • UDP 數據讀寫:

recvfrom()讀取 sockfd 上的數據,buff 和 len 參數分別指定讀緩沖區的位置和大小

src_addr 記錄發送端的 socket 地址 ;addrlen 指定該地址的長度 ;sendto()往 socket 上寫入數據,buff 和 len 參數分別指定寫緩沖區的位置和數據長度 ;dest_addr 指定接收數據端的 socket 地址 ;addrlen 指定該地址的長度

ssize_t recvfrom(int sockfd, void *buff, size_t len, int flags,struct sockaddr* src_addr, socklen_t *addrlen);ssize_t sendto(int sockfd, void *buff, size_t len, int flags,struct sockaddr* dest_addr, socklen_t addrlen);
  • close

close()關閉一個連接,實際上就是關閉該連接對應的 socket

int close(int sockfd);
  • connect

connect()客戶端需要通過此系統調用來主動與服務器建立連接,成功返回 0,失敗返回-1

sockfd 參數是由 socket()返回的一個 socket。 serv_addr 是服務器監聽的 socket 地址 ;addrlen 則指定這個地址的長度

int connect(int sockfd, const struct sockaddr *serv_addr, socklen_t addrlen);

四、TCP編程流程

TCP 提供的是面向連接的、可靠的、字節流服務。TCP 的服務器端和客戶端編程流程如下:

  • socket()方法是用來創建一個套接字,有了套接字就可以通過網絡進行數據的收發。這也是為什么進行網絡通信的程序首先要創建一個套接字。創建套接字時要指定使用的服務類型,使用 TCP 協議選擇流式服務(SOCK_STREAM)。
  • bind()方法是用來指定套接字使用的 IP 地址和端口。IP 地址就是自己主機的地址,如果主機沒有接入網絡,測試程序時可以使用回環地址“127.0.0.1”。端口是一個 16 位的整形值,一般 0-1024 為知名端口,如 HTTP 使用的 80 號端口。這類端口一般用戶不能隨便使用。其次,1024-4096 為保留端口,用戶一般也不使用。4096 以上為臨時端口,用戶可以使用。在Linux 上,1024 以內的端口號,只有 root 用戶可以使用。
  • listen()方法是用來創建監聽隊列。監聽隊列有兩種,一個是存放未完成三次握手的連接,一種是存放已完成三次握手的連接。listen()第二個參數就是指定已完成三次握手隊列的長度。
  • accept()處理存放在 listen 創建的已完成三次握手的隊列中的連接。每處理一個連接,則accept()返回該連接對應的套接字描述符。如果該隊列為空,則 accept 阻塞。
  • connect()方法一般由客戶端程序執行,需要指定連接的服務器端的 IP 地址和端口。該方法執行后,會進行三次握手, 建立連接。
  • send()方法用來向 TCP 連接的對端發送數據。send()執行成功,只能說明將數據成功寫入到發送端的發送緩沖區中,并不能說明數據已經發送到了對端。send()的返回值為實際寫入到發送緩沖區中的數據長度。
  • recv()方法用來接收 TCP 連接的對端發送來的數據。recv()從本端的接收緩沖區中讀取數據,如果接收緩沖區中沒有數據,則 recv()方法會阻塞。返回值是實際讀到的字節數,如果 recv()返回值為 0, 說明對方已經關閉了 TCP 連接。
  • close()方法用來關閉 TCP 連接。此時,會進行四次揮手。

代碼示例:

服務端

編輯
?
?
代碼示例:
服務端:
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdlib.h>int main(){
int sockfd = socket(AF_INET,SOCK_STREAM,0);//創建套接字
if( sockfd == -1 ){
exit(1);
}
struct sockaddr_in saddr;//定義ipv4地址,
memset(&saddr,0,sizeof(saddr));//清空
saddr.sin_family = AF_INET;//填充地址圖
saddr.sin_port = htons(6000);//填充端口,大端
saddr.sin_addr.s_addr = inet_addr("127.0.0.1");//ip地址
int res = bind(sockfd,(struct sockaddr*)&saddr,sizeof(saddr));//綁定
if( res == -1){
printf("bind err\n");
exit(1);
}
res = listen(sockfd,5);//創建監聽隊列
if( res == -1){
exit(1);
}
while( 1 ){
struct sockaddr_in caddr;//記錄客戶端地址
socklen_t len = sizeof(caddr);//計算大小
int c = accept(sockfd,(struct sockaddr*)&caddr,&len);//接受套接字,阻塞
char buff[128] = {0};
int num = recv(c,buff,127,0);//read
printf("buff=%s\n",buff);//接收之后,打印
send(c,"ok",2,0);//write,描述符,帶發送的數據,大小,標志位
close(c);
}
close(sockfd);
exit(0);
}

客戶端

客戶端:
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdlib.h>
int main(){
int sockfd = socket(AF_INET,SOCK_STREAM,0);//創建套接字
if( sockfd == -1){
exit(1);
}
struct sockaddr_in saddr;//ipv4地址
memset(&saddr,0,sizeof(saddr));
saddr.sin_family = AF_INET;
saddr.sin_port = htons(6000);
saddr.sin_addr.s_addr = inet_addr("127.0.0.1");
int res = connect(sockfd,(struct sockaddr*)&saddr,sizeof(saddr));
if( res == -1){
printf("connect err\n");
exit(1);
}
printf("input:\n");
char buff[128] = {0};
fgets(buff,128,stdin);//從鍵盤獲取數據
send(sockfd,buff,strlen(buff)-1,0);
memset(buff,0,128);
recv(sockfd,buff,127,0);
printf("buff=%s\n",buff);
close(sockfd);
exit(0);
}

執行結果:

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/91740.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/91740.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/91740.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

網頁操作自動化解決方案:如何用Browser-Use+CPolar提升企業運營效率

文章目錄前言1. 安裝Ollama2. Gemma3模型安裝與運行3. 虛擬環境準備3.1 安裝Python3.2. 安裝conda4. 本地部署Brower Use WebUI4.1 創建一個新conda環境4.2 克隆存儲庫4.3 安裝依賴環境4.4 安裝瀏覽器自動化工具4.5 修改配置信息5. 本地運行測試6. 安裝內網穿透6.1 配置公網地址…

Pycharm的設置過程

20250802 用于記錄pycharm的設置過程 編輯器相關 python語言設置文件注釋 在設置的編輯器部分&#xff0c;按照需求設置模板&#xff01; 函數生成注釋

GaussDB as的用法

通過使用 SQL&#xff0c;可以為表名稱或列名稱指定別名&#xff08;Alias&#xff09;。1 別名的作用SQL 別名用于為表或表中的列提供臨時名稱。 SQL 別名通常用于使列名更具可讀性。 SQL 一個別名只存在于查詢期間。 提高SQL執行效率與編寫SQL代碼效率。2 使用別名的場景在下…

Prim算法

一&#xff0c;prim算法邏輯1.理解&#xff1a;克魯斯卡爾算法關注的是邊&#xff0c;普里姆算法關注的是點把圖中每個頂點比作孤島&#xff0c;點亮一座孤島就可以解鎖附近的孤島每次解鎖的點都是離自身最近的點2.普里姆算法流程a.采用鄰接矩陣表示&#xff0c;考慮要查找最小…

嵌入式學習之硬件——51單片機 1.0

一、基礎知識1.什么是嵌入式&#xff1f;嵌入式以應用為中心&#xff0c;計算機技術為基礎&#xff0c;軟硬件可裁剪的專用計算機系統&#xff1b;2.嵌入式的應用&#xff1f;消費電子、無人駕駛、儲能、新能源........3.嵌入式發展&#xff1f;&#xff08;1&#xff09;第一階…

51c大模型~合集161

自己的原文哦~ https://blog.51cto.com/whaosoft/14079111 #這家國內公司&#xff0c;在給xx智能技術棧做「通解」 打通機器人智能化的關鍵&#xff1a;眼腦手。 xx智能&#xff08;Embodied Intelligence&#xff09;是 AI 領域里熱度極高的賽道&#xff1a;給大模型…

Linux9 root密碼修改

開機按e進入在linux行即quiet后面輸入rd.break ctrlx進入內核輸入mount -o remount,rw /sysrootchroot /sysrootpasswd root即可修改密碼輸入touch /.autorelabelexitexit等待即可

提示詞增強工程(Prompt Enhancement Engineering)白皮書草稿

提示詞增強工程&#xff08;Prompt Enhancement Engineering&#xff09;白皮書草稿 作者&#xff1a; 技術人進化社 Email&#xff1a;2819699195qq.com 日期&#xff1a; 2025年7月30日 1. 引言 隨著大型語言模型&#xff08;LLM&#xff09;能力的飛速發展&#xff0c;如何高…

電路元器件

電流單位 電壓 電阻單位 電阻的決定式 歐姆定律 交流電和直流電 交流電 串聯電路 并聯電路 在線模擬器 Circuitjs web 在線電路模擬器 下載

廣泛分布于內側內嗅皮層全層的速度細胞(speed cells)對NLP中的深層語義分析的積極影響和啟示

速度細胞&#xff08;Speed Cells&#xff09;作為內側內嗅皮層&#xff08;MEC&#xff09;的核心神經元&#xff0c;通過編碼運動速度信息與網格細胞協同實現動態路徑整合。這一神經機制為自然語言處理&#xff08;NLP&#xff09;的深層語義分析提供了以下關鍵啟示和影響&am…

sql中的多表查詢

在SQL中&#xff0c;多表查詢用于從多個表中組合數據&#xff0c;常見的方法包括 ?連接查詢&#xff08;JOIN&#xff09;?? 和 ?子查詢。以下是詳細說明和示例&#xff1a;一、連接查詢&#xff08;JOIN&#xff09;通過關聯字段將多個表的數據合并&#xff0c;分為以下幾…

Ruby 面向對象編程深入解析

Ruby 面向對象編程深入解析 引言 Ruby 作為一種動態、解釋型、面向對象的語言,自1995年由日本程序員Yukihiro Matsumoto創造以來,憑借其簡潔、靈活和強大的面向對象特性,在全球范圍內獲得了廣泛的認可。本文將深入探討Ruby的面向對象編程(OOP)特性,幫助讀者更好地理解和…

Baumer工業相機堡盟工業相機如何通過YoloV8深度學習模型實現圍欄羊駝的檢測識別(C#代碼,UI界面版)

Baumer工業相機堡盟工業相機如何通過YoloV8深度學習模型實現圍欄羊駝的檢測識別&#xff08;C#代碼&#xff0c;UI界面版&#xff09;工業相機使用YoloV8模型實現圍欄羊駝的檢測識別工業相機通過YoloV8模型實現圍欄羊駝的檢測識別的技術背景在相機SDK中獲取圖像轉換圖像的代碼分…

如何利用 rowid 在OceanBase 中處理大表時提效

本文作者&#xff1a;張瑞遠&#xff0c;現主要從事電信級IT系統及數據庫的規劃設計、架構設計、運維實施、運維服務、故障處理、性能優化等工作&#xff0c;曾經從事銀行、證券數倉設計、開發、優化類工作&#xff0c;持有Orale OCM,MySQL OCP及國產代表數據庫認證。 獲得包括…

【從0開始學習Java | 第4篇】類和對象

文章目錄&#x1f44f;類和對象的概念什么是類&#xff1f;什么是對象&#xff1f;&#x1f95d;構造方法如何創建一個對象&#xff1f;&#x1f95d;對象內存布局完整應用 - 編寫一個類&#xff1a;人&#xff0c;其具備年齡、性別、姓名等基礎屬性&#xff0c;并實例化一個人…

Synopsys:默認報告精度(report_default_significant_digits變量)

相關閱讀 Synopsyshttps://blog.csdn.net/weixin_45791458/category_12812219.html?spm1001.2014.3001.5482 在使用report_timing之類的報告命令時&#xff0c;可以使用-significant_digits選項指定報告的精度&#xff0c;在不使用該選項的情況下&#xff0c;命令使用由repor…

2025年藍橋杯青少圖形化編程國考真題——擺放玩具

編程實現擺放玩具。&#xff08;角色非源素材&#xff09;擺放規則&#xff1a;在方格中擺放玩具&#xff0c;每個方格只能擺放一個&#xff0c;并且如果某個方格中已經擺放了玩具&#xff0c;那么與之上、下、左、右相鄰的四個方格中無法再擺放同種玩具。具體要求1&#xff09…

Android 應用的安裝流程

安裝流程總覽&#xff1a; 用戶觸發安裝->系統驗證APK的合法性->解析APK元數據->檢查權限和存儲空間->復制APK到目標位置->生成應用私有數據->注冊組件到系統->安裝完成 關鍵步驟&#xff1a; 1.用戶觸發安裝&#xff1a;a.通過應用商店b.通過adb命令c.通…

基于 Amazon Bedrock 與 Anthropic Claude 3 智能文檔處理方案:從掃描件提取到數據入庫全流程實踐

基于 Amazon Bedrock 與 Anthropic Claude 3 智能文檔處理方案&#xff1a;從掃描件提取到數據入庫全流程實踐 文章目錄基于 Amazon Bedrock 與 Anthropic Claude 3 智能文檔處理方案&#xff1a;從掃描件提取到數據入庫全流程實踐方案架構前提準備&#xff1a;亞馬遜云科技注冊…

深入淺出設計模式——創建型模式之單例模式 Singleton

文章目錄“天上天下&#xff0c;唯我獨尊”——單例模式單例模式簡介單例模式結構餓漢式懶漢式客戶端示例運行結果單例模式總結構建型模式 Creational Patterns 小結 Summary代碼倉庫“天上天下&#xff0c;唯我獨尊”——單例模式 你能在電腦上調出兩個Windows任務管理器嗎&a…