要寫網絡程序就必須用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了--防火牆必定會組織其經過的!實際上,咱們能夠看到,防火牆並無阻止這樣的鏈接,並且這是最多見的鏈接請求和處理方式。個人不解就是,爲何防火牆沒有阻止這樣的鏈接?它是如何斷定那條鏈接是由於connet80端口而生成的?是否是TCP數據包裏有什麼特別的標誌?或者防火牆記住了什麼東西?
後來,我又仔細研讀了TCP/IP的協議棧的原理,對不少概念有了更深入的認識。好比,在TCP和UDP同屬於傳輸層,共同架設在IP層(網絡層)之上。而IP層主要負責的是在節點之間(End to End)的數據包傳送,這裏的節點是一臺網絡設備,好比計算機。由於IP層只負責把數據送到節點,而不能區分上面的不一樣應用,因此TCP和UDP協議在其基礎上加入了端口的信息,端口因而標識的是一個節點上的一個應用。除了增長端口信息,UPD協議基本就沒有對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編程接口,因此首先由Socket層來處理)。所謂accept函數,其實抽象的是TCP的鏈接創建過程。accept函數返回的新socket其實指代的是本次建立的鏈接,而一個鏈接是包括兩部分信息的,一個是源IP和源端口,另外一個是宿IP和宿端口。因此,accept能夠產生多個不一樣的socket,而這些socket裏包含的宿IP和宿端口是不變的,變化的只是源IP和源端口。這樣的話,這些socket宿端口就能夠都是80,而Socket層仍是能根據源/宿對來準確地分辨出IP包和socket的歸屬關係,從而完成對TCP/IP協議的操做封裝!而同時,放火牆的對IP包的處理規則也是清晰明瞭,不存在前面設想的種種複雜的情形。
明白socket只是對TCP/IP協議棧操做的抽象,而不是簡單的映射關係,這很重要!
一、TCP鏈接
手機可以使用聯網功能是由於手機底層實現了TCP/IP協議,可使手機終端經過無線網絡創建TCP鏈接。TCP協議能夠對上層網絡提供接口,使上層網絡數據的傳輸創建在「無差異」的網絡之上。
創建起一個TCP鏈接須要通過「三次握手」:
第一次握手:客戶端發送syn包(syn=j)到服務器,並進入SYN_SEND狀態,等待服務器確認;
第二次握手:服務器收到syn包,必須確認客戶的SYN(ack=j+1),同時本身也發送一個SYN包(syn=k),即SYN+ACK包,此時服務器進入SYN_RECV狀態;
第三次握手:客戶端收到服務器的SYN+ACK包,向服務器發送確認包ACK(ack=k+1),此包發送完畢,客戶端和服務器進入ESTABLISHED狀態,完成三次握手。
握手過程當中傳送的包裏不包含數據,三次握手完畢後,客戶端與服務器才正式開始傳送數據。理想狀態下,TCP鏈接一旦創建,在通訊雙方中的任何一方主動關閉鏈接以前,TCP 鏈接都將被一直保持下去。斷開鏈接時服務器和客戶端都可以主動發起斷開TCP鏈接的請求,斷開過程須要通過「四次握手」(過程就不細寫了,就是服務器和客戶端交互,最終肯定斷開)
二、HTTP鏈接
HTTP協議即超文本傳送協議(Hypertext Transfer Protocol ),是Web聯網的基礎,也是手機聯網經常使用的協議之一,HTTP協議是創建在TCP協議之上的一種應用。
HTTP鏈接最顯著的特色是客戶端發送的每次請求都須要服務器回送響應,在請求結束後,會主動釋放鏈接。從創建鏈接到關閉鏈接的過程稱爲「一次鏈接」。
1)在HTTP 1.0中,客戶端的每次請求都要求創建一次單獨的鏈接,在處理完本次請求後,就自動釋放鏈接。
2)在HTTP 1.1中則能夠在一次鏈接中處理多個請求,而且多個請求能夠重疊進行,不須要等待一個請求結束後再發送下一個請求。
因爲HTTP在每次請求結束後都會主動釋放鏈接,所以HTTP鏈接是一種「短鏈接」,要保持客戶端程序的在線狀態,須要不斷地向服務器發起鏈接請求。一般的作法是即時不須要得到任何數據,客戶端也保持每隔一段固定的時間向服務器發送一次「保持鏈接」的請求,服務器在收到該請求後對客戶端進行回覆,代表知道客戶端「在線」。若服務器長時間沒法收到客戶端的請求,則認爲客戶端「下線」,若客戶端長時間沒法收到服務器的回覆,則認爲網絡已經斷開。
三、SOCKET原理
3.1套接字(socket)概念
套接字(socket)是通訊的基石,是支持TCP/IP協議的網絡通訊的基本操做單元。它是網絡通訊過程當中端點的抽象表示,包含進行網絡通訊必須的五種信息:鏈接使用的協議,本地主機的IP地址,本地進程的協議端口,遠地主機的IP地址,遠地進程的協議端口。
應用層經過傳輸層進行數據通訊時,TCP會遇到同時爲多個應用程序進程提供併發服務的問題。多個TCP鏈接或多個應用程序進程可能須要經過同一個 TCP協議端口傳輸數據。爲了區別不一樣的應用程序進程和鏈接,許多計算機操做系統爲應用程序與TCP/IP協議交互提供了套接字(Socket)接口。應用層能夠和傳輸層經過Socket接口,區分來自不一樣應用程序進程或網絡鏈接的通訊,實現數據傳輸的併發服務程序員