【網絡】Socket套接字

目錄

一、端口號

二、初識TCP/UDP協議

三、網絡字節序

3.1 概念

3.2 常用API

四、Socket套接字

4.1 概念

4.2 常用API

(1)socket

(2)bind

sockaddr結構

(3)listen

(4)accept

(5)connect

(6)recvfrom

(7)sendto

4.3 地址轉換函數

(1)inet_aton

(2)inet_addr

(3)inet_pton


一、端口號

網絡協議棧中的下三層,主要解決的是如何將數據安全可靠的送到遠端機器上的問題。

在上層,用戶使用特定的應用層軟件完成數據的發送和接收。而軟件在啟動后變為了進程,因此我們日常網絡通信的本質,就是進程間通信!

問題1:一個進程通過網絡將數據傳輸到遠端主機上時,如何區分要把數據傳給主機上的哪個進程呢?

實際上,每個進程都有屬于自己的端口號(port)。IP地址用于標識主機的唯一性,而端口號則用于標識一個進程在該主機中的唯一性。所以網絡通信中我們不止需要IP地址來找到目標主機,還需要端口號找到目標進程

端口號是傳輸層協議的內容,是一個2字節16位的整數,用于在主機中標識一個進程的唯一性。因此通過IP地址+端口號就能標識全網唯一的一個進程

問題2:進程PID也可以標識進程在主機中的唯一性,為什么還要有端口號?

  • 不是所有的進程都需要進行網絡通信,但是所有進程都要有自己的PID
  • 將系統和網絡功能解耦

一個端口號只能被一個進程綁定,但一個進程可以綁定多個端口號


二、初識TCP/UDP協議

傳輸層協議(TCP和UDP)的數據段中分別記錄了源端口號和目的端口號,用來描述數據是從哪個進程發的、要發給哪個進程

關于TCP和UDP協議,我們首先對它們有一個簡單且直觀的認識,后續再進行深入了解

TCP(Transmission Control Protocol,傳輸控制協議):

  • 面向連接
  • 保證數據傳輸可靠性
  • 面向字節流

UDP(User Datagram Protocol,用戶數據報協議):

  • 無連接
  • 不保證數據傳輸可靠性
  • 面向數據報


三、網絡字節序

3.1 概念

內存中的多字節數據相對于內存地址而言有大端和小端的區別,因此主機也分為大端機和小端機

讓我們回顧一下大端和小端的概念?

大端:數據的高位存儲在內存的低位

小端:數據的高位存儲在內存的高位

不止是內存,網絡數據流中同樣有大端小端之分。發送方在發送數據時通常將發送緩沖區中的數據按內存地址從低到高的順序發出,接收方將數據保存在接收緩沖區中,也是按內存地址從低到高的順序保存。

問題在于,不同類型的主機在跨網絡互相傳輸數據時就可能導致問題。例如大端機將數據發送給小端機,就可能導致數據的錯亂

因此TCP/IP協議規定,發送到網絡中的數據流應統一按照大端字節序發送。也就是說不論是大端機還是小端機,都要按照TCP/IP規定的網絡字節序來發送或接收數據

所以如果發送數據的主機是小端機,必須先將數據轉換成大端字節序后再發送。在后面調用套接字相關API時,我們也通常需要對端口號和ip地址進行網絡字節序轉換。

3.2 常用API

為了讓網絡程序具有可移植性,我們可以使用下列庫函數進行主機字節序和網絡字節序的轉換

#include <arpa/inet.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);

其中h表示host,n表示network,l表示32位長整型,s表示16位短整型

例如htonl就是將32位長整型從主機字節序轉為網絡字節序,適用于轉換IP地址

如果主機字節序本身是小端,調用對應庫函數后則會將參數做相應大小端轉換后返回;如果主機字節序已經是大端了,則不作改變


四、Socket套接字

4.1 概念

