package com.nio.real;
import java.io.IOException;
public class TimeClient {
public static void main(String[] args)throws IOException {
// 設置要監聽的端口
int port = 8785;
if (args != null && args.length > 0){
try {
port = Integer.valueOf(args[0]);
} catch (NumberFormatException e) {
e.printStackTrace();
}
}
// 創建TimeClientHandler線程來處理異步連接和讀寫操作
new Thread(new TimeClientHandler("localhost", port), "NIO-MultiplexerTimeClient-001").start();
}
}
package com.nio.real;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;
public class TimeClientHandler implements Runnable{
private String host;
private int port;
private Selector selector;
private SocketChannel socketChannel;
private volatile boolean stop;
public TimeClientHandler(String host, int port) {
this.port = port;
this.host = host == null ? "localhost" : host;
try {
// 初始化多路複用器和SocketChannel
selector = Selector.open();
socketChannel = SocketChannel.open();
// 設置爲異步非阻塞模式
socketChannel.configureBlocking(false);
} catch (IOException e) {
e.printStackTrace();
System.exit(1);
}
}
@Override
public void run() {
try {
doConnect();
} catch (IOException e) {
e.printStackTrace();
System.exit(1);
}
while (!stop){
try {
// 設置休眠時間爲1s,無論是否發生讀寫等事件,selector都會每隔1s倍喚醒一次
selector.select(1000);
Set<SelectionKey> selectionKeys = selector.selectedKeys();
Iterator<SelectionKey> it = selectionKeys.iterator();
SelectionKey key = null;
// 多路複用器在線程run方法的無限循環體內輪詢準備就緒的key
while (it.hasNext()){
key = it.next();
it.remove();
try {
// 當有就緒的channel時,執行handleInput方法
handleInput(key);
} catch (Exception e) {
if (key != null){
key.cancel();
if (key.channel() != null){
key.channel().close();
}
}
}
}
} catch (IOException e) {
e.printStackTrace();
System.exit(1);
}
}
// 多路複用關閉後所有註冊在上面的channel和pipe等資源都會被自動去註冊並關閉,所以不需要重複釋放資源
if (selector != null){
try {
selector.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
private void handleInput(SelectionKey key)throws IOException {
// 這個key是不是有效的
if (key.isValid()){
// 判斷是否連接成功
SocketChannel sc = (SocketChannel) key.channel();
// 判斷連接狀態,處於連接狀態說明服務端已經返回ack應答消息
if (key.isConnectable()){
// 對連接結果判斷,返回TRUE說明客戶端連接成功
if (sc.finishConnect()){
// 將sc註冊到多路複用器上,註冊SelectionKey.OP_READ,監聽網絡ducaoz,然後將消息發送給服務端
sc.register(selector,SelectionKey.OP_READ);
doWrite(sc);
}else {
// 連接失敗,進程退出
System.exit(1);
}
}
if (key.isReadable()){
// 預分配1MB接收緩衝區的應答消息
ByteBuffer readBuffer = ByteBuffer.allocate(1024);
// 調用read()方法進行異步讀取操作,將客戶端請求消息讀取到緩衝區
int readBytes = sc.read(readBuffer);
// readBytes大於0, 讀到了字節,對字節進行編碼處理
if (readBytes > 0){
// 將緩衝區當前的limit設置爲position,position = 0;用於後續對緩衝區的讀取操作
readBuffer.flip();
// 根據緩衝區可讀的字節個數創建字節數組
// readBuffer.remaining()返回得是新數組的大小,源碼:limit - position
byte[] bytes = new byte[readBuffer.remaining()];
// 將緩衝區的可讀的字節數組複製到新創建的字節數組中
readBuffer.get(bytes);
// 對讀取到的消息進行解碼並且打印
String body = new String(bytes, "utf-8");
System.out.println("現在是:" + body);
// 將stop設置爲TRUE,線程退出循環
this.stop = true;
}else if (readBytes < 0){
// 對端鏈路關閉
key.cancel();
sc.close();
}else
; // 讀到0字節,忽略
}
}
}
private void doConnect() throws IOException{
// 如果直接連接成功,則註冊到多路複用器上,發送請求消息,讀應答
if (socketChannel.connect(new InetSocketAddress(host, port))){
socketChannel.register(selector, SelectionKey.OP_READ);
doWrite(socketChannel);
}else {
// 沒有直接連接成功,說明服務端沒有返回TCP握手應答消息,但是不代表連接失敗
// 註冊到多路複用器上,註冊electionKey.OP_CONNECT,當服務端返回TCPsyn-ack消息後,
// selector就能夠輪訓到這個socketChannel處於連接就緒狀態
socketChannel.register(selector, SelectionKey.OP_CONNECT);
}
}
private void doWrite(SocketChannel sc)throws IOException {
// 構造請求消息體
String str = "QUERY TIME ORDER";
// 將字符串編碼成字節數組
byte[] req = str.getBytes();
// 根據字節數組的容量創建ByteBuffer
ByteBuffer writeBuffer = ByteBuffer.allocate(req.length);
// 將字節數組複製到緩衝區中
writeBuffer.put(req);
// 將緩衝區當前的limit設置爲position,position = 0;用於後續對緩衝區的讀取操作
writeBuffer.flip();
// 發送
sc.write(writeBuffer);
// 對發送結果進行判斷,如果緩衝區中的消息全部發送完畢,將輸出打印結果
if (!writeBuffer.hasRemaining()){
System.out.println("客戶端成功發送: " + str);
}
}
}
【1】NIO入門案例使用netty最新版本框架代碼實現及詳細註釋
https://blog.csdn.net/wildwolf_001/article/details/81132896
【2】NIO入門案例服務端的代碼具體實現以及詳細註釋
https://blog.csdn.net/wildwolf_001/article/details/81085938
【3】NIO類庫的簡介
https://blog.csdn.net/wildwolf_001/article/details/81069324
【4】NIO入門案例之分析NIO服務端序列圖
https://blog.csdn.net/wildwolf_001/article/details/81069180