TCP /UDP協議的 socket 調用的過程

在傳輸層有兩個主流的協議 TCP 和 UDP,socket 程序設計也是主要操作這兩個協議。這兩個協議的區別是什么呢?通常的答案是下面這樣的。

  • TCP 是面向連接的,UDP 是面向無連接的。
  • TCP 提供可靠交付,無差錯、不丟失、不重復、并且按序到達;UDP 不提供可靠交付,不保證不丟失,不保證按順序到達。
  • TCP 是面向字節流的,發送時發的是一個流,沒頭沒尾;UDP 是面向數據報的,一個一個地發送。
  • TCP 是可以提供流量控制和擁塞控制的,既防止對端被壓垮,也防止網絡被壓垮。

從本質上來講,所謂的建立連接,其實是為了在客戶端和服務端維護連接,而建立一定的數據結構來維護雙方交互的狀態,并用這樣的數據結構來保證面向連接的特性。TCP 無法左右中間的任何通路,也沒有什么虛擬的連接,中間的通路根本意識不到兩端使用了 TCP 還是 UDP。

所謂的連接,就是兩端數據結構狀態的協同,兩邊的狀態能夠對得上。符合 TCP 協議的規則,就認為連接存在;兩面狀態對不上,連接就算斷了。

所謂的可靠,也是兩端的數據結構做的事情。不丟失其實是數據結構在“點名”,順序到達其實是數據結構在“排序”,面向數據流其實是數據結構將零散的包,按照順序捏成一個流發給應用層。總而言之,“連接”兩個字讓人誤以為功夫在通路,其實功夫在兩端。

socket 函數用于創建一個 socket 的文件描述符,唯一標識一個 socket。我們把它叫作文件描述符,因為在內核中,我們會創建類似文件系統的數據結構,并且后續的操作都有用到它。

socket 函數有三個參數。

  • domain:表示使用什么 IP 層協議。AF_INET 表示 IPv4,AF_INET6 表示 IPv6。
  • type:表示 socket 類型。SOCK_STREAM,顧名思義就是 TCP 面向流的,SOCK_DGRAM 就是 UDP 面向數據報的,SOCK_RAW 可以直接操作 IP 層,或者非 TCP 和 UDP 的協議。例如 ICMP。
  • protocol 表示的協議,包括 IPPROTO_TCP、IPPTOTO_UDP。

通信結束后,我們還要像關閉文件一樣,關閉 socket。

針對 TCP,我們應該如何編程。

TCP 的服務端要先監聽一個端口,一般是先調用 bind 函數,給這個 socket 賦予一個端口和 IP 地址。

int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);struct sockaddr_in {__kernel_sa_family_t  sin_family;  /* Address family    */__be16    sin_port;  /* Port number      */struct in_addr  sin_addr;  /* Internet address    *//* Pad to size of `struct sockaddr'. */unsigned char    __pad[__SOCK_SIZE__ - sizeof(short int) -sizeof(unsigned short int) - sizeof(struct in_addr)];
};struct in_addr {__be32  s_addr;
};

其中,sockfd 是上面我們創建的 socket 文件描述符。在 sockaddr_in 結構中,sin_family 設置為 AF_INET,表示 IPv4;sin_port 是端口號;sin_addr 是 IP 地址。

服務端所在的服務器可能有多個網卡、多個地址,可以選擇監聽在一個地址,也可以監聽 0.0.0.0 表示所有的地址都監聽。服務端一般要監聽在一個眾所周知的端口上,例如,Nginx 一般是 80,Tomcat 一般是 8080。

如果你看上面代碼中的數據結構,里面的變量名稱都有“be”兩個字母,代表的意思是“big-endian”。如果在網絡上傳輸超過 1 Byte 的類型,就要區分大端(Big Endian)和小端(Little Endian)。

最低位放在最后一個位置,我們叫作小端,最低位放在第一個位置,叫作大端。TCP/IP 棧是按照大端來設計的,而 x86 機器多按照小端來設計,因而發出去時需要做一個轉換。

接下來,就要建立 TCP 的連接了,也就是著名的三次握手,其實就是將客戶端和服務端的狀態通過三次網絡交互,達到初始狀態是協同的狀態。下圖就是三次握手的序列圖以及對應的狀態轉換。

接下來,服務端要調用 listen 進入 LISTEN 狀態,等待客戶端進行連接。

int listen(int sockfd, int backlog);

連接的建立過程,也即三次握手,是 TCP 層的動作,是在內核完成的,應用層不需要參與。

接著,服務端只需要調用 accept,等待內核完成了至少一個連接的建立,才返回。如果沒有一個連接完成了三次握手,accept 就一直等待;如果有多個客戶端發起連接,并且在內核里面完成了多個三次握手,建立了多個連接,這些連接會被放在一個隊列里面。accept 會從隊列里面取出一個來進行處理。如果想進一步處理其他連接,需要調用多次 accept,所以 accept 往往在一個循環里面。

