深刻理解socket程式設計
我們還知道如下幾個事實:
1。一個指定的埠號不能被多個程式共用。比如,如果IIS佔用了80埠,那麼Apache就不能也用80埠了。
2。很多防火牆只允許特定目標埠的資料包通過。
3。服務程式在listen某個埠並accept某個連線請求後,會生成一個新的socket來對該請求進行處理。
於是,一個困惑了我很久的問題就產生了。如果一個socket建立後並與80埠繫結後,是否就意味著該socket佔用了80埠呢?如果是這樣的,那麼當其accept一個請求後,生成的新的socket到底使用的是什麼埠呢(我一直以為系統會預設給其分配一個空閒的埠號)?如果是一個空閒的埠,那一定不是80埠了,於是以後的TCP資料包的目標埠就不是80了--防火牆一定不會組織其通過的!實際上,我們可以看到,防火牆並沒有阻止這樣的連線,而且這是最常見的連線請求和處理方式。我的不解就是
1.為什麼防火牆沒有阻止這樣的連線?
2.它是如何判定那條連線是因為connet80埠而生成的?是不是TCP資料包裡有什麼特別的標誌?或者防火牆記住了什麼東西?
研讀了TCP/IP的協議棧的原理後,對很多概念有了更深刻的認識。比如,在TCP和UDP同屬於傳輸層,共同架設在IP層(網路層)之上。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其實是一個序號,表示其在控制代碼表中的位置。這一點,我們已經見過很多了,比如檔案控制代碼,
現在我們明白,如果一個程式建立了一個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協議棧操作的抽象,而不是簡單的對映關係,這很重要!
二.socket通訊過程:
socket是"開啟—讀/寫—關閉"模式的實現,以使用TCP協議通訊的socket為例,其互動流程大概是這樣子的
過程詳解:伺服器程式將一個套接字繫結到一個特定的埠(一個應用),並通過此套接字等待和監聽客戶的連線請求。
客戶程式根據伺服器程式所在的主機名和埠號發出連線請求。
如果一切正常,伺服器接受連線請求。並獲得一個新的繫結到不同埠地址的套接字。(不可能有兩個程式同時佔用一個埠)。
客戶和伺服器通過讀寫套接字進行通訊。
過程總結:使用ServerSocket和Socket實現伺服器端和客戶端的Socket通訊
伺服器socket監聽埠號請求,隨時準備接收客戶端發來的連線,這時候伺服器的socket並沒有被開啟
客戶端建立socket
客戶端開啟socket,根據伺服器ip地址和埠號試圖連線伺服器socket
伺服器socket接收到客戶端socket請求,被動開啟,開始接收客戶端請求,直到客戶端返回連線資訊。這時候socket進入阻塞狀態,所謂阻塞即accept()方法一直到客戶端返回連線資訊後才返回,開始接收下一個客戶端諒解請求
客戶端連線成功,向伺服器傳送連線狀態資訊
伺服器accept方法返回,連線成功 (連線建立好後,伺服器端和客戶端的輸入流和輸出流就互為彼此,即一端的輸出流是另一端的輸入流)
客戶端向socket寫入資訊
伺服器讀取資訊
客戶端關閉
伺服器端關閉
三:socket採用的模型:
最重要的是,Socket是面向客戶/伺服器模型而設計的,針對客戶和伺服器程式提供不同的Socket系統呼叫。客戶隨機申請一個Socket(相當於一個想打電話的人可以在任何一臺入網電話上撥號呼叫),系統為之分配一個Socket號;伺服器擁有全域性公認的Socket,任何客戶都可以向它發出連線請求和資訊請求(相當於一個被呼叫的電話擁有一個呼叫方知道的電話號碼)。解決了兩個完全隨機的使用者程序之間建立通訊總結:使用ServerSocket和Socket實現伺服器端和客戶端的Socket通訊
(1)建立Socket連線
(2)獲得輸入/輸出流
(3)讀/寫資料
(4)關閉輸入/輸出流
(5)關閉Socket
補充:
1、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連線的請求,斷開過程需要經過“四次握手”(過程就不細寫了,就是伺服器和客戶端互動,最終確定斷開)
2、HTTP連線
HTTP協議即超文字傳送協議(Hypertext Transfer Protocol ),是Web聯網的基礎,也是手機聯網常用的協議之一,HTTP協議是建立在TCP協議之上的一種應用。
HTTP連線最顯著的特點是客戶端傳送的每次請求都需要伺服器回送響應,在請求結束後,會主動釋放連線。從建立連線到關閉連線的過程稱為“一次連線”。
1)在HTTP 1.0中,客戶端的每次請求都要求建立一次單獨的連線,在處理完本次請求後,就自動釋放連線。
2)在HTTP 1.1中則可以在一次連線中處理多個請求,並且多個請求可以重疊進行,不需要等待一個請求結束後再發送下一個請求。
由於HTTP在每次請求結束後都會主動釋放連線,因此HTTP連線是一種“短連線”,要保持客戶端程式的線上狀態,需要不斷地向伺服器發起連線請求。通常的做法是即時不需要獲得任何資料,客戶端也保持每隔一段固定的時間向伺服器傳送一次“保持連線”的請求,伺服器在收到該請求後對客戶端進行回覆,表明知道客戶端“線上”。若伺服器長時間無法收到客戶端的請求,則認為客戶端“下線”,若客戶端長時間無法收到伺服器的回覆,則認為網路已經斷開。
3、SOCKET原理
3.1套接字(socket)概念
套接字(socket)是通訊的基石,是支援TCP/IP協議的網路通訊的基本操作單元。它是網路通訊過程中端點的抽象表示,包含進行網路通訊必須的五種資訊:連線使用的協議,本地主機的IP地址,本地程序的協議埠,遠地主機的IP地址,遠地程序的協議埠。
應用層通過傳輸層進行資料通訊時,TCP會遇到同時為多個應用程式程序提供併發服務的問題。多個TCP連線或多個應用程式程序可能需要通過同一個 TCP協議埠傳輸資料。為了區別不同的應用程式程序和連線,許多計算機作業系統為應用程式與TCP/IP協議互動提供了套接字(Socket)介面。應用層可以和傳輸層通過Socket介面,區分來自不同應用程式程序或網路連線的通訊,實現資料傳輸的併發服務(socket是應用層與傳輸層之間的一個抽象層,它把TCP/IP層複雜的操作抽象為幾個簡單的介面供應用層呼叫已實現程序在網路中通訊。)