套接字(Socket)是一種獨立于協議的網絡編程接口,是對網絡中不同主機的應用進程之間進行雙向通信的端點的抽象。套接字上聯應用進程,下接網絡協議棧,是應用程序與網絡協議棧進行交互的接口。

套接字包括?IP 地址和端口號兩個部分,可以用來區分不同的進程之間的數據傳輸。傳輸層使用的協議不同,套接字的種類也會發生相應的改變。

在Linux中,套接字的本質也是文件,因此有對應的網絡文件描述符,用戶通過網絡文件描述符對套接字進行操作。

4.2 常用API

(1)socket

#include <sys/types.h>
#include <sys/socket.h>int socket(int domain, int type, int protocol);

socket函數類似于打開文件的操作,會創建套接字并返回一個網絡文件描述符,其中:

  • domain:協議域,又稱協議族,例如AF_INET代表IPv4協議,AF_INET6代表IPv6協議
  • type:指定socket類型,例如流式套接字SOCK_STREAM(TCP)和數據報套接字SOCK_DGRAM(UDP)
  • protocal:指定協議信息,常見的有IPPROTO_TCP、IPPROTO_UDP等,通常設置為0代表自動選擇套接字類型對應的默認協議

創建成功返回一個網絡文件描述符,失敗返回-1并設置環境變量errno

例如:

(2)bind

#include <sys/types.h>
#include <sys/socket.h>int bind(int socket, const struct sockaddr *address, socklen_t address_len);

bind函數用于將一個服務的ip地址和端口號綁定到一個套接字上,一般是服務端在綁定監聽套接字時會用到。客戶端則不必要調用bind綁定,因為客戶端的端口號由內核自動分配

其中:

  • socket:待綁定的網絡文件描述符
  • address:指向一個sockaddr結構體的指針,該結構體包含了要綁定的ip地址和端口號
  • address_len:address指向的結構體大小

成功綁定返回0, 失敗返回-1并設置errno

例如:

uint16_t port = 8888; //端口號
string ip = "127.0.0.1"; //字符串格式的ip地址
int sockfd = socket(AF_INET, SOCK_STREAM, 0); //創建套接字
if (sockfd < 0)
{// 創建套接字失敗時//...
}
//填充結構體字段
struct sockaddr_in local;
memset(&local, 0, sizeof(local));
local.sin_family = AF_INET; //IPv4協議
local.sin_port = htons(port);
inet_aton(ip.c_str(), &(local.sin_addr)); 
if (bind(sockfd, (struct sockaddr *)&local, sizeof(local)) < 0) // 綁定
{//綁定失敗時//...
}

填充結構體字段時,需要對端口號進行網絡字節序轉換和對字符串格式的ip地址轉四字節ip地址后再填充到sockaddr_in結構體中

關于ip地址的格式轉換函數會在后面提及,這里先簡單提一下sockaddr的結構

sockaddr結構

關于socket的API是一層抽象的網絡編程接口,適用于各種底層網絡協議,如IPv4、IPv6等,但是各種網絡協議的地址格式并不相同。

例如IPv4的地址用sockaddr_in結構體表示,其中包含16位地址類型、16位端口號和32位ip地址

不同的結構體中,前16位都填充了ip地址的協議類型,因此我們可以統一用struct sockaddr*類型接收,取得結構體首地址后按位數獲取地址類型字段就可以確定是哪一種結構體了。

在使用Unix域套接字進行本機進程間通信時,綁定時就得使用sockaddr_un結構

(3)listen

#include <sys/types.h> 
#include <sys/socket.h>int listen(int sockfd, int backlog);

listen函數常用于服務端監聽來自客戶端的TCP連接請求,通常在調用bind函數后使用,成功返回0,失敗返回-1并設置errno

其中:

  • sockfd:將被設置為監聽狀態的網絡文件描述符
  • backlog:設置全連接隊列的長度(全連接隊列用于臨時維護未被上層accept的已經建立好的連接,長度為backlog+1)

例如:

