1. 程式人生 > >Java NIO(6):Server和Client案例

Java NIO(6):Server和Client案例

Server:

package com.tony.app;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.nio.ByteBuffer;
import java.nio.channels.SelectableChannel;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;

/**
 * 伺服器
 * 
 * @author TONY
 *
 */
public class Server {
	// 緩衝區的大小
	private final static int BUFFER_SIZE = 1024;

	// 緩衝區
	private ByteBuffer buffer = ByteBuffer.allocate(BUFFER_SIZE);

	// Server監聽的埠號
    // private final static int PORT = 8888;

	// 選擇器
	private Selector selector = null;

	// 初始化工作
	public void init(int port) throws IOException {
		System.out.println("============ Listening On Port : " + port + "============");
		// 開啟伺服器套接字通道
		ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
		// 設定為非阻塞狀態
		serverSocketChannel.configureBlocking(false);
		// 獲取通道相關聯的套接字
		ServerSocket serverSocket = serverSocketChannel.socket();
		// 繫結埠號
		serverSocket.bind(new InetSocketAddress(port));
		// 開啟一個選擇器
		selector = Selector.open();
		// 伺服器套接字註冊到Selector中 並指定Selector監控連線事件
		serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
	}

	public void listen() throws IOException {
		while (true) {
			// 開啟選擇
			int readyChannels = selector.select(); // 沒有通道就緒 一直阻塞 返回已經就緒通道的數目(有可能為0)
			if (readyChannels == 0) {
				continue;
			}
			// 返回已選擇鍵的集合
			Set<SelectionKey> selectedKeys = selector.selectedKeys();
			// 遍歷鍵 並檢查鍵對應的通道里註冊的就緒事件
			Iterator iterator = selectedKeys.iterator();
			while (iterator.hasNext()) {
				// SelectionKey封裝了一個通道和選擇器的註冊關係
				SelectionKey key = (SelectionKey) iterator.next();
				handleKey(key);
				// Selector不會移除SelectionKey 處理完了手動移除
				iterator.remove();
			}
		}
	}

	// 處理SelectionKey
	private void handleKey(SelectionKey key) throws IOException {
		// 是否有連線進來
		if (key.isAcceptable()) {
			ServerSocketChannel server = (ServerSocketChannel) key.channel();// 獲取通道 轉化為要處理的型別
			SocketChannel socketChannel = server.accept();
			// SocketChannel通道的可讀事件註冊到Selector中
			registerChannel(selector, socketChannel, SelectionKey.OP_READ);
			// 連線成功 向Client打個招呼
			if (socketChannel.isConnected()) {
				buffer.clear();
				buffer.put("I am Server...".getBytes());
				buffer.flip();
				socketChannel.write(buffer);

			}

		}
		// 通道的可讀事件就緒
		if (key.isReadable()) {
			SocketChannel socketChannel = (SocketChannel) key.channel();
			buffer.clear(); // 清空緩衝區
			// 讀取資料
			int len = 0;
			while ((len = socketChannel.read(buffer)) > 0) {
				buffer.flip();
				while (buffer.hasRemaining()) {
					System.out.println("Server讀取的資料:" + new String(buffer.array(), 0, len));
				}
			}
			if (len < 0) {
				// 非法的SelectionKey 關閉Channel
				socketChannel.close();
			}
			// SocketChannel通道的可寫事件註冊到Selector中
			registerChannel(selector, socketChannel, SelectionKey.OP_WRITE);
		}
		// 通道的可寫事件就緒
		if (key.isWritable()) {
			SocketChannel socketChannel = (SocketChannel) key.channel();
			buffer.clear(); // 清空緩衝區
			// 準備傳送的資料
			String message_from_server = "Hello,Client... " + socketChannel.getLocalAddress();
			buffer.put(message_from_server.getBytes());
			buffer.flip();
			socketChannel.write(buffer);
			System.out.println("Server傳送的資料:" + message_from_server);
			// SocketChannel通道的可寫事件註冊到Selector中
			registerChannel(selector, socketChannel, SelectionKey.OP_READ);
		}

	}

	// 註冊通道到指定Selector上
	private void registerChannel(Selector selector, SelectableChannel channel, int ops) throws IOException {
		if (channel == null) {
			return;
		}
		channel.configureBlocking(false);
		// 註冊通道
		channel.register(selector, ops);

	}
	
