阻塞模式下:
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對象添加到已選擇的鍵集合中。