uint16_t port = 8888; //端口號
string ip = "127.0.0.1"; //字符串格式的ip地址
int sockfd = socket(AF_INET, SOCK_STREAM, 0); //創建套接字
if (sockfd < 0)
{// 創建套接字失敗時//...
}
//填充結構體字段
struct sockaddr_in local;
memset(&local, 0, sizeof(local));
local.sin_family = AF_INET; //IPv4協議
local.sin_port = htons(port);
inet_aton(ip.c_str(), &(local.sin_addr)); 
if (bind(sockfd, (struct sockaddr *)&local, sizeof(local)) < 0) // 綁定
{//綁定失敗時//...
}
if (listen(sockfd, 10) < 0) // 將套接字設置為監聽狀態,全連接隊列最多存放10+1個連接
{//監聽失敗時//...
}

(4)accept

#include <sys/types.h>
#include <sys/socket.h>int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);

accept函數常用于服務端從全連接隊列中接收來自客戶端的TCP連接請求并創建一個新的套接字,通常用于listen函數后。成功會返回該套接字的文件描述符用來負責后續的數據通信服務,失敗返回-1并設置errno。

如果全連接隊列中暫時沒有Tcp連接請求,accept函數將阻塞等待直到有客戶端發起連接請求(除非服務器處于非阻塞狀態)

其中:

  • sockfd:被綁定并設置為監聽狀態的套接字對應的文件描述符
  • addr:指向sockaddr結構體的指針,用于填充客戶端對應的地址信息。設置為NULL表示不關心客戶端地址
  • addrlen:指向socklen_t的指針,表示addr的大小

例如:

uint16_t port = 8888; //端口號
string ip = "127.0.0.1"; //字符串格式的ip地址
int sockfd = socket(AF_INET, SOCK_STREAM, 0); //創建套接字
if (sockfd < 0)
{// 創建套接字失敗時//...
}
//填充結構體字段
struct sockaddr_in local;
memset(&local, 0, sizeof(local));
local.sin_family = AF_INET; //IPv4協議
local.sin_port = htons(port);
inet_aton(ip.c_str(), &(local.sin_addr)); 
if (bind(sockfd, (struct sockaddr *)&local, sizeof(local)) < 0) // 綁定
{// 綁定失敗時//...
}
if (listen(sockfd, 10) < 0) // 將套接字設置為監聽狀態,全連接隊列最多存放10+1個連接
{// 監聽失敗時//...
}
struct sockaddr_in client; // 存儲客戶端信息的結構體
socklen_t len = sizeof(client);
int newfd = accept(sockfd, (struct sockaddr *)&client, &len); // sockfd只負責獲取連接,newfd負責后續的數據通信服務
if (newfd < 0)
{// 接收失敗時//...
}

(5)connect

#include <sys/types.h>
#include <sys/socket.h>int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

connect函數常用于發起建立網絡連接的請求,成功返回0,失敗返回-1并設置errno

其中:

  • sockfd:調用socket函數創建套接字成功后返回的文件描述符
  • addr:指向sockaddr結構體的指針,其中包含了準備建立連接的目標服務器地址信息
  • addrlen:addr指向的結構體的大小

例如:

string serverip = "127.0.0.1";
uint16_t serverport = 8888;
int sockfd = socket(AF_INET, SOCK_STREAM, 0); // 創建套接字
if (sockfd < 0)
{// 創建套接字失敗時//...
}
// 填充結構體字段
struct sockaddr_in server;
memset(&server, 0, sizeof(server));
server.sin_family = AF_INET;
server.sin_port = htons(serverport);
inet_pton(AF_INET, serverip.c_str(), &(server.sin_addr));
// 發起連接
int n = connect(sockfd, (struct sockaddr *)&server, sizeof(server));
if (n < 0)
{// 連接發起失敗時//...
}

(6)recvfrom

#include <sys/types.h>
#include <sys/socket.h>ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,struct sockaddr *src_addr, socklen_t *addrlen);

