Socket 連接建立過程


阻塞模式下:

1,客戶端向服務器端發起請求建立連接時,服務器端只需要運行到

serverSocket = new ServerSocket(port,3);

客戶端注冊的  SelectionKey.OP_CONNECT 事件就能夠發生。

也就是說,不需要等到服務器端執行到

socket = serverSocket.accept();

客戶端注冊的SelectionKey.OP_CONNECT 通過 select()查詢時就返回一個 大於 0 的值了。

 

2,server端未執行到

  serverSocket = new ServerSocket(port,3);

時,client端執行了

socket.connect(new InetSocketAddress(host, port));

時,拋出異常:Exception in thread "main" java.net.ConnectException: Connection refused: connect

 

3,server端執行了

 serverSocket = new ServerSocket(port,3);

之后,client端執行

socket.connect(new InetSocketAddress(host, port));

System.out.println("connection accepted " + socket.getInetAddress() + ":" + socket.getPort());

時,能成功執行,且輸出:connection accepted localhost/127.0.0.1:8000

然后client 能夠給server發送數據。但只有到server執行了 accpet()后,才能受理client的數據。

 

4,關於SocketChannel類的connect()方法作用

當SocketChannel工作於非阻塞模式下時,調用connect()時會立即返回:如果連接建立成功則返回的是true(比如連接localhost時,能立即建立起連接),否則返回false。在非阻塞模式下,返回false后,必須要在隨后的某個地方調用finishConnect()方法完成連接。

當SocketChannel處於阻塞模式下時,調用connect()時會進入阻塞,直至連接建立成功或者發生IO錯誤時,才從阻塞狀態中退出。

 

5,Selector.select()方法從阻塞狀態返回的詳細過程 和 ServerSocket.accpet()方法從阻塞狀態返回的過程

select()方法:如果事件注冊或者已經注冊的事件沒有發生。調用select()方法的線程將會被阻塞。該線程可以通過如下方法退出阻塞狀態:

①其他線程執行了同一個Selector的wakeup()方法將之喚醒,在這種情況下,select()返回值為0。表明它是被wakeup()喚醒的,而不是因為有注冊的事件發生了而喚醒的。

②注冊到Selector上的事件發生了。此時,線程也會從阻塞狀態中退出,並且select()方法返回一個非0值,代表發生的事件的數目。

參考一段代碼:

1 while(!shutdown){
2                 try{
3                     registerTargets();
4                     if(selector.select() > 0)
5                         processSelectedKeys();
6                 }catch(Exception e){
7                     e.printStackTrace();
8                 }
9             }

假設線程A執行到第4行的if語句中的select()方法時,進入了阻塞狀態:

如果另外一個線程執行 selector.wakeup() 。該線程(線程A)將會被喚醒,然后select()方法返回一個0值(表示沒有注冊的事件發生),執行if判斷,if語句就會不成立,繼續下一輪while循環。-----先從select()方法中返回,再執行if判斷----“驗證了程序總是從段點處往下執行的!!!”

 

accept()方法從阻塞狀態返回的具體細節---假設通道工作在阻塞模式。

當serverSocketChannel.acppet()執行時,若此時沒有連接請求到來(准確地說應該是:操作系統管理的連接請求隊列為空),線程就會進入阻塞狀態。直到下面情況時,從阻塞狀態返回:

①被其他線程中斷了--不詳細討論

②連接請求到來了(操作系統管理的連接請求隊列中有數據了),線程從accept()退出阻塞狀態的同時返回一個SocketChannel對象。這個對象就表示:從客戶端到服務器端建立起的一條TCP連接。

 

6,當Selector中注冊的事件發生時,底層發生了什么操作?

以OP_ACCEPT事件為例討論:JDK中關於該事件的注釋如下:

Suppose that a selection key's interest set contains OP_ACCEPT at the start of a selection operation. If the selector detects that the corresponding server-socket channel is ready to accept another connection, or has an error pending, then it will add OP_ACCEPT to the key's ready set and add the key to its selected-key set.

首先,selection key的感興趣的集合中要包含OP_ACCEPT,也即,要把OP_ACCEPT事件注冊到Selector中,代碼如下:

serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);

其次,注冊了之后,由selector來 detect---檢測 相應的 server-socket 通道是否已經准備好接收其他的連接請求,如果准備好了:

就會把OP_ACCEPT添加到這個SelectionKey對象的 就緒事件集合(ready set)中,同時,再把這個SelectionKey對象添加到已選擇的Key集合(selected-key set)中。

注意:上面的過程涉及到了兩個集合:一個是事件的集合,另一個是SelectionKey對象的集合。明顯這兩個集合是不同的。事件一共只有如下四種:

public static final int OP_ACCEPT = 1 << 4;
public static final int OP_CONNECT = 1 << 3;
public static final int OP_CONNECT = 1 << 3;
public static final int OP_WRITE = 1 << 2;

事件的集合是附屬在某個SelectionKey對象上的。也即,一個SelectionKey對象就擁有一個事件集合。

SelectionKey集合分以下三種:

已注冊的鍵的集合(Registered key set)
已選擇的鍵的集合(Selected key set)
已取消的鍵的集合(Cancelled key set)

如上面所說,如果selector檢測到了通道准備好了接收其他的連接請求,就會把相應的SelectionKey對象添加到已選擇的鍵集合中。

 


注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



 
粤ICP备14056181号  © 2014-2021 ITdaan.com