同一进程中同一端口如何区分不同的Socket
?為了區分不同應用進程間的網絡通信和連接,主要有3個參數:通信的目的IP地址、使用的傳輸層協議(TCP 或 UDP)和使用的端口號。
??? Socket的原意是“插座”。通過將這3個參數結合起來,與一個“插座”Socket綁定,應用層就可以和傳輸層通過套接字接口,區分來自不同應用程序進程或網絡連接的通信,實現數據傳輸的并發服務。
??? accept()產生的Socket端口號是多少?
??? 要寫網絡程序就必須用Socket,這是程序員都知道的。而且,面試的時候,我們也會問對方會不會Socket編程?一般來說,很多人都會說,Socket編程基本就是listen, accept, 以及send, write等幾個基本的操作。是的,就跟常見的文件操作一樣,只要寫過就一定知道。
??? 對于網絡編程,我們也言必稱TCP/IP,似乎其他網絡協議已經不存在了。對于TCP/IP,我們還知道TCP和UDP,前者可以保證數據的正確和可靠性,后者則允許數據丟失。最后,我們還知道,在建立連接前,必須知道對方的IP地址和端口號。除此,普通的程序員就不會知道太多了,很多時候這些知識已經夠用了。最多,寫服務程序的時候,會使用多線程來處理并發訪問。
我們還知道如下幾個事實:
1.?一個指定的端口號不能被多個應用程序共用。比如,如果IIS占用了80端口,那么Apache就不能也用80端口了;
2.?很多防火墻只允許特定目標端口的數據包通過。
3. 服務程序在listen某個端口并accept某個連接請求后,會生成一個新的socket來對請求進行處理。
??? 于是,一個困惑了我很久的問題就產生了,如果一個socket創建后并與80端口綁定后,是否就意味著該socket占用了80端口呢?
??? 如果是這樣的,那么當其accept一個請求后,生成的新的socket到底使用的是什么端口呢(我一直以為系統會默認給其分配一個空閑的端口號)?
??? 如果是一個空閑的端口,那么一定不是80端口了,于是以后的TCP數據包的目標端口就不是80了——防火墻一定會阻止其通過的!
??? 實際上,我們可以看到,防火墻并沒有阻止這樣的連接,而且這是最常見的連接請求和處理方式。我不理解的就是,為什么防火墻沒有阻止這樣的連接?它是如何判斷那條連接是因為connect80端口而生成的?是不是TCP數據包里有什么特別的標志?或者防火墻記住了什么東西?
??????后來,我又仔細研讀了TCP/IP的協議棧原理,對很多概念有了更深刻的認識。比如,TCP和UDP同屬傳輸層,共同架設在IP層(網絡層)之上。而IP層主要負責的是在節點之間(End to End)的數據包傳送,這里的節點是一臺網絡設備,比如計算機。因為IP層只負責把數據送到節點上,而不能區分上面的不同應用,所以TCP和UDP協議在其基礎上加入了端口的信息,端口于是標識的是一個節點上的一個應用。除了增加端口信息,UDP協議基本就沒有對IP層的數據進行任何處理了。而TCP協議還加入了更復雜的傳輸控制,比如滑動的數據發送窗口(Slice Window),以及接收確認和重發機制,以達到數據的可靠傳送。不管應用層看到的是怎樣一個穩定的TCP數據流,下面傳送的都是一個個的IP數據包,需要由TCP協議來進行數據重組。
?????? 所以,我有理由懷疑,防火墻并沒有足夠的信息判斷TCP數據包的更多信息,除了IP地址和端口號。而且,我們也看到,所謂的端口,是為了區分不同的應用的,以在不同的IP包來到的時候能夠正確轉發。
??????TCP/IP只是一個協議棧,就像操作系統的運行機制一樣,必須要具體實現,同時還要提供對外的操作接口。就像操作系統會提供標準的編程接口,比如Win32編程接口一樣,TCP/IP也必須對外提供編程接口,這就是Socket編程接口——原來是這么回事啊!
??????在Socket編程接口里,設計者提出了一個很重要的概念,那就是socket。這個socket跟文件句柄很相似,實際上,在BSD系統里就是跟文件句柄一樣存放在一樣的進程句柄里。這個socket其實是一個序號,表示其在句柄表中的位置。這一點,我們已經見過很多了,比如文件句柄,窗口句柄等。這些句柄,其實是代表了系統中的某些特定的對象,用于在各種函數中作為參數傳入,以對特定對象進行操作——這其實是C語言的問題,在C++語言里,這個句柄其實就是this指針,實際就是對象指針啦。
現在我們知道,socket跟TCP/IP并沒有必然的聯系。Socket編程接口在設計的時候,就希望也能適應其他的網絡協議。所以,socket的出現只是可以更方便的使用TCP/IP協議棧而已,其對TCP/IP進行了抽象,形成了幾個最基本的函數接口。比如create, listen, accept, connect, read和write等。
現在我們明白,如果一個程序創建了一個socket,并讓其監聽80端口,其實是向TCP/IP協議棧聲明了其對80端口的占有。以后,所有目標是80端口的TCP數據包都會轉發給該程序(這里的程序,因為使用的是Socket編程接口,所以首先由Socekt層來處理)。所謂的accept函數,其實抽象的是TCP的連接建立過程。accept函數返回的新socket其實指代的是本次創建的連接,而一個連接是包括兩部分信息的,一個是源IP和源端口,另一個宿IP和宿端口。這樣的話,這些socket宿端口就可以都是80!而同時,防火墻的對IP包的處理規則也是清晰明了,不存在前面設想的種種復雜的情形。
??????明白socket只是對TCP/IP協議棧操作的抽象,而不是簡單的映射關系,這很重要!
昨天和朋友聊了下網絡編程,關于Socket,這里寫一下我個人的一些理解:)
程序里可以創建Socket,分為普通Socket和原始Socket兩種類型。
一:普通Socket是對TCP/IP協議棧中傳輸層的操作的編程接口(一種API)。
有面向連接的流式套接字(SOCK_STREAM),屬于針對TCP方式的應用;
有無連接數據包式套接字(SOCK_DGRAM),屬于針對UDP方式的應用。
??????對于普通Socket,我曾經有個模糊的問題,在多線程情況下,服務器端監聽(listen)某個端口(假設8080)后,每accept一個客戶端的連接就會產生一個新的Socket。那么這些新產生的Socket的端口是什么?程序里肯定沒有指定,那就應該有兩種可能,1:產生隨機端口。2:還是8080端口。第一種假設想了就覺得不可能,防火墻非常有可能會阻止這些隨機端口的包。那么就是第二種假設了,服務端端口還是8080。但這推翻了我原有的認識,就是“一個端口被程序占有,其他程序就不能用該端口了”。我覺得其實最有可能的是范圍不同:就是在程序與程序間不能用同一端口,但是在程序內部不同的Socket還是可以用同一端口的。所以,為了能夠使“客戶端發給服務端的同一端口(8080)不同線程(即不同的Socket連接)的包能夠被區分開并進行組合”,必須得有一個區分包是來自不同連接的顯著特征,那就是傳輸層包頭里的源端口了,即一個Socket連接里客戶端那方的端口??偨Y一下,對于這種情況,就是傳輸層包頭里源端口(客戶端)會隨著產生的Socket不同,而宿端口相同(服務器端)。
二:原始Socket,建立在網絡層上,所以我們可以在傳輸層上構建自己的協議。
如果是自己做個Sniffer(網絡嗅探器),那么監聽到的包是來自同一網段的普通Socket包(TCP方式或UDP方式),所以在程序里我們要自己寫數據結構(IP頭和TCP或UDP頭),并綁定數據。
如果是客戶端和服務端都是由自己用原始Socket寫的,那么可以自己控制協議,像一些網絡應用(MSN, skype等),可以在網絡層往上重寫協議。
總結
以上是生活随笔為你收集整理的同一进程中同一端口如何区分不同的Socket的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 计算机操作系统原理精品课程,计算机操作系
- 下一篇: 单链表反转 c实现