recvfrom常用于使用UDP協議(或其他無連接的數據報服務)時從套接字中讀取數據,成功返回讀取到的字節數,當套接字已經關閉時返回0,出錯返回-1并設置errno

其中:

  • sockfd:已打開的套接字文件描述符
  • buf:指向用于存放接收到的數據的緩沖區的指針
  • len:緩沖區大小
  • flags:控制接收行為的標志,通常設置為0表示阻塞模式
  • src_addr:指向一個sockaddr結構體,存儲數據來源方的地址信息
  • addrlen:代表sockaddr結構體的大小

例如:

int sockfd = socket(AF_INET, SOCK_DGRAM, 0); // 創建套接字
if (sockfd < 0)
{//...
}
char buffer[1024];
sockaddr_in temp;
socklen_t len = sizeof(temp);
ssize_t s = recvfrom(sockfd, buffer, sizeof(buffer) - 1, 0,(struct sockaddr *)&temp, &len); // 接收服務端返回的消息
//...

(7)sendto

#include <sys/types.h>
#include <sys/socket.h>ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,const struct sockaddr *dest_addr, socklen_t addrlen);

sendto函數常用于使用UDP協議時通過指定的socket將數據發送到目標主機,成功返回實際發送的字節數,失敗返回-1并設置errno

其中:

  • sockfd:已打開的套接字文件描述符
  • buf:指向要發送的數據
  • len:要發送的數據長度
  • flags:標志位,通常設置為0
  • dest_addr:指向存儲目標主機地址信息的sockaddr結構體
  • addrlen:結構體大小

4.3 地址轉換函數

sockaddr_in結構體中的成員sin_addr表示32位的ip地址,但我們日常中見到的ip地址通常是點分十進制格式的字符串表示的。通過一些函數可以實現ip地址在兩種格式間的轉換。

字符串轉32位ip地址:

(1)inet_aton

#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>int inet_aton(const char *cp, struct in_addr *inp);

其中:

  • cp:待轉換的點分十進制ip地址字符串
  • inp:指向in_addr結構體的指針,存儲轉換后的網絡字節序ip地址

in_addr內部存放了一個32位整型用于存儲轉換后的ip地址,其結構如下:

typedef uint32_t in_addr_t;
struct in_addr
{in_addr_t s_addr;
};

例如:

struct sockaddr_in addr;
inet_aton("127.0.0.1", &addr.sin_addr);

?

(2)inet_addr

#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>in_addr_t inet_addr(const char *cp);

其中cp是待轉換的點分十進制ip地址字符串

例如:

struct sockaddr_in addr;
addr.sin_addr.s_addr = inet_addr("127.0.0.1");

(3)inet_pton

#include <arpa/inet.h>int inet_pton(int af, const char *src, void *dst);

其中:

  • af:協議族
  • src:指向點分十進制ip地址字符串的指針
  • dst:指向用于存儲轉換后ip地址的內存區域

網絡字節序ip地址轉點分十進制的函數有inet_ntoa、inet_ntop,有興趣的可以自行查閱文檔

完.

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

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

相關文章

內聯函數/函數重載/函數參數缺省

一、內聯函數 為了減少函數調用的開銷 在函數定義前加“inline”關鍵字&#xff0c;即可定義內聯函數 二、函數重載 1.名字相同 2.參數個數或者參數類型不同 編譯器根據調用語句實參的個數和類型判斷應該調用哪個函數 三、函數的缺省參數 定義函數的時候可以讓最右邊的連…

基于神經網絡的文本分類的設計與實現

標題:基于神經網絡的文本分類的設計與實現 內容:1.摘要 在信息爆炸的時代&#xff0c;大量文本數據的分類處理變得至關重要。本文旨在設計并實現一種基于神經網絡的文本分類系統。通過構建合適的神經網絡模型&#xff0c;采用公開的文本數據集進行訓練和測試。在實驗中&#x…

Baklib內容中臺的核心定位是什么?

