初識網路協議 基於TCP和UDP的Socket程式設計

2022-04-29 08:33:07 字數 4594 閱讀 7837

在建立socket的時候,socket函式需要指定是ipv4還是ipv6,分別對應af_inetaf_inet6,這是網路層的。其次,還需指定是tcp還是udp,這是傳輸層的。tcp是基於資料流的,所以設定為sock_stream,udp是基於資料報的,所以設定為sock_dgram

基於tcp協議的socket程式函式呼叫過程:

服務端要先監聽乙個埠,一般是先呼叫bind函式,給這個socket賦予乙個ip位址和埠。當伺服器有了ip和埠,就可以呼叫listen函式進行監聽。此時伺服器進入監聽狀態,客戶端可以發起連線。

在linux核心中,為每個socket維護兩個佇列。乙個是已經建立了連線的佇列,這時候連線三次握手已經完畢,處於established狀態;乙個是還沒有完全建立連線的佇列,這個時候三次握手還沒完成,處於syn_rcvd的狀態。

接下來,服務端呼叫accept函式,拿出乙個已經完成的連線進行處理。如果還沒有完成,就要等待。

在服務端等待的時候,客戶端可以通過connect函式發起連線。先在引數中指明要連線的ip位址和埠號,然後開始發起三次握手。核心會給客戶端分配乙個臨時的埠。一旦握手成功,服務端的accept就會返回另乙個socket。監聽的socket和真正用來傳輸資料的socket是兩個,乙個叫作監聽socket,乙個叫作已連線socket

連線建立成功之後,雙方開始通過readwrite函式來讀寫資料。說tcp的socket就是乙個檔案流,是非常準確的。因為socket在linux中就是以檔案的形式存在的。除此之外,還存在檔案描述符,寫入和讀出,就是通過檔案描述符。

在核心中,socket是乙個檔案,那對應就有檔案描述符。每乙個程序都有乙個資料結構task_struct,裡面指向乙個檔案描述符陣列,來列出這個程序開啟的所有檔案的檔案描述符。檔案描述符是乙個整數,是這個陣列的下標。

這個陣列中的內容是乙個指標,指向核心中所有開啟的檔案的列表。既然是乙個檔案,就會有乙個inode,只不過socket對應的inode不像真正的檔案系統一樣,儲存在硬碟上,而是在記憶體中。在這個inode中,指向了socket在核心中的socket結構。在這個結構裡面,主要的是兩個佇列,乙個是傳送佇列,乙個是接收佇列。在這兩個佇列裡面儲存的是乙個快取sk_buff。這個快取裡面能夠看到完整的包的結構。

基於udp協議的scoket程式函式呼叫過程:

對於udp來說,不需要三次握手,也就不需要呼叫listenconnect。但是,udp的互動仍然需要ip和埠號,因此也需要bind。udp是沒有維護連線狀態的,因此不需要每對建立一組socket,而是只要有乙個socket,就能夠和多個客戶端通訊。也正是因為沒有連線狀態,每次通訊的時候,都呼叫sendtorecvfrom,都可以傳入ip位址和埠。

系統會用乙個四元組來標識乙個tcp連線。

伺服器的ip和埠是本能隨意改變的,所以能改變的只有客戶端的ip和對端埠。因此理論上最大tcp連線數 = 客戶端ip數 * 客戶端埠數。對ipv4,客戶端的ip數最多為2^32,埠數為2^16,也就是服務端單機理論上最大的tcp連線數是2^48。

顯然,這只是理論上來說,事實上遠遠達不到。首先主要是檔案描述符限制,socket都是檔案,所以首先要通過ulimit配置檔案描述符的數目;另乙個限制是記憶體,按上面的資料結構,每個tcp連線都要占用一定的記憶體,作業系統是有限的。

所以,我們要在有限的資源下接更多的專案,就必須降低每個專案消耗的資源數量。

方式一:多程序

一旦建立乙個連線,就會有乙個已連線socket,這個時候建立乙個子程序,然後將基於已連線socket的互動交給這個新的子程序來做。

在linux中,建立子程序使用fork函式。這是在父程序的基礎上完全拷貝乙個子程序。在linux核心中,會複製檔案描述符的列表,也會複製記憶體空間,還會複製一條記錄當前執行到了哪一行程式的程序。顯然,複製的時候在呼叫fork,複製完畢之後,父程序和子程序都會記錄當前剛剛執行完fork。這兩個程序剛複製完的時候,幾乎一模一樣,只是根據fork的返回值來區分到底是父程序還是子程序。如果返回0,則是子程序;如果返回其他整數,就是父程序。