	public static void main(String[] args) throws IOException {
		Server server = new Server();
		server.init(8888);
		server.listen();
	}
}

Client:

package com.tony.app;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectableChannel;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;

/**
 * 客戶端
 * 
 * @author TONY
 *
 */
public class Client {

	// 緩衝區的大小
	private final static int BUFFER_SIZE = 1024;
	// 緩衝區
	private ByteBuffer buffer = ByteBuffer.allocate(BUFFER_SIZE);

	// 選擇器
	private Selector selector = null;

	private final static int PORT = 8888;

	// 初始化工作
	public void init(String address) throws IOException {

		// 開啟客戶端套接字通道
		SocketChannel socketChannel = SocketChannel.open();
		// 設定為非阻塞狀態
		socketChannel.configureBlocking(false);
		// 開啟選擇器
		selector = Selector.open();
		// 註冊
		socketChannel.register(selector, SelectionKey.OP_CONNECT);
		// 發起連線
		socketChannel.connect(new InetSocketAddress(address, PORT));

	}

	public void connect() throws IOException {
		while (true) {
			int readyChannels = selector.select();
			if (readyChannels == 0) {
				continue;
			}
			// 返回已選擇鍵的集合
			Set<SelectionKey> selectedKeys = selector.selectedKeys();
			// 遍歷鍵 並檢查鍵對應的通道里註冊的就緒事件
			Iterator iterator = selectedKeys.iterator();
			while (iterator.hasNext()) {
				// SelectionKey封裝了一個通道和選擇器的註冊關係
				SelectionKey key = (SelectionKey) iterator.next();
				handleKey(key);
				// Selector不會移除SelectionKey 處理完了手動移除
				iterator.remove();
			}
		}
	}

	// 處理SelectionKey
	private void handleKey(SelectionKey key) throws IOException {
		// 是否可連線
		if (key.isConnectable()) {
			SocketChannel socketChannel = (SocketChannel) key.channel();
			// 完成連線
			if(socketChannel.isConnectionPending()) {
				socketChannel.finishConnect();
				System.out.println("連線成功...");
				// 傳送資料給Server
				String message_to_server = "Hello,Server...";
				buffer.clear();
				buffer.put(message_to_server.getBytes());
				buffer.flip();
				socketChannel.write(buffer);
				System.out.println("Client傳送的資料:" + message_to_server);
				registerChannel(selector, socketChannel, SelectionKey.OP_READ);
			}else {
				System.exit(1); // 連線失敗 退出
			}
			

		}
		// 通道的可讀事件就緒
		if (key.isReadable()) {
			SocketChannel socketChannel = (SocketChannel) key.channel();
			buffer.clear(); // 清空緩衝區
			// 讀取資料
			int len = 0;
			while ((len = socketChannel.read(buffer)) > 0) {
				buffer.flip();
				while (buffer.hasRemaining()) {
					System.out.println("Client讀取的資料:" + new String(buffer.array(), 0, len));
				}
			}
			if (len < 0) {
				// 非法的SelectionKey 關閉Channel
				socketChannel.close();
			}
			// SocketChannel通道的可寫事件註冊到Selector中
			registerChannel(selector, socketChannel, SelectionKey.OP_WRITE);
		}
		// 通道的可寫事件就緒
		if (key.isWritable()) {
			SocketChannel socketChannel = (SocketChannel) key.channel();
			buffer.clear(); // 清空緩衝區
			// 準備傳送的資料
			String message_from_server = "Hello,Server... " + socketChannel.getLocalAddress();
			buffer.put(message_from_server.getBytes());
			buffer.flip();
			socketChannel.write(buffer);
			System.out.println("Client傳送的資料:" + message_from_server);
			// SocketChannel通道的可寫事件註冊到Selector中
			registerChannel(selector, socketChannel, SelectionKey.OP_READ);
		}

	}

	// 註冊通道到指定Selector上
	private void registerChannel(Selector selector, SelectableChannel channel, int ops) throws IOException {
		if (channel == null) {
			return;
		}
		channel.configureBlocking(false);
		// 註冊通道
		channel.register(selector, ops);

	}
	
	public static void main(String[] args) throws IOException {
		Client client = new Client();
		client.init("localhost");
		client.connect();
	}

}