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.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/*
* 主线程selector只处理新连接请求
* 子线程selector处理所有channel的读写事件
*
*
* 1.如何在main thread中给子线程添加channel
* 2.如何处理client的正常关闭异常关闭
*
*
* 关闭服务器与客户端的通信链路资源
* channel.close(); 把链路关闭掉
* key.cancel(); 把channel从selector中删除掉
*
* 在得不到key的情况下
* channel.keyFor(selector).cancel();
* */
class WorkTask extends Thread{
private Selector selector;
private ByteBuffer buffer;
private List<SocketChannel> list;
public WorkTask() throws IOException {
super();
selector = Selector.open();
buffer = ByteBuffer.allocate(1024);
list = Collections.synchronizedList(new ArrayList<SocketChannel>());
}
public Selector getSelector(){
return selector;
}
public List<SocketChannel> getList(){
return list;
}
public void run(){
try{
while (!Thread.currentThread().isInterrupted()){
int num = selector.select();
if (num <= 0){
Iterator<SocketChannel> it = list.iterator();
while (it.hasNext()){
SocketChannel cChannel = it.next();
//selector正在阻塞或者在下一轮阻塞被唤醒后
//将链表中的SocketChannel注册到Selector当中,并删除
cChannel.register(selector, SelectionKey.OP_READ);
it.remove();
}
continue;
}
Iterator<SelectionKey> it = selector.selectedKeys().iterator();
while (it.hasNext()){
SelectionKey key = it.next();
it.remove();
if (key.isValid() && key.isReadable()){
SocketChannel cChannel = (SocketChannel) key.channel();
try{
int readcnt = cChannel.read(buffer);
//处理客户端正常关闭
if (readcnt <= 0){
cChannel.close();
key.cancel();
System.out.println("client normal close!");
continue;
}
System.out.println(new String(buffer.array()).trim());
cChannel.write(ByteBuffer.wrap(new String("server recv your msg!").getBytes()));
cChannel.write(ByteBuffer.wrap(new String("\n").getBytes()));//加\n将缓存主动刷新
buffer.flip();
buffer.clear();
}catch (IOException e){
//处理客户端异常关闭
cChannel.close();
key.cancel();
System.out.println("client close with Exception!");
}
}
}
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
public class NioServer {
private Selector selector;
private ServerSocketChannel sschannel;
private WorkTask worktask;
private static ExecutorService threadpool;
static{
threadpool = Executors.newFixedThreadPool(1);
}
public NioServer() throws IOException {
super();
//创建selector对象
selector = Selector.open();
//创建server的channel
sschannel = ServerSocketChannel.open();
sschannel.bind(new InetSocketAddress("127.0.0.1", 6000));
sschannel.configureBlocking(false);
//注册server channel的accept事件
sschannel.register(selector, SelectionKey.OP_ACCEPT);
//创建处理读写事件的子线程
worktask = new WorkTask();
threadpool.submit(worktask);
}
public void startServer() throws IOException{
System.out.println("server start...");
while (!Thread.currentThread().isInterrupted()){
int num = selector.select();
if (num <= 0){
continue;
}
//key(channel()) -> channel(socket()) -> socket
Iterator<SelectionKey> it = selector.selectedKeys().iterator();
while (it.hasNext()){
SelectionKey key = it.next();
//先删除事件集合里面的key
it.remove();
if (key.isValid() && key.isAcceptable()){
SocketChannel cChannel = sschannel.accept();
cChannel.configureBlocking(false);
//先将SocketChannel添加到线程安全的ArrayList当中,再将工作线程中的Selector唤醒
worktask.getList().add(cChannel);
worktask.getSelector().wakeup();
}
}
}
}
public static void main(String[] args) throws IOException {
// TODO Auto-generated method stub
NioServer server = new NioServer();
server.startServer();
}
}
13.NIO中selector编写服务器练习
最新推荐文章于 2022-10-17 15:23:49 发布