int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);

接下來,客戶端可以通過 connect 函數發起連接。

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

我們先在參數中指明要連接的 IP 地址和端口號,然后發起三次握手。內核會給客戶端分配一個臨時的端口。一旦握手成功,服務端的 accept 就會返回另一個 socket。

這里需要注意的是,監聽的 socket 和真正用來傳送數據的 socket,是兩個 socket,一個叫作監聽 socket,一個叫作已連接 socket。成功連接建立之后,雙方開始通過 read 和 write 函數來讀寫數據,就像往一個文件流里面寫東西一樣。

針對 UDP 應該如何編程。

UDP 是沒有連接的,所以不需要三次握手,也就不需要調用 listen 和 connect,但是 UDP 的交互仍然需要 IP 地址和端口號,因而也需要 bind。

對于 UDP 來講,沒有所謂的連接維護,也沒有所謂的連接的發起方和接收方,甚至都不存在客戶端和服務端的概念,大家就都是客戶端,也同時都是服務端。只要有一個 socket,多臺機器就可以任意通信,不存在哪兩臺機器是屬于一個連接的概念。因此,每一個 UDP 的 socket 都需要 bind。每次通信時,調用 sendto 和 recvfrom,都要傳入 IP 地址和端口。

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

TCP 協議的 socket 調用的過程:

  • 服務端和客戶端都調用 socket,得到文件描述符;
  • 服務端調用 listen,進行監聽;
  • 服務端調用 accept,等待客戶端連接;
  • 客戶端調用 connect,連接服務端;
  • 服務端 accept 返回用于傳輸的 socket 的文件描述符;
  • 客戶端調用 write 寫入數據;服務端調用 read 讀取數據。

此文章為11月Day23學習筆記,內容來源于極客時間《趣談Linux操作系統》,推薦該課程。

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

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

相關文章

Selenium介紹及基本使用方法

Selenium是一個開源、免費、簡單、靈活,對Web瀏覽器支持良好的自動化測試工具,在UI自動化、爬蟲等場景下是十分實用的,能夠熟練掌握并使用Selenium工具可以大大的提高效率。 Selenium簡介 Selenium支持多平臺、多瀏覽器、多語言去實現自動化…

深入理解強化學習——馬爾可夫決策過程:動作價值函數

分類目錄:《深入理解強化學習》總目錄 不同于馬爾可夫獎勵過程,在馬爾可夫決策過程中,由于動作的存在,我們額外定義一個動作價值函數(Action-value Function)。我們用 Q π ( s , a ) Q^\pi(s, a) Qπ(s,a)…

線程提交線程到線程池,有幾種方式,哪一種方式是工作中不能使用的,無法捕捉異常,線程池的拒絕策略,線程池的提交方式

線程池的工作原理 JDK中提交線程到線程池,有幾種方式,哪一種方式是工作中不能使用的,無法捕捉異常 兩種提交任務的方法 ExecutorService 提供了兩種提交任務的方法: execute():提交不需要返回值的任務 submit()&a…

【C語言】多組輸入

C系列文章目錄 目錄 C系列文章目錄 一、什么是多組輸入? 二、如何使用多組輸入 2.1,試題舉例講解 2.2,錯誤解法 2.3,我們實現多組輸入的思路 2.4,第一種正確的解法 2.5,第二種正確的解法 2.6&…

Python入門教程 | Python3 字典(dict)

Python3 字典 字典是另一種可變容器模型,且可存儲任意類型對象。 Python3中的字典是一種無序、可變、可迭代的數據結構,它由鍵(key)和對應的值(value)組成。字典在Python中被視為可變對象,這意…

ES ElasticSearch安裝、可視化工具kibana安裝

1、安裝ES docker run -d --name es9200 -e "discovery.typesingle-node" -p 9200:9200 elasticsearch:7.12.1訪問測試: http://域名:9200/ 2、安裝kibana對es進行可視化操作 執行命令 docker run -d --name kibana5601 -p 5601:5601 kibana:7.1.12.修…

如何實現在公網下使用navicat圖形化工具遠程連接本地內網的MariaDB數據庫

公網遠程連接MariaDB數據庫【cpolar內網穿透】 文章目錄 公網遠程連接MariaDB數據庫【cpolar內網穿透】1. 配置MariaDB數據庫1.1 安裝MariaDB數據庫1.2 測試局域網內遠程連接 2. 內網穿透2.1 創建隧道映射2.2 測試隨機地址公網遠程訪問3. 配置固定TCP端口地址3.1 保留一個固定的…

Redis深入理解-Socket連接建立流程以及文件事件處理機制

