socket的accept函式解析

2021-07-03 20:54:44 字數 2911 閱讀 8618



今天與同學爭執乙個話題:

由於socket的accept函式在有客戶端連線的時候產生了新的socket用於服務該客戶端,那麼,這個新的socket到底有沒有占用乙個新的埠?

討論完後,才發現,自己雖然熟悉socket的程式設計套路,但是卻並不是那麼清楚socket的原理,今天就趁這個機會,把有關socket程式設計的幾個疑問給搞清楚吧。

先給出乙個典型的tcp/ip通訊示意圖。

問題一:socket結構體物件究竟是怎樣定義的?

我們知道,在使用socket程式設計之前,需要呼叫socket函式建立乙個socket物件,該函式返回該socket物件的描述符。

函式原型:

intsocket(

intdomain, 

inttype, 

intprotocol); 

那麼,這個socket物件究竟是怎麼定義的呢?它記錄了哪些資訊呢?只記錄了本機ip及埠、還是目的ip及埠、或者都記錄了?

關於這個問題,大家可以在核心原始碼裡面找,也可以參考這篇文章《struct socket 結構詳解》,我們可以看到 socket  結構體的定義如下: 

struct

socket ; 

其中,struct sock 包含有乙個 sock_common 結構體,而sock_common結構體又包含有struct inet_sock 結構體,而struct inet_sock 結構體的部分定義如下:

struct

inet_sock  

由此,我們清楚了,socket結構體不僅僅記錄了本地的ip和埠號,還記錄了目的ip和埠。

問題二:connect函式究竟做了些什麼操作?

在tcp客戶端,首先呼叫乙個socket()函式,得到乙個socket描述符socketfd,然後通過connect函式對伺服器進行連線,連線成功後,就可以利用這個socketfd描述符使用send/recv函式收發資料了。

關於connect函式和send函式的原型如下:

int

connect( 

intsockfd, 

const

struct

sockaddr* server_addr, socklen_t addrlen)  

intsend( 

intsockfd, 

const

void

*msg,

intlen,

intflags); 

那麼,現在的困惑是,為什麼send函式僅僅傳入sockfd就可以知道伺服器的ip和埠號?

其實,由「問題一」中的答案我們已經很清楚了,sockfd 描述符所描述的socket物件不僅包含了本地ip和埠,同時也包含了伺服器的ip和埠,這樣,才能使得send函式只需要傳入sockfd 即可知道該把資料發向什麼地方。而**中,目的ip和埠只是在connect函式中出現過,因此,肯定是connect函式在成功建立連線後,將目的ip和埠寫入了sockfd 描述符所描述的socket物件中。

問題三: accept函式產生的socket有沒有占用新的埠?

首先,回顧一下accept函式,原型如下:

intaccept(

intsockfd, 

struct

sockaddr* addr, socklen_t* len)  

accept函式主要用於伺服器端,一般位於listen函式之後,缺省會阻塞程序,直到有乙個客戶請求連線,建立好連線後,它返回的乙個新的套接字 socketfd_new ,此後,伺服器端即可使用這個新的套接字socketfd_new與該客戶端進行通訊,而sockfd 則繼續用於監聽其他客戶端的連線請求。

至此,我的困惑產生了,這個新的套接字 socketfd_new 與監聽套接字sockfd 是什麼關係?它所代表的socket物件包含了哪些資訊?socketfd_new 是否占用了新的埠與客戶端通訊?

先簡單分析一番,由於**的伺服器也是一種tcp伺服器,使用的是80埠,並不會因客戶端的連線而產生新的埠給客戶端服務,該客戶端依然是向伺服器端的80埠傳送資料,其他客戶端依然向80埠申請連線。因此,可以判斷,socketfd_new 並沒有占用新的埠與客戶端通訊,依然使用的是與監聽套接字socketfd_new一樣的埠號。

那這麼說,難道乙個埠可以被兩個socket物件繫結?當客戶端傳送資料過來的時候,究竟是與哪乙個socket物件通訊呢?

我是這麼理解的(歡迎拍磚)。

首先,乙個埠肯定只能繫結乙個socket。我認為,伺服器端的埠在bind的時候已經繫結到了監聽套接字socetfd所描述的物件上,accept函式新建立的socket物件其實並沒有進行埠的占有,而是複製了socetfd的本地ip和埠號,並且記錄了連線過來的客戶端的ip和埠號。

那麼,當客戶端傳送資料過來的時候,究竟是與哪乙個socket物件通訊呢?

客戶端傳送過來的資料可以分為2種,一種是連線請求,一種是已經建立好連線後的資料傳輸。

由於tcp/ip協議棧是維護著乙個接收和傳送緩衝區的。在接收到來自客戶端的資料報後,伺服器端的tcp/ip協議棧應該會做如下處理:如果收到的是請求連線的資料報,則傳給監聽著連線請求埠的socetfd套接字,進行accept處理;如果是已經建立過連線後的客戶端資料報,則將資料放入接收緩衝區。這樣,當伺服器端需要讀取指定客戶端的資料時,則可以利用socketfd_new 套接字通過recv或者read函式到緩衝區裡面去取指定的資料(因為socketfd_new代表的socket物件記錄了客戶端ip和埠,因此可以鑑別)。

本文出自 「對影成三人」 部落格,

779866

socket通訊 accept函式

先給出乙個典型的tcp ip通訊示意圖。問題一 socket結構體物件究竟是怎樣定義的?我們知道,在使用socket程式設計之前,需要呼叫socket函式建立乙個socket物件,該函式返回該socket物件的描述符。函式原型 intsocket intdomain,inttype,intproto...

由socket的accept說開去

討論完後,才發現,自己雖然熟悉socket的程式設計套路,但是卻並不是那麼清楚socket的原理,今天就趁這個機會,把有關socket程式設計的幾個疑問給搞清楚吧。先給出乙個典型的tcp ip通訊示意圖。問題一 socket結構體物件究竟是怎樣定義的?我們知道,在使用socket程式設計之前,需要呼...

關於accept得到的socket本地埠

伺服器端accept得到的socket本地埠就是listen埠 客戶端如果不指定會隨機乙個本地埠 因此伺服器端沒有連線數量的限制 硬體無限 客戶端最多不能超過65533個連線 今天與同學爭執乙個話題 由於socket的accept函式在有客戶端連線的時候產生了新的socket用於服務該客戶端,那麼,...