1.监听客户端输入线程
public class ChatThread extends Thread {
private Selector selector;
private SocketChannel socketChannel;
public ChatThread(Selector selector, SocketChannel socketChannel) {
super();
this.selector = selector;
this.socketChannel = socketChannel;
}
@Override
public void run() {
try {
//等待连接建立
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
Scanner scanner = new Scanner(System.in);
System.out.println("========================请输入您要发送给服务端的消息:=================================");
while (scanner.hasNextLine()) {
String s = scanner.nextLine();
try {
//将输入的消息发送给客户端
socketChannel.register(selector, SelectionKey.OP_WRITE, ByteBuffer.wrap(s.getBytes()));
//唤醒之前因为监听OP_READ而阻塞的select()
selector.wakeup();
} catch (ClosedChannelException e) {
e.printStackTrace();
}
}
}
}
2.客户端启动类
public class NioClient {
public static void main(String[] args) {
try {
//初始化客户端
SocketChannel socketChannel = SocketChannel.open();
//设置非阻塞
socketChannel.configureBlocking(false);
Selector selector = Selector.open();
//注册连接事件
socketChannel.register(selector, SelectionKey.OP_CONNECT);
//发起连接
socketChannel.connect(new InetSocketAddress("192.168.100.38", 8899));
//开启监听
new ChatThread(selector, socketChannel).start();
//轮询处理
while (true) {
if (socketChannel.isOpen()) {
//在注册的键中选择已准备就绪的事件
selector.select();
//已选择键集
Set keys = selector.selectedKeys();
Iterator iterator = keys.iterator();
//处理准备就绪的事件
while (iterator.hasNext()) {
SelectionKey key = iterator.next();
//删除当前键,避免重复消费
iterator.remove();
//连接
if (key.isConnectable()) {
//在非阻塞模式下connect也是非阻塞的,所以要确保连接已经建立完成
while (!socketChannel.finishConnect()) {
System.out.println("连接中");
}
socketChannel.register(selector, SelectionKey.OP_READ);
}
//控制台监听到有输入,注册OP_WRITE,然后将消息附在attachment中
if (key.isWritable()) {
//发送消息给服务端
socketChannel.write((ByteBuffer) key.attachment());
/*
已处理完此次输入,但OP_WRITE只要当前通道输出方向没有被占用
就会准备就绪,select()不会阻塞(但我们需要控制台触发,在没有输入时
select()需要阻塞),因此改为监听OP_READ事件,该事件只有在socket
有输入时select()才会返回。
*/
socketChannel.register(selector, SelectionKey.OP_READ);
System.out.println("==============" + Calendar.getInstance().getTime() + " ==============");
}
//处理输入事件
if (key.isReadable()) {
ByteBuffer byteBuffer = ByteBuffer.allocate(1024 * 4);
int len = 0;
//在服务端关闭后会发送FIN报文,会触发read事件,但连接已关闭,此时read()会产生异常
if ((len = socketChannel.read(byteBuffer)) > 0) {
System.out.println("接收到來自服务器的消息\t");
System.out.println(new String(byteBuffer.array(), 0, len));
} else {
System.out.println("服务器异常,请联系客服人员!正在关闭客户端.........");
key.cancel();
socketChannel.close();
}
System.out.println("=========================================================");
}
}
} else {
break;
}
}
} catch (IOException e) {
System.out.println("客户端异常,请重启!");
}
}
}
3.服务端线程类
public class Server2 extends Thread {
private int port = 8899;
private String ip = "192.168.100.38";
private Boolean startListener = true;
private int threadPoolSize = 10;
@Override
public void run() {
Selector selector = null;
ServerSocketChannel socketChannel = null;
int nKeys = 0;
try {
selector = Selector.open();
socketChannel = ServerSocketChannel.open();
InetSocketAddress inetSocketAddress = new InetSocketAddress(ip, port);
//信道绑定IP、端口
socketChannel.socket().bind(inetSocketAddress);
//设置非阻塞
socketChannel.configureBlocking(false);
//注册选择器
socketChannel.register(selector, SelectionKey.OP_ACCEPT);
//开始监听
System.out.println("开启监听");
while (startListener) {
//设置超时时间,多久返回一次选择器key
nKeys = selector.select(100);
if (nKeys > 0) {
Set selectedKeys = selector.selectedKeys();
Iterator it = selectedKeys.iterator();
while (it.hasNext()) {
SelectionKey key = it.next();
Socket socket = null;
it.remove();
if (key.isAcceptable()) { //处理连接事件
SocketChannel channel = socketChannel.accept();
channel.configureBlocking(false); //设置为非阻塞
System.out.println("client:" + channel.getLocalAddress() + " is connect");
channel.register(selector, SelectionKey.OP_READ); //注册客户端读取事件到selector
} else if (key.isReadable()) { //处理读取事件
ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
SocketChannel channel = (SocketChannel) key.channel();
//查询是否读取到值,客户端断开连接返回-1
int read=channel.read(byteBuffer);
if(read==-1){
continue;
}
//获取请求报文
String requestInfo = new String(byteBuffer.array(),"GBK");
System.out.println("请求报文:"+requestInfo+"--"+requestInfo.length());
try {
if (requestInfo!=null&&!requestInfo.equals("")) {
//响应报文
String newData="响应报文:2023-05-05";
ByteBuffer buf = ByteBuffer.allocate(newData.getBytes().length);
buf.clear();
buf.put(newData.getBytes());
buf.flip();
while (buf.hasRemaining()) {
channel.write(buf);
}
buf.clear();
}
}catch (Exception e){
e.printStackTrace();
}
}
}
}
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (socketChannel != null) {
socketChannel.close();
}
} catch (Exception e) {
e.printStackTrace();
System.out.println("关闭ServerSocketChannel异常" + e.getMessage());
}
}
}
}
4.服务端启动类
public class SocketMain {
public static void main(String[] args) {
Server2 socketServer=new Server2();
Thread thread=new Thread(socketServer);
thread.start();
}
}