Redis Server 運行原理圖 Redis 服務器中 Socket 網絡建立以及文件事件模型 一個 redis 單機,可以抗幾百上千的并發,這里的并發指的就是同時可以有幾百個 client 對這個 redis server 發起請求,都需要去建立網絡連接,同時間可能會…

利用 docker 實現JMeter分布式壓測

為什么需要分布式? 在工作中經常需要對一些關鍵接口做高QPS的壓測,JMeter是由Java 語言開發,沒創建一個線程(虛擬用戶),JVM默認會為每個線程分配1M的堆棧內存空間。受限于單臺試壓機的配置很難實現太高的并…

YAML 深入解析:從語法到最佳實踐

什么是YAML YAML(YAML Ain’t Markup Language)是一種人類可讀的數據序列化語言。它的設計目標是使數據在不同編程語言之間交換和共享變得簡單。YAML采用了一種簡潔、直觀的語法,以易于閱讀和編寫的方式表示數據結構。 YAML廣泛應用于配置文…

【OpenCV實現圖像:制作酷炫的動畫效果】

文章目錄 概要生成背景圖添加點動畫添加文本顯示小結 概要 首先,通過導入必要的庫,包括NumPy用于數學運算和Matplotlib庫用于數據可視化。隨后,創建圖形和軸,初始化點的位置,以及編寫初始化函數和更新函數。 初始化函…

C語言歸并排序

以夢為馬,不負韶華 文章目錄 引入:實現原理問題引出:遞歸實現:迭代實現穩定性分析:總結: 引入: 如何將兩個有序數組(假設為升序)合并為一個有序數組? 雙指針…

yolov5/v7修改標簽和檢測框顯示【最全】

《記錄自己在使用yolov5遇到的一些問題》同時也供大家參考,如果對你們有幫助,希望大家可以給個點贊、收藏鼓勵下,非常感謝! 以自帶的一張圖片作為示例,yolov5(6.1版本)的初始檢測框應該是如下圖所示 修改線條粗細、隱藏標簽、隱…

EI論文故障識別程序:DBN深度置信/信念網絡的故障識別Matlab程序,數據由Excel導入,直接運行!

?適用平臺:Matlab2021b版及以上 本程序參考中文EI期刊《基于變分模態分解和改進灰狼算法優化深度置信網絡的自動轉換開關故障識別》中的深度置信網絡(Deep Belief Network,DBN)部分進行故障識別,程序注釋清晰&#x…

Python之學生信息管理系統

目錄 一、基礎界面實現 1、主函數 2、保持循環,獲取用戶需求 二、函數實現模塊功能 1、添加學生信息 2、刪除學生信息 3、修改學生信息 4、查找全部學生信息 5、退出系統 三、整合代碼 1、 完整代碼 2、完整實現過程 實現 打印功能菜單、添加學生信息、刪…

想自學軟件測試?一般人我還是勸你算了吧。。。

📢專注于分享軟件測試干貨內容,歡迎點贊 👍 收藏 ?留言 📝 如有錯誤敬請指正!📢交流討論:歡迎加入我們一起學習!📢資源分享:耗時200小時精選的「軟件測試」資…

<keep-alive>作用及用法

<keep-alive>是Vue.js的內置組件。它用于緩存具有相同組件樹的組件。當組件使用<keep-alive>包裹時&#xff0c;組件不會被銷毀&#xff0c;而是會緩存到內存中&#xff0c;等到下次再次渲染時&#xff0c;直接使用緩存中的組件實例。 <keep-alive>有以下幾…

【Linux】共享內存

文章目錄 一、共享內存的原理詳談共享內存的實現過程二、共享內存的接口函數1.shmget2. shmatshmdtshmctl 進程間使用共享內存通信三、共享內存的特性 關于代碼 一、共享內存的原理 共享內存是由操作系統維護和管理的一塊內存。 共享內存的本質是內核級的緩沖區。 一個進程向…

C語言精華題目錦集1

第一題 test.c文件中包括如下語句&#xff0c;文件中定義的四個變量中&#xff0c;是指針類型的是&#xff08;&#xff09;【多選】 #define INT_PTR int* typedef int* intptr; INT_PRT a,b; int_ptr c,d;A:a ?B:b ?C:c ?D:d #define是宏定義&#xff0c;此時在程序中IN…

SQLite3 數據庫學習(六):Qt 嵌入式 Web 服務器詳解

參考引用 SQLite 權威指南&#xff08;第二版&#xff09;SQLite3 入門 1. Apache 搭建 cgi 環境 1.1 什么是 Apache Apache 是世界使用排名第一的 Web 服務器軟件 它可以運行在幾乎所有廣泛使用的計算機平臺上&#xff0c;由于其跨平臺和安全性被廣泛使用 1.2 具體搭建流程…