package main.java;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.nio.charset.Charset;
import java.util.Date;
import java.util.Iterator;
import java.util.Set;
/**
* @Description: NIO服务器端
* @author: littl
* @date: 2020/8/25
* @Copyright:
*/
public class NioServer {
public void start() throws Exception {
/**
* 1. 创建一个selector
*/
Selector selector = Selector.open();
/**
* 2. 创建一个channel 通道,通过serverSocketChannel
*/
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
/**
* 3. 为channel通道绑定一个监听端口
*/
serverSocketChannel.bind(new InetSocketAddress(8000));
/**
* 4. 设置channel为非阻塞模式
*/
serverSocketChannel.configureBlocking(false);
/**
* 5.将channel注册到selector上,监听连接事件
*/
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
System.out.println("服务器连接成功!");
/**
* 6. 循环等待新接入的连接
*/
while (true){
int readyChannels = selector.select(); // 可用的channel个数
System.out.println(new Date()+"--服务器端可用的channel个数--"+readyChannels);
/**
* 没有就绪的channel,直接返回
*/
if(readyChannels == 0){
continue;
}
/**
* 处理已就绪的channel
*/
Set selectionKeys = selector.selectedKeys();
Iterator iterator = selectionKeys.iterator();
while (iterator.hasNext()){
SelectionKey selectionKey = iterator.next();
/**
* 移除当前已经处理的selectionKey
*/
iterator.remove();
/**
* 7. 根据就绪状态,调用对应方法处理业务逻辑
* 根据不同的事件类型做不同的处理
*/
if(selectionKey.isAcceptable()){
// 接入事件触发
acceptHandler(serverSocketChannel,selector);
}
if(selectionKey.isReadable()){
// 可读事件
readHandler(selectionKey,selector);
}
}
}
}
/**
* 接入事件处理器
*/
private void acceptHandler(ServerSocketChannel channel,
Selector selector) throws Exception{
// 1. 创建socketChannel连接
SocketChannel socketChannel = channel.accept();
// 2. 将socketChannel设置为非阻塞模式
socketChannel.configureBlocking(false);
// 3. 将channel注册到selector上,监听可读 事件
socketChannel.register(selector,SelectionKey.OP_READ);
// 4. 回复客户端
socketChannel.write(Charset.forName("UTF-8").encode("你与聊天室里其他人都不是朋友关系,请注意隐私安全!!"));
}
/**
* 可读事件处理器
*/
private void readHandler(SelectionKey key,Selector selector) throws Exception{
// 1. 从selectionkey中获取到已经就绪的channel
SocketChannel socketChannel = (SocketChannel) key.channel();
// 2. 创建buffer(只有buffer才能操作channel)
ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
// 3. 循环读取客户端请求信息
String request = "";
while (socketChannel.read(byteBuffer) > 0){
// 切换buffer为读模式
byteBuffer.flip();
// 读取buffer中的内容
request += Charset.forName("UTF-8").decode(byteBuffer);
}
// 4. 将channel再次注册到selector上,监听他的可读事件
socketChannel.register(selector,SelectionKey.OP_READ);
// 5. 打印客户端发送的请求信息
if(request.length() > 0){
System.out.println("request:"+request);
// 广播到其他客户端
broadcast(selector,socketChannel,request);
}
}
/**
*
* @param selector
* @param sourceChannel 发送消息的channel
* @param request 发送的消息
*/
private void broadcast(Selector selector,SocketChannel sourceChannel,String request){
// 1. 获取所有的channel
Set channels = selector.keys();
// 2. 遍历,剔除源channel
channels.forEach(selectionKey -> {
Channel channel = selectionKey.channel();
if(channel instanceof SocketChannel && channel != sourceChannel){
try {
((SocketChannel) channel).write(Charset.forName("UTF-8").encode(request));
} catch (IOException e) {
e.printStackTrace();
}
}
});
}
public static void main(String[] args) throws Exception {
NioServer server = new NioServer();
server.start();
}
}
package main.java;
import java.net.InetSocketAddress;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.nio.charset.Charset;
import java.util.Scanner;
/**
* @Description:
* @author: littl
* @date: 2020/8/25
* @Copyright:
*/
public class NioClient {
/**
* 启动客户端
* @throws Exception
*/
public void start(String nickName)throws Exception{
// 1. 连接服务器
SocketChannel socketChannel = SocketChannel.open(new InetSocketAddress("127.0.0.1", 8000));
// 2. 接收服务端响应
// 新开启一个线程,接收服务端的响应
Selector selector = Selector.open();
socketChannel.configureBlocking(false);
socketChannel.register(selector, SelectionKey.OP_READ);
new Thread(new NioClientThread(selector)).start();
// 3. 向服务端发送数据(从键盘读取数据)
Scanner scanner = new Scanner(System.in);
while (scanner.hasNextLine()){
String request = scanner.nextLine();
if(request != null && request.length() > 0){
socketChannel.write(Charset.forName("utf-8").encode(nickName +":"+ request));
}
}
}
public static void main(String[] args) throws Exception {
}
}
package main.java;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.nio.charset.Charset;
import java.util.Date;
import java.util.Iterator;
import java.util.Set;
/**
* @Description: 客户端接收服务端响应信息的线程
* @author: littl
* @date: 2020/8/26
* @Copyright:
*/
public class NioClientThread implements Runnable{
private Selector selector;
public NioClientThread(Selector selector) {
this.selector = selector;
}
@Override
public void run() {
System.out.println("线程启动....");
try {
/**
* 6. 循环等待新接入的连接
*/
while (true){
int readyChannels = selector.select(); // 可用的channel个数(就绪的channel个数)
System.out.println(new Date()+"--客户端可用的channel个数--"+readyChannels);
/**
* 没有就绪的channel,直接返回
*/
if(readyChannels == 0){
continue;
}
/**
* 处理已就绪的channel
*/
Set selectionKeys = selector.selectedKeys();
Iterator iterator = selectionKeys.iterator();
while (iterator.hasNext()){
SelectionKey selectionKey = iterator.next();
/**
* 移除当前已经处理的selectionKey
*/
iterator.remove();
/**
* 7. 根据就绪状态,调用对应方法处理业务逻辑
* 读取服务端返回的响应信息
*/
if(selectionKey.isReadable()){
// 可读事件
readHandler(selectionKey,selector);
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 可读事件处理器
*/
private void readHandler(SelectionKey key, Selector selector) throws Exception{
// 1. 从selectionkey中获取到已经就绪的channel
SocketChannel socketChannel = (SocketChannel) key.channel();
// 2. 创建buffer(只有buffer才能操作channel)
ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
// 3. 循环读取客户端请求信息
String response = "";
while (socketChannel.read(byteBuffer) > 0){
// 切换buffer为读模式
byteBuffer.flip();
// 读取buffer中的内容
response += Charset.forName("UTF-8").decode(byteBuffer);
}
// 4. 将channel再次注册到selector上,监听他的可读事件
socketChannel.register(selector,SelectionKey.OP_READ);
// 5. 打印服务器返回的响应信息
if(response.length() > 0){
System.out.println("response:"+response);
}
}
}
package main.java;
/**
* @Description:
* @author: littl
* @date: 2020/8/26
* @Copyright:
*/
public class AClient {
public static void main(String[] args) throws Exception {
new NioClient().start("小白");
}
}
package main.java;
/**
* @Description:
* @author: littl
* @date: 2020/8/26
* @Copyright:
*/
public class BClient {
public static void main(String[] args) throws Exception {
new NioClient().start("小黑");
}
}
package main.java;
/**
* @Description:
* @author: littl
* @date: 2020/8/26
* @Copyright:
*/
public class CClient {
public static void main(String[] args) throws Exception {
new NioClient().start("小绿");
}
}
socketChannel 使用后要再次注册,以便下次监听触发事件。