從問題看本質:socket到底是什麼?

一、問題的引入——socket的引入是爲了解決不同計算機間進程間通信的問題

1.socket與進程的關係

1).socket與進程間的關係:socket 用來讓一個進程和其他的進程互通信息(IPC),而Socket接口是TCP/IP網絡的API接口函數。

2).進程間通信(本機內)

進程間通信(不同計算機,要聯網)



2、socket與文件的關係——如何理解socket是種特殊的I/O?

1)Socket最先應用於Unix操作系統,如果瞭解Unix系統的I/O的話,就很容易瞭解Socket了,因爲Socket數據傳輸其實就是一種特殊的I/O。 

2)可對其進行文件操作

3)有文件描述符。而文件描述符的本質是一個非負整數。只是用於區分。類似的還有進程ID。

3.服務器端口與連接個數的關係
1)服務端在8088上監聽,然後生成一個新的socket與client通訊。(注意:服務器端監聽端口是
不變的,但socket連接可以一直生成,一個線程對應一個socket.)
同一時刻,一個端口只能建立一個連接。 
在一個端口監聽,但是在監聽端口的同時,生成一個等待隊列,每一個來自客戶端的連接都會送入等待隊列中,服務器利用一定的算法進行選擇相應的連接請求處理,所以在一個端口可以監聽多個請求嘛。如果同時的連接過多,服務器相應每個連接的時間就會相應的變長。就會變慢。
2)QQ的實現方法就是在登陸的時候告訴服務器你已經登陸,發送消息的時候,首先你會發送一個包給服務器,服務器查看你需要發送的對方是否在線,如果在線就返回包告訴你對方在線,然後你就會和對方建立一個連接,然後直接發消息給對方,如果對方不在線,那麼就會通過服務器轉發你這次的消息
3)網絡IO數與你的CPU數目一致將是比較好的選擇(考慮到多線程多進程可以提高效率)。 
沒有必要每個客戶分配一個端口。絕對是一種無謂的浪費。 


4.有人知道socket監聽的一個端口.最多可以有多少個客戶連接? 
1)listen()中有個參數,應該是確定並行連接客戶數?!
2)The maximum length of the queue of pending connections. If this value is SOMAXCONN, then the underlying service provider responsible for socket s will set the backlog to a maximum "reasonable " value. There is no standard provision to find out the actual backlog value. 
3)linux2.4下,最多可以有1024個socket連接
4)同時連接請求好像是5個(是連接請求,不是連接),可保持的鏈接理論上是65535(2字節的SOCKET端口號),

3.Socket是網絡上運行的兩個程序間雙向通訊的一端,它既可以接受請求,也可以發送請求,利用它可以較爲方便的編寫網絡上數據的傳遞。

5.問:現在server與client想建立socket連接,server僅知道client的IP,端口號不知道,能建立連接嗎?怎麼建立呢?有沒有代碼看看?
答:C和S是相對而言的,發起連接的一方就是C,而監聽端口接受連接的一方就是S,C如果不知道S監聽的端口,怎麼發起連接呢,
另外,對於S而言,端口是S上各個服務的區分標誌,如果用錯誤的端口號去連接,是不能獲得正確的服務的。
client的端口是不需要指定的,Server綁定端口,然後監聽,client使用server的IP和端口建立socket連接 



6.精彩問答

