版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_30698633/article/details/79012587
服务器端:相当于是一个接收客户端消息的分发器,为了简单,直接在接收到客户端的消息后,
直接发送给所有的客户端
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("连接关闭");
}
}
}
}
}
}
}