構建企業級知識中樞 在數字化轉型趨勢下&#xff0c;Baklib內容中臺通過構建企業級知識中樞&#xff0c;實現了從碎片化信息到體系化資產的躍遷。其核心能力體現為對多源內容的智能聚合與結構化存儲&#xff0c;支持從文檔、圖片到視頻的全格式整合&#xff0c;并通過語義標簽…

藍耘平臺API深度剖析:如何高效實現AI應用聯動

目錄 一、藍耘平臺簡介 1.1 藍耘通義大模型 1.2 藍耘云計算資源 1.3 藍耘API與微服務 二、 藍耘平臺應用聯動場景 2.1 數據采集與預處理聯動 2.2 模型推理與后端服務聯動 2.3 跨平臺聯動 三、藍耘平臺注冊體驗功能 3.1 注冊 3.2 體驗藍耘MaaS平臺如何使用海螺AI生成視頻…

《大語言模型賦能證券業開發安全:海云安技術方案在上交所專刊發表》

近日&#xff0c;海云安《大語言模型在證券業開發安全領域的探索與實踐》技術方案經過上海證券交易所&#xff08;以下簡稱”上交所“&#xff09;行業專家評審后正式收錄于《交易技術前沿——網絡安全專刊&#xff08;2025年第1期 總第61期&#xff09;》。 證券信息技術研究…

第三課:Stable Diffusion圖生圖入門及應用

文章目錄 Part01 圖生圖原理Part02 圖生圖基本流程Part03 隨機種子作用解析Part04 圖生圖的拓展應用 Part01 圖生圖原理 當提示詞不能足夠表達用戶需求的時候&#xff0c;加入圖片能讓AI更好的理解你的想法圖片上的像素信息會在加噪和去噪的過程中&#xff0c;作為一種特征反映…

將網絡安全和第三方風險管理與業務目標相結合

在網絡安全風險領域&#xff0c;我們經常遇到與企業語言不通的問題。這可能導致網絡安全風險管理計劃得不到支持。當發現網絡安全風險時&#xff0c;困難在于以符合組織語言和目標的方式來表達它。 第三方風險屬于另一個灰色地帶。在組織內部&#xff0c;許多利益相關者&#…

使用Github項目nghttp3的樣例學習HTTP/3

文章目錄 前言一、HTTP3測試 in Ubuntu1.1. 基本軟件1.2. gcc/g1.2.1. Ubuntu221.2.2. Ubuntu201.2.2.1. 必備庫1.2.2.1.1. gmp1.2.2.1.2. mpfr1.2.2.1.3. mpc 1.2.2.2. 安裝 1.3. libev > 4.11&#xff08;備用&#xff09;1.3.1. 安裝1.3.2. 測試 1.4. nghttp31.5. ngtcp2…

uniapp 在app上 字體如何不跟著系統字體大小變

在UniApp開發中&#xff0c;默認情況下App的字體可能會跟隨系統字體設置而變化。如果你希望保持固定的字體樣式&#xff0c;不隨系統字體設置改變&#xff0c;可以采用以下幾種方法&#xff1a; 方法一&#xff1a;全局CSS設置 在App.vue的樣式中添加以下CSS&#xff1a; /*…

跨域問題的解決方案

一、跨域問題的本質 1.1 同源策略的三要素 瀏覽器的同源策略&#xff08;Same-Origin Policy&#xff09;要求請求的 協議、域名、端口 完全一致&#xff0c;否則視為跨域&#xff1a; 協議不同&#xff1a;http 與 https域名不同&#xff1a;a.com 與 b.com端口不同&#x…

Linux 上使用 Docker 部署 Kafka 集群

在 Linux 上使用 Docker 部署 Kafka 集群的步驟如下 1. 準備工作 確保已安裝&#xff1a; Docker Docker Compose 2. 創建 Docker Compose 文件 (docker-compose.yml) version: 3.8services:zookeeper:image: wurstmeister/zookeepercontainer_name: zookeeperports:- &quo…