方式二:多執行緒

相比於程序,執行緒要輕量級的多。在linux下,通過pthread_create建立乙個程序,也是呼叫do_fork。不同的是,雖然新的執行緒在task列表會新建立一項,但是很多資源,例如檔案描述符、程序空間、還是共享的,只不過多了乙個引用而已。

新的程序也可以通過已連線socket處理請求,從而達到併發處理的目的。

上面基於程序或者執行緒模型的,其實還是有問題的。新到來乙個tcp連線,就需要分配乙個程序或執行緒,一台機器無法建立很多程序和執行緒。有個c10k,它的意思是一台機器要維護10萬個連線,就要建立1萬個程序或者執行緒,那麼作業系統是無法承受的。

方式三:io多路復用,乙個執行緒維護多個socket

由於socket是檔案描述符,因而某個執行緒盯的所有的socket,都放在乙個檔案描述符集合fd_set中,然後呼叫select函式來監聽檔案描述符集合是否有變化。一旦有變化,就會依次檢視每個檔案描述符。那些發生變化的檔案描述符在fd_set對應的位都設為1,表示socket可讀或者可寫,從而可以進行讀寫操作,然後再呼叫select,接著盯著下一輪的變化。

方式四:io多路復用,從selectepoll

上面的select函式還是有問題的,因為每次socket所在的檔案描述符集合中有socket發生變化的時候,都需要通過輪詢的方式,這會大大影響連線數。因此使用select,需要設定能夠同時盯著的檔案描述符的數量fd_setsize

如果改成事件通知的方式,情況會好很多,就不需要輪詢檔案描述符了。而是當集合發生變化時,主動通知執行緒,然後執行緒再做出相應的操作。

能完成這件事情的函式叫epoll,它在核心中的實現不是通過輪詢的方式,而是通過註冊callback函式的方式,當某個檔案描述符發生變化的時候,就會主動通知。

如圖所示,假如程序開啟了socket m, n, x等多個檔案描述符,現在需要通過epoll來監聽是否這些socket都有事件發生。其中epoll_create建立乙個epoll物件,也是乙個檔案,也對應乙個檔案描述符,同樣也對應著開啟檔案列表中的一項。在這項裡面有乙個紅黑樹,在紅黑樹裡,要儲存這個epoll要監聽的所有socket。

epoll_ctl新增乙個socket的時候,其實是加入這個紅黑樹,同時紅黑樹裡面的節點指向乙個結構,將這個結構掛在被監聽的socket的事件列表中。當乙個socket來了乙個事件的時候,可以從這個列表中得到epoll物件,並呼叫callback通知它。

這種通知方式使得監聽的socket資料增加的時候,效率不會大幅度降低,能夠同時監聽的socket的數目也非常多了。上限就為系統定義的、程序開啟的最大檔案描述符個數。因此,epoll被稱為解決c10k問題的利器。

基於Socket的UDP和TCP協議

一 概述 tcp 傳輸控制協議 和udp 使用者資料報協議 是網路體系結構tcp ip模型中傳輸層一層中的兩個不同的通訊協議。tcp 傳輸控制協議,一種面向連線的協議,給使用者程序提供可靠的全雙工的位元組流,tcp套介面是位元組流套介面 stream socket 的一種。udp 使用者資料報協議。...

分析udp資料報 網路協議之TCP和UDP

首先強調一點,tcp ip協議是乙個協議簇。裡面包括很多協議的,udp只是其中的乙個,之所以命名為tcp ip協議,因為tcp ip協議是兩個很重要的協議,就用他兩命名了。兩個協議的區別實際使用時,只需要記住 tcp正常連線傳送資料時一般不會產生丟包 排除上下層其他因素 而udp產生丟包是很常見的事...

基於TCP和UDP的Socket網路應用程式

基於tcp和udp的socket網路應用程式 計算機網路課設 一 需求分析 利用socket編寫乙個簡單的網路應用程式,獲取伺服器當前的時間和日期。說明與要求 1 對客戶與伺服器之間使用的協議進行設計。2 分別採用流式套接字和資料報套接字進行實現。二 程式設計 2.1 基於tcp協議的流程 編寫用t...