1. 程式人生 > >使用java nio 編寫簡易聊天室

使用java nio 編寫簡易聊天室

伺服器端:相當於是一個接收客戶端訊息的分發器,為了簡單,直接在接收到客戶端的訊息後,

                 直接傳送給所有的客戶端

package chatroom.chatserver;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.nio.ByteBuffer;
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;

public class ChatServer {

	public static void main(String[] args) {
		new ChatServer().serverRun();
	}

	public void serverRun() {

		Selector selector = null;

		try {
			ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
			serverSocketChannel.configureBlocking(false);
			ServerSocket socket = serverSocketChannel.socket();
			socket.bind(new InetSocketAddress(8888));

			selector = Selector.open();

			// 這裡只是將該channel的accept設定為了selector感興趣的,
                        // 此時並沒有準備好,只有當客戶端有連線請求的時候,這個channel的
                        // 感興趣事件才會準備好
			SelectionKey register = serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);

			System.out.println("啟動監聽:8888埠");
			while (true) {
			// 這裡只有存在準備好的事件的時候,才會返回,否則會一直阻塞
                        // 怎麼才算準備好的? (當有相應的事件到來的時候,就說這個channel的這個事件準備好了)
                        // accept 當客戶端有連線請求時,才會準備好,不過這裡要想能獲取到,
                        //     必須之前註冊了accept事件,即設定為了感興趣的事件
                        // read  當客戶端有訊息到來時,才會準備好,前提也是設定了read的感興趣事件
                        // write 如果設定了write為感興趣事件,那麼每次都能在這裡獲取到,並執行寫事件,
                        //     因為寫事件是服務端向客戶端寫事件,所以如果設定成了write事件,不修改成別的
                        //      事件的話,這裡將會一直執行write事件。
				int select = selector.select();
				System.out.println("監聽中。。。" + select);
				if (select > 0) {
		       // 這裡獲取到準備好的SelectionKey的集合
                       // SelectionKey 我理解的就是一個pojo,它裡面封裝了該channel中selector感興趣的事件,
                       // 準備好的事件,各種狀態,選擇器和channel等資訊,我們可以通過它獲取各種需要的資訊
					Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
					while (iterator.hasNext()) {
						SelectionKey key = iterator.next();
						if (key.isValid() && key.isAcceptable()) {
							accept(key);
						} else if (key.isValid() && key.isReadable()) {
							read(key);
						} else if (key.isValid() && key.isWritable()) {
							write(key);
						}
					       // 這裡一定要移除,不然會一直存在之前準備好的事件,出現異常
						iterator.remove();
					}
				}
			}

		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	private void write(SelectionKey key) {
		// 為了簡單,並沒有通過SelectionKey.OP_WRITE進行像所有客戶端分發訊息
	}

	private void read(SelectionKey key) {
		SocketChannel channel = (SocketChannel) key.channel();
		ByteBuffer buffer = ByteBuffer.allocate(1024);
		try {
			int read = channel.read(buffer);
			if (read > 0) {
				System.out.println(read);
				buffer.flip();
				String info = new String(buffer.array(), 0, read);
				System.out.println("伺服器接收到訊息:" + info);
				// 將收到的資訊發給聊天室的所有人
				// 這裡獲取到的是所有的channel的Keys集合
				Set<SelectionKey> selectedKeys = key.selector().keys();
				if (!selectedKeys.isEmpty()) {
					Iterator<SelectionKey> iterator = selectedKeys.iterator();
					while (iterator.hasNext()) {
						System.out.println("回寫資料");
						SocketChannel channel2 = null;
						try {
							SelectionKey key1 = iterator.next();
							if (key1.isValid() && !key1.isReadable()) {
								continue;
							}
							channel2 = (SocketChannel) key1.channel();
							channel2.configureBlocking(false);
							channel2.write(buffer);
							buffer.rewind();
						} catch (IOException e) {
							if (channel2 != null) {
								try {
									channel2.close();
								} catch (IOException e1) {
								}
								System.out.println("關閉連線");
							}
						}
					}
				}
			}
		} catch (IOException e) {
			if (channel != null) {
				try {
					channel.close();
				} catch (IOException e1) {
				}
				System.out.println("關閉連線");
			}
		}

	}

	private void accept(SelectionKey key) {
		ServerSocketChannel ssc = null;
		SocketChannel channel = null;
		try {
			ssc = (ServerSocketChannel) key.channel();
			channel = ssc.accept();
			channel.configureBlocking(false);

			channel.register(key.selector(), SelectionKey.OP_READ);

		} catch (IOException e) {
			if (channel != null) {
				try {
					channel.close();
				} catch (IOException e1) {
					e1.printStackTrace();
				}
			}
			if (ssc != null) {
				try {
					ssc.close();
				} catch (IOException e1) {
					e1.printStackTrace();
				}
			}
		}

	}
}

     客戶端:

package chatroom.chatserver;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;
import java.util.Scanner;

public class ChatClient {

	public static void main(String[] args) {
		new ChatClient().clientRun();
	}

	public void clientRun() {

		try {
			SocketChannel socketChannel = SocketChannel.open();
			Socket socket = socketChannel.socket();
			socket.connect(new InetSocketAddress("127.0.0.1", 8888));

			socketChannel.configureBlocking(false);
                        // 建立一個新執行緒進行伺服器訊息的監聽
			new Thread(new receive(socketChannel)).start();

			ByteBuffer buffer = ByteBuffer.allocate(1024);

			Scanner scanner = new Scanner(System.in);

			while (true) {
				System.out.println("請輸入:");
				buffer.clear();
				buffer.put(scanner.nextLine().getBytes());

                       // 這裡寫之前一定要flip,因為上面的put已經把buffer的position等資訊修改了,
                       // 將快取資料寫出去的話,要從buffer的0開始
				buffer.flip();
				socketChannel.write(buffer);
			}

		} catch (IOException e) {
			e.printStackTrace();
		}

	}

	class receive implements Runnable {

		private SocketChannel socketChannel;

		public receive(SocketChannel socketChannel) {
			this.socketChannel = socketChannel;
		}

		@Override
		public void run() {

			System.out.println("開始監聽資料");

			ByteBuffer buffer = ByteBuffer.allocate(1024);

			while (true) {
				buffer.clear();
				int read;
				try {
					read = socketChannel.read(buffer);
					if (read > 0) {
						buffer.flip();
						System.out.println("接收到資料:" + new String(buffer.array(), 0, read));
					}
				} catch (IOException e) {
					if (socketChannel != null) {
						try {
							socketChannel.close();
						} catch (IOException e1) {
							System.out.println("連線關閉");
						}
					}
				}

			}

		}

	}

}