問:看到的文章上說“每個網絡通信循環地進出主計算機的TCP 應用層。它被兩個所連接的號碼唯一地識別。這兩個號碼合起來叫做套接字.組成套接字的這兩個號碼就是機器的IP 地址和TCP 軟件所使用的端口號。” 
又說“通過socket()函數可以創建一個套接字,然後再把它綁定到端口號...” 
那麼套接字socket的概念究竟到哪裏爲止呢?是僅限於socket()返回的文件描述符?還是是IP和端口號的組合?如果是,那麼socket()調用之後產生的套接字描述符的作用是什麼呢? 套接字描述符,IP地址,端口號三者間的關係是怎樣的? 
謝謝各位前輩解答。
答:一個socket句柄代表兩個地址對 “本地ip:port”--“遠程ip:port”
問:那麼socket的概念到底到那裏爲止呢?比如,利用socket()可以產生一個套接字句柄,可是在bind() 或者 connect () 之前它只是一個文件描述符,和linux中其他的文件描述符一樣。 
如果說socket代表一個兩個地址對,那麼句柄的作用是不是僅僅是在bind() 或者 connect () 之後的用於區分和標記這樣的地址對呢?因爲這樣他才能和網絡的概念聯繫起來。這樣的話,socket的意義應該是說用文件描述符描述的通信雙方的IP地址和端口號地址對?(而文件描述符是區分這些地址對的標記?)
答:socket爲內核對象,由操作系統內核來維護其緩衝區,引用計數,並且可以在多個進程中使用。 
至於稱它爲“句柄”“文件描述符”都是一樣的,它只不過是內核開放給用戶進程使用的整數而已
問:謝謝樓上,是我沒描述清楚。對於“句柄”和“文件描述符”我沒有異議。 
我想我的問題是在於句柄和ip、port的關係,不知道我這樣說對否: 
1. 每一個socket 本質上都是指一個ip地址和端口對 
2. 爲了操作這樣的地址對,使用了文件描述符 
3. socket()函數只創建了一個普通的文件描述符,在進行bind()或者connect()之前並不能說創建了用於網絡通訊的套接字 
4. 只有在進行了bind()或者connect()之後socket才被創立起來
答:socket()創建了一個socket內核對象。 
accept或者connect後,纔可以對socket句柄讀寫。因爲只有在 connect或者bind,listen,accept後纔會設置好socket內核對象裏邊的ip和端口 

二、socket和端口理解

一個socket句柄代表兩個地址對 “本地ip:port”--“遠程ip:port” 
在windows下叫句柄,在linux下叫文件描述符 
socket爲內核對象,由操作系統內核來維護其緩衝區,引用計數,並且可以在多個進程中使用。 至於稱它爲“句柄”“文件描述符”都是一樣的
我假定讀者已經對於socket連接的建立過程和各種狀態轉換比較熟悉了,因爲這篇文檔的目的是澄清概念,而不是介紹概念。 
在使用socket編程時,我們都知道在網絡通信以前首先要建立連接,而連接的建立是通過對socket的一些操作來完成的。那麼,建立連接的過程大致可以分爲以下幾步: 
1) 建立socket套接字。 
2) 給套接字賦予地址,這個地址不是通常的網絡地址的概念。 
3) 建立socket連接。 

1. 建立socket套接字。 
使用socket建立套接字的時候,我們實際上是建立了一個數據結構。這個數據結構最主要
的信息是指定了連接的種類和使用的協議,此外還有一些關於連接隊列操作的結構字段
(這裏就先不涉及他們了)。 
當我們使用socket函數以後,如果成功的話會返回一個int型的描述符,它指向前面那個
被維護在內核裏的socket數據結構。我們的任何操作都是通過這個描述符而作用到那個數
據結構上的。這就像是我們在建立一個文件後得到一個文件描述符一樣,對文件的操作都
是通過文件描述符來進行的,而不是直接作用到inode數據結構上。我之所以用文件描述
符舉例,是因爲socket數據結構也是和inode數據結構密切相關,它不是獨立存在於內核
中的,而是位於一個VFS inode結構中。所以,有一些比較抽象的特性,我們可以用文件
操作來不恰當的進行類比以加深理解。 
如前所述,當建立了這個套接字以後,我們可以獲得一個象文件描述符那樣的套接字描述
符。就象我們對文件進行操作那樣,我們可以通過向套接字裏面寫數據將數據傳送到我們
指定的地方,這個地方可以是遠端的主機,也可以是本地的主機。如果你有興趣的話,還
可以用socket機制來實現IPC,不過效率比較低,試試也就行了(我沒有試過)。 

