爲了區分不一樣應用進程間的網絡通訊和鏈接,主要有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
因而,一個困惑了我好久的問題就產生了,若是一個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鏈接裏客戶端那方的端口。總結一下,對於這種狀況,就是傳輸層包頭裏源端口(客戶端)會隨着產生的Socket不一樣,而宿端口相同(服務器端)。
二:原始Socket,創建在網絡層上,因此咱們能夠在傳輸層上構建本身的協議。
若是是本身作個Sniffer(網絡嗅探器),那麼監聽到的包是來自同一網段的普通Socket包(TCP方式或UDP方式),因此在程序裏咱們要本身寫數據結構(IP頭和TCP或UDP頭),並綁定數據。
若是是客戶端和服務端都是由本身用原始Socket寫的,那麼能夠本身控制協議,像一些網絡應用(MSN, skype等),能夠在網絡層往上重寫協議。