package com.gengu;
import java.io.IOException;
import java.net.InetSocketAddress;
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.nio.channels.spi.SelectorProvider;
import java.util.Iterator;
/**
* TCP/IP的NIO非阻塞方式 服务器端
* */
public class Server implements Runnable {
// 第一个端口
private Integer port1 = 8099;
// 第二个端口
private Integer port2 = 9099;
// 第一个服务器通道 服务A
private ServerSocketChannel serversocket1;
// 第二个服务器通道 服务B
private ServerSocketChannel serversocket2;
// 连接1
private SocketChannel clientchannel1;
// 连接2
private SocketChannel clientchannel2;
// 缓冲区
private ByteBuffer buf = ByteBuffer.allocate(512);
public Server() {
init();
}
// 选择器,主要用来监控各个通道的事件
private Selector selector;
/**
* 这个method的作用1:是初始化选择器 2:打开两个通道 3:给通道上绑定一个socket 4:将选择器注册到通道上
* */
public void init() {
try {
// 创建选择器
this.selector = SelectorProvider.provider().openSelector();
// 打开第一个服务器通道
this.serversocket1 = ServerSocketChannel.open();
// 告诉程序现在不是阻塞方式的
this.serversocket1.configureBlocking(false);
// 获取现在与该通道关联的套接字
this.serversocket1.socket().bind(
new InetSocketAddress("localhost", this.port1));
// 将选择器注册到通道上,返回一个选择键
// OP_ACCEPT用于套接字接受操作的操作集位
this.serversocket1.register(this.selector, SelectionKey.OP_ACCEPT);
// 然后初始化第二个服务端
this.serversocket2 = ServerSocketChannel.open();
this.serversocket2.configureBlocking(false);
this.serversocket2.socket().bind(
new InetSocketAddress("localhost", this.port2));
this.serversocket2.register(this.selector, SelectionKey.OP_ACCEPT);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 这个方法是连接 客户端连接服务器
*
* @throws IOException
* */
public void accept(SelectionKey key) throws IOException {
ServerSocketChannel server = (ServerSocketChannel) key.channel();
if (server.equals(serversocket1)) {
clientchannel1 = server.accept();
clientchannel1.configureBlocking(false);
// OP_READ用于读取操作的操作集位
clientchannel1.register(this.selector, SelectionKey.OP_READ);
} else {
clientchannel2 = server.accept();
clientchannel2.configureBlocking(false);
// OP_READ用于读取操作的操作集位
clientchannel2.register(this.selector, SelectionKey.OP_READ);
}
}
/**
* 从通道中读取数据 并且判断是给那个服务通道的
*
* @throws IOException
* */
public void read(SelectionKey key) throws IOException {
this.buf.clear();
// 通过选择键来找到之前注册的通道
// 但是这里注册的是ServerSocketChannel为什么会返回一个SocketChannel??
SocketChannel channel = (SocketChannel) key.channel();
// 从通道里面读取数据到缓冲区并返回读取字节数
int count = channel.read(this.buf);
if (count == -1) {
// 取消这个通道的注册
key.channel().close();
key.cancel();
return;
}
// 将数据从缓冲区中拿出来
String input = new String(this.buf.array()).trim();
// 那么现在判断是连接的那种服务
if (channel.equals(this.clientchannel1)) {
System.out.println("欢迎您使用服务A");
System.out.println("您的输入为:" + input);
} else {
System.out.println("欢迎您使用服务B");
System.out.println("您的输入为:" + input);
}
}
@Override
public void run() {
while (true) {
try {
// 选择一组键,其相应的通道已为 I/O 操作准备就绪。
this.selector.select();
// 返回此选择器的已选择键集
// public abstract Set<SelectionKey> selectedKeys()
Iterator selectorKeys = this.selector.selectedKeys().iterator();
while (selectorKeys.hasNext()) {
// 这里找到当前的选择键
SelectionKey key = (SelectionKey) selectorKeys.next();
// 然后将它从返回键队列中删除
selectorKeys.remove();
if (!key.isValid()) {
continue;
}
if (key.isAcceptable()) {
// 如果遇到请求那么就响应
this.accept(key);
} else if (key.isReadable()) {
// 读取客户端的数据
this.read(key);
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
Server server = new Server();
Thread thread = new Thread(server);
thread.start();
}
}