2. 給套接字賦予地址。 
依照建立套接字的目的不同,賦予套接字地址的方式有兩種:服務器端使用bind,客戶端
使用connetc。 
Bind: 
我們都知道,只要使用IP, prot就可以區分一個tcp/ip連接(當然這個連接指的是一個
連接通道,如果要區分特定的主機間的連接,還需要第三個屬性 hostname)。 
我們可以使用bind函數來爲一個使用在服務器端例程中的套接字賦予通信的地址和端口。
在這裏我們稱通信的IP地址和端口合起來構成了一個socket地址,而指定一個socket使
用特定的IP和port組合來進行通行的過程就是賦予這個socket一個地址。 
要賦予socket地址,就得使用一個數據結構來指明特定的socket地址,這個數據結構就
是struct sockaddr。對它的使用我就不說了,因爲這篇文檔的目的是澄清概念而不是說
明使用方法。Bind函數的作用就是將這個特定的標註有socket地址信息的數據結構和
socket套接字聯繫起來,即賦予這個套接字一個地址。但是在具體實現上,他們兩個是怎
麼聯繫在一起的,我還不知道。 
一個特定的socket的地址的生命期是bind成功以後到連接斷開前。你可以建立一個
socket數據結構和socket地址的數據結構,但是在沒有bind以前他們兩個是沒有關係
的,在bind以後他們兩個纔有了關係。這種關係一直維持到連接的結束,當一個連接結束
時,socket數據結構和socket地址的數據結構還都存在,但是他們兩個已經沒有關係
了。如果你要是用這個套接字在socket地址上重新進行連接時,需重新bind他們兩個。再
註明一次,我說的這個連接是一個連接通道,而不是特定的主機之間的連接。 
Bind指定的IP通常是本地IP(一般不特別指定,而使用INADDR_ANY來聲明),而最主要
的作用是指定端口。在服務器端的socket進行了bind以後就是用listen來在這個socket
地址上準備進行連接。 
connect: 
對於客戶端來說,是不會使用bind的(並不是不能用,但沒什麼意義),他們會通過
connet函數來建立socket和socket地址之間的關係。其中的socket地址是它想要連接的
服務器端的socket地址。在connect建立socket和socket地址兩者關係的同時,它也在
嘗試着建立遠端的連接。 
3. 建立socket連接。 
對於準備建立一個連接,服務器端要兩個步驟:bind, listen;客戶端一個步驟:
connct。如果服務器端accept一個connect,而客戶端得到了這個accept的確認,那麼
一個連接就建立了。


三、客戶/服務器模式模式的理解

客戶/服務器模式採取的是主動請求方式:

首先服務器方要先啓動,並根據請求提供相應服務:

1. 打開一通信通道並告知本地主機,它願意在某一公認地址上(周知口,如FTP爲21)接收客戶請求;

2. 等待客戶請求到達該端口;

3. 接收到重複服務請求,處理該請求併發送應答信號。接收到併發服務請求,要激活一新進程來處理這個客戶請求(如UNIX系統中用fork、exec)。新進程處理此客戶請求,並不需要對其它請求作出應答。服務完成後,關閉此新進程與客戶的通信鏈路,並終止。

4. 返回第二步,等待另一客戶請求。

5. 關閉服務器

客戶方:

1. 打開一通信通道,並連接到服務器所在主機的特定端口;

2. 向服務器發服務請求報文,等待並接收應答;繼續提出請求......

3. 請求結束後關閉通信通道並終止。

從上面所描述過程可知:

1. 客戶與服務器進程的作用是非對稱的,因此編碼不同。

2. 服務進程一般是先涌紀紀戶請求而啓動的。只要系統運行,該服務進程一直存在,直到正常或強迫終止。

轉自:http://blog.csdn.net/yeyuangen/article/details/6799575

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章