1. 服务端代码
ChatServer类:
package aio.server;
import java.io.Closeable;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.channels.AsynchronousChannelGroup;
import java.nio.channels.AsynchronousServerSocketChannel;
import java.nio.channels.AsynchronousSocketChannel;
import java.nio.channels.CompletionHandler;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ChatServer {
private static final String LOCALHOST = "localhost";
private static final int DEFAULT_PORT = 8888;
private static final String QUIT = "quit";
private static final int BUFFER = 1024;
private static final int THREADPOOL_SIZE = 8;
private AsynchronousChannelGroup channelGroup;
private AsynchronousServerSocketChannel serverSocketChannel;
private List<ClientHandler> connectedClients;
private Charset charset = Charset.forName("UTF-8");
private int port;
public ChatServer(){
this(DEFAULT_PORT);
}
public ChatServer(int port){
this.port = port;
this.connectedClients = new ArrayList<>();
}
/**
* 准备退出
* @param msg
* @return
*/
private boolean readyToQuit(String msg){
return QUIT.equals(msg);
}
/**
* 关闭此流并释放与之相关联的任何系统资源,如果流已经关闭,则调用此方法将不起作用。
* @param closeable
*/
private void close(Closeable closeable){
if(closeable != null){
try {
closeable.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* 往客户端列表里添加一个新的客户端
* @param clientHandler
*/
private synchronized void addClient(ClientHandler clientHandler) {
connectedClients.add(clientHandler);
System.out.println(getClientName(clientHandler.clientChannel) + "已经连接到服务器");
}
/**
* 将客户端链表里移除一个断开的客户端,同时关闭连接
* @param clientHandler
*/
private synchronized void removeClient(ClientHandler clientHandler) {
connectedClients.remove(clientHandler);
System.out.println(getClientName(clientHandler.clientChannel) + "已断开连接");
close(clientHandler.clientChannel);
}
/**
* 接收消息,并解码
* @param byteBuffer
* @return
*/
private String receive(ByteBuffer byteBuffer){
CharBuffer charBuffer = charset.decode(byteBuffer);
return String.valueOf(charBuffer);
}
/**
* 获取客户端的信息
* @param clientChannel
* @return
*/
private String getClientName(AsynchronousSocketChannel clientChannel) {
int clientPort = -1;
try {
InetSocketAddress inetSocketAddress = (InetSocketAddress) clientChannel.getRemoteAddress();
clientPort = inetSocketAddress.getPort();
} catch (IOException e) {
e.printStackTrace();
}
return "客户端[" + clientPort + "]";
}
/**
* 转发客户端的消息
* @param clientChannel
* @param fwdMsg
*/
private synchronized void forwardMessage(AsynchronousSocketChannel clientChannel, String fwdMsg) {
for (ClientHandler handler : connectedClients){
if (!handler.clientChannel.equals(clientChannel)){
try { // 防止发生意想不到的错误或异常
ByteBuffer byteBuffer = charset.encode(getClientName(handler.clientChannel) + ":" + fwdMsg);
handler.clientChannel.write(byteBuffer, null, handler);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
/**
* 当客户端连接时,用于接收客户端请求的Handler
*/
private class AcceptHandler implements
CompletionHandler<AsynchronousSocketChannel, Object>{
@Override
public void completed(AsynchronousSocketChannel clientChannel, Object attachment) {
if (serverSocketChannel.isOpen()){ //保证serverSocketChannel继续去监听
serverSocketChannel.accept(null, this);
}
if (clientChannel != null && clientChannel.isOpen()){
ClientHandler clientHandler = new ClientHandler(clientChannel);
ByteBuffer byteBuffer = ByteBuffer.allocate(BUFFER);
// 将新用户添加到在线用户列表
addClient(clientHandler);
//启动异步读操作,以从该通道读取到给定缓冲器中的字节序列
clientChannel.read(byteBuffer, byteBuffer, clientHandler);
}
}
@Override
public void failed(Throwable exc, Object attachment) {
System.out.println("连接失败:" + exc);
}
}
/**
* 处理客户端事件
*/
private class ClientHandler implements
CompletionHandler<Integer, Object>{
private AsynchronousSocketChannel clientChannel;
public ClientHandler(AsynchronousSocketChannel channel){
this.clientChannel = channel;
}
@Override
public void completed(Integer result, Object attachment) {
ByteBuffer byteBuffer = (ByteBuffer)attachment;
if (byteBuffer != null){ // 如果发生了读事件
if (result <= 0){
// 客户端通道发生了异常
// 将客户从在线客户列表中去除
removeClient(this);
}else {
byteBuffer.flip(); //转换为读操作
String fwdMsg = receive(byteBuffer); //读取buffer里的消息
System.out.println(getClientName(clientChannel) + ":" +fwdMsg);
forwardMessage(clientChannel, fwdMsg); //向其他用户转发消息
byteBuffer.clear();//清除缓冲区
// 检查用户是否退出
if (readyToQuit(fwdMsg)){
// 将客户从在线客户列表中去除
removeClient(this);
}else {
clientChannel.read(byteBuffer,byteBuffer,this);
}
}
}
}
@Override
public void failed(Throwable exc, Object attachment) {
System.out.println("读写失败: " + exc);
}
}
/**
* 启动服务器
*/
private void start(){
try {
ExecutorService executorService = Executors.newFixedThreadPool(THREADPOOL_SIZE);//初始化线程池
channelGroup = AsynchronousChannelGroup.withThreadPool(executorService);//将线程池加入到异步通道,进行资源共享
serverSocketChannel = AsynchronousServerSocketChannel.open(channelGroup);
serverSocketChannel.bind(new InetSocketAddress(LOCALHOST, port));
System.out.println("启动服务器,监听端口:"+ port);
while (true){
serverSocketChannel.accept(null, new AcceptHandler());
System.in.read();//阻塞调用,防止占用系统资源,一直调用accept()函数
}
} catch (IOException e) {
e.printStackTrace();
}finally {
close(serverSocketChannel);
}
}
/**
* 主函数入口
* @param args
*/
public static void main(String[] args) {
ChatServer server = new ChatServer(7777);
server.start();
}
}
2. 客户端代码
ChatClient类:
package aio.client;
import java.io.Closeable;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousSocketChannel;
import java.nio.charset.Charset;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
public class ChatClient {
private static final String LOCALHOST = "localhost";
private static final int DEFAULT_PORT = 8888;
private static final String QUIT = "quit";
private static final int BUFFER = 1024;
private String host;
private int port;
private AsynchronousSocketChannel clientChannel;
private Charset charset = Charset.forName("UTF-8");
public ChatClient(){
this(LOCALHOST, DEFAULT_PORT);
}
public ChatClient(String host, int port){
this.host = host;
this.port = port;
}
/**
* 关闭此流并释放与之相关联的任何系统资源,如果流已经关闭,则调用此方法将不起作用。
* @param closeable
*/
private void close(Closeable closeable){
if(closeable != null){
try {
closeable.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* 判断客户端是否准备退出
* @param msg
* @return
*/
public boolean readyToQuit(String msg){
return QUIT.equals(msg);
}
private void start(){
try {
// 创建channel
clientChannel = AsynchronousSocketChannel.open();
Future<Void> future = clientChannel.connect(new InetSocketAddress(host, port));
future.get();// 阻塞式调用
//启动一个新的线程,处理用户的输入
new Thread(new UserInputHandler(this)).start();
ByteBuffer byteBuffer = ByteBuffer.allocate(BUFFER);
// 读取服务器转发的消息
while(true){
//启动异步读操作,以从该通道读取到给定的缓冲器字节序列
Future<Integer> readResult = clientChannel.read(byteBuffer);
//Future的get方法返回读取的字节数或-1如果没有字节可以读取,因为通道已经到达流终止。
int result = readResult.get();
if(result <= 0){
// 服务器异常
System.out.println("服务器断开");
close(clientChannel);
// 0是正常退出,非0是不正常退出
System.exit(1);
}else {
byteBuffer.flip(); //准备读取
String msg = String.valueOf(charset.decode(byteBuffer));
byteBuffer.clear();
System.out.println(msg);
}
}
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
public void send(String msg){
if (msg.isEmpty()){
//没有必要向服务器发送空白的消息从而占用资源
return;
}
ByteBuffer byteBuffer = charset.encode(msg);
Future<Integer> writeResult = clientChannel.write(byteBuffer);
try {
writeResult.get();
} catch (InterruptedException | ExecutionException e) {
System.out.println("消息发送失败");
e.printStackTrace();
}
}
public static void main(String[] args) {
ChatClient chatClient = new ChatClient("127.0.0.1", 7777);
chatClient.start();
}
}
UserInputHandler类:
package aio.client;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
public class UserInputHandler implements Runnable{
private ChatClient chatclient;
public UserInputHandler(ChatClient chatClient){
this.chatclient = chatClient;
}
/**
*
*/
@Override
public void run() {
try {
//等待用户输入的消息
BufferedReader consoleReader = new BufferedReader(
new InputStreamReader(System.in)
);
while(true){
String input = consoleReader.readLine();
//向服务器发送消息
chatclient.send(input);
//检查用户是否准备退出
if(chatclient.readyToQuit(input)){
break;
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}