【性能優化點滴】odygrd/quill 中一個簡單的標記位作用--降低 IO 次數

在 StreamSink 類中&#xff0c;成員變量 _write_occurred 的作用是 跟蹤自上次刷新&#xff08;Flush&#xff09;以來是否有寫入操作發生&#xff0c;其核心目的是 優化 I/O 性能。以下是詳細解析&#xff1a; _write_occurred 的作用 1. 避免不必要的刷新&#xff08;Flush…

Ubuntu Linux安裝PyQt5并配置Qt Designer

一 安裝 PyQt5 借助 apt 包管理器來安裝 PyQt5 及其相關的開發工具&#xff1a; sudo apt install python3-pyqt5 pyqt5-dev-tools 假如報錯&#xff0c; You might want to run apt --fix-broken install to correct these. 直接執行&#xff1a; sudo apt --fix-…

2025清華大學:DeepSeek教程全集(PDF+視頻精講,共10份).zip

一、資料列表 第一課&#xff1a;Deepseek基礎入門 第二課&#xff1a;DeepSeek賦能職場 第三課&#xff1a;普通人如何抓住DeepSeek紅利 第四課&#xff1a;讓科研像聊天一樣簡單 第五課&#xff1a;DeepSeek與AI幻覺 第六課&#xff1a;基于DeepSeek的AI音樂詞曲的創造法 第…

容器C++

string容器 string構造函數 #include<iostream> using namespace std; #include<string.h> void test01() {string s1;//默認構造const char* str "hello world";string s2(str);//傳入char*cout << "s2" << s2 << endl;s…

【2.項目管理】2.4 Gannt圖【甘特圖】

甘特圖&#xff08;Gantt&#xff09;深度解析與實踐指南 &#x1f4ca; 一、甘特圖基礎模板 項目進度表示例 工作編號工作名稱持續時間(月)項目進度&#xff08;周&#xff09;1需求分析3▓▓▓???????2設計建模3?▓▓▓??????3編碼開發3.5???▓▓▓▓??…

C++List模擬實現|細節|難點|易錯點|全面解析|類型轉換|

目錄 1.模擬代碼全部 2.四大塊代碼理解 1.最底層&#xff1a;ListNode部分 2.第二層&#xff1a;ListIterator部分 3.第三層&#xff1a;ReserveListIterator部分 4最終層&#xff1a;List 1.模擬代碼全部 using namespace std; template<class T> struct ListNode …

【深度學習與實戰】2.1、線性回歸模型與梯度下降法先導

import numpy as np# 數據準備 X np.array([1, 2, 3]) y np.array([3, 5, 7])# 參數初始化 w0, w1 0, 0 alpha 0.1 n len(X)# 迭代10次 for epoch in range(10):# 計算預測值y_pred w1 * X w0# 計算梯度grad_w0 (1/n) * np.sum(y_pred - y)grad_w1 (1/n) * np.sum((y_…

銳捷EWEB路由器 timeout.php任意文件上傳漏洞代碼審計(DVB-2025-9003)

免責聲明 僅供網絡安全研究與教育目的使用。任何人不得將本文提供的信息用于非法目的或未經授權的系統測試。作者不對任何由于使用本文信息而導致的直接或間接損害承擔責任。如涉及侵權,請及時與我們聯系,我們將盡快處理并刪除相關內容。 一:產品介紹 銳捷EWEB路由器是銳…

flask開發中設置Flask SQLAlchemy 的 db.Column 只存儲非負整數(即 0 或正整數)

如果你想控制一個 Flask SQLAlchemy 的 db.Column 只存儲非負整數&#xff08;即 0 或正整數&#xff09;&#xff0c;你可以在模型中使用驗證來確保這一點。一種常見的方法是使用模型的 validate 方法或者在執行插入或更新操作時進行檢查。 以下是實現這一目標的幾種方法&…