在伺服器端,socket()返回的套接字用於監聽(listen)和接受(accept)客戶端的連線請求。這個套接字不能用於與客戶端之間傳送和接收資料。
accept()接受乙個客戶端的連線請求,並返回乙個新的套接字。所謂「新的」就是說這個套接字與socket()返回的用於監聽和接受客戶端的連線請求的套接字不是同乙個套接字。與本次接受的客戶端的通訊是通過在這個新的套接字上傳送和接收資料來完成的。
再次呼叫accept()可以接受下乙個客戶端的連線請求,並再次返回乙個新的套接字(與socket()返回的套接字、之前accept()返回的套接字都不同的新的套接字)。這個新的套接字用於與這次接受的客戶端之間的通訊。
假設一共有3個客戶端連線到伺服器端。那麼在伺服器端就一共有4個套接字:第1個是socket()返回的、用於監聽的套接字;其餘3個是分別呼叫3次accept()返回的不同的套接字。
如果已經有客戶端連線到伺服器端,不再需要監聽和接受更多的客戶端連線的時候,可以關閉由socket()返回的套接字,而不會影響與客戶端之間的通訊。
當某個客戶端斷開連線、或者是與某個客戶端的通訊完成之後,伺服器端需要關閉用於與該客戶端通訊的套接字。
舉個簡單的例子(以下**只是示範性的,用於說明不同套接字的作用,實際的函式會需要更多的引數):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/* 建立用於監聽和接受客戶端連線請求的套接字 */
server_sock = socket();
/* 繫結監聽的ip位址和埠 */
bind(server_sock);
/* 開始監聽 */
listen(server_sock);
/**
* 等待客戶端連線請求,在沒有客戶端連線請求到來之前,
* 程式會一直阻塞在這個函式裡。
*/
client_sock = accept(server_sock);
/**
* 已經接受客戶端連線請求,accept()函式建立並返回了乙個
* 新的套接字client_sock,用於與客戶端通訊。
* 如果不再需要接受其他客戶端的連線請求,可以關閉監聽
* 套接字了。
*/
close(server_sock);
/* 傳送資料到客戶端 */
send(client_sock, data);
/* 從客戶端接收資料 */
recv(client_sock, data);
/* 通訊結束,關閉與客戶端通訊的套接字 */
close(client_sock);
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了--防火牆一定會阻止其通過的!實際上,我們可以看到,防火牆並沒有阻止這樣的連線
,而且這是最常見的連線請求和處理方式。我的不解就是,為什麼防火牆沒有阻止這樣的連線?它是如何判定那條連線是因為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協議棧操作的抽象,而不是簡單的對映關係,這很重要!
python網路程式設計 TCP網路程式設計
tcp程式設計 客戶端 import socket 1 套接字 tcp socket socket.socket socket.af inet,socket.sock stream 2 建立鏈結 tcp socket.connect 172.27.35.1 8080 3 傳送資訊 tcp socke...
網路程式設計 網路基礎
1.物理層 提供建立 維護和拆除物理鏈路所需的機械 電氣 功能和規程的特性 提供有關在傳輸介質上傳輸非結構的位流及物理鏈路故障檢測指示。在這一層,資料還沒有被組織,僅作為原始的位流或電氣電壓處理,單位是位元。常用協議 eia tia rs 232 eia tia rs 449 v.35 rj 45 ...
網路程式設計 網路概述
應用層 常見的協議有http協議 ftp協議 傳輸層 常見協議有tcp udp協議。網路層 常見的協議有ip協議 icmp協議 igmp協議 鏈路層 常見的協議有arp協議 rarp協議。tcp 傳輸控制協議 是一種面向連線的 可靠的 基於位元組流的傳輸層通訊協議。http 超文字傳輸協議 是網際網...