Java NIO非阻塞服务器示例

本文介绍了一个使用Java NIO非阻塞模式实现的服务器端程序。该程序通过Selector处理多个客户端连接,实现了高效的数据读写操作。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

以前一直用的是“ervery thread per connection”的服务器端模式,今天试了下NIO非阻塞模式的服务器。 不过java不能实现I/O完成端口模型,这点很遗憾
packagecom.vista.Server;

importjava.io.IOException;
importjava.net.InetSocketAddress;
importjava.net.ServerSocket;
importjava.nio.ByteBuffer;
importjava.nio.channels.SelectionKey;
importjava.nio.channels.Selector;
importjava.nio.channels.ServerSocketChannel;
importjava.nio.channels.SocketChannel;
importjava.util.Iterator;
importjava.util.LinkedList;
importjava.util.Set;

publicclassSelectorServer
{
privatestaticintDEFAULT_SERVERPORT=6018;//默认端口
privatestaticintDEFAULT_BUFFERSIZE=1024;//默认缓冲区大小为1024字节
privateServerSocketChannelchannel;
privateLinkedList<SocketChannel>clients;
privateSelectorreadSelector;
privateByteBufferbuffer;//字节缓冲区
privateintport;

publicSelectorServer(intport)throwsIOException
{
this.port=port;
this.clients=newLinkedList<SocketChannel>();
this.channel=null;
this.readSelector=Selector.open();//打开选择器
this.buffer=ByteBuffer.allocate(DEFAULT_BUFFERSIZE);
}

//服务器程序在服务循环中调用sericeClients()方法为已接受的客户服务
publicvoidserviceClients()throwsIOException
{
Setkeys;
Iteratorit;
SelectionKeykey;
SocketChannelclient;
//在readSelector上调用select()方法,参数1代表如果调用select的时候那么阻塞最多1秒钟等待可用的客户端连接
if(readSelector.select(1)>0)
{
keys
=readSelector.selectedKeys();//取得代表端通道的键集合
it=keys.iterator();
//遍历,为每一个客户服务
while(it.hasNext())
{
key
=(SelectionKey)it.next();
if(key.isReadable())
{//如果通道可读,那么读此通道到buffer中
intbytes;
client
=(SocketChannel)key.channel();//取得键对应的通道
buffer.clear();//清空缓冲区中的内容,设置好position,limit,准备接受数据
bytes=client.read(buffer);//从通道中读数据到缓冲中,返回读取得字节数
if(bytes>=0)
{
buffer.flip();
//准备将缓冲中的数据写回到通道中
client.write(buffer);//数据写回到通道中
}

elseif(bytes<0)
{//如果返回小于零的值代表读到了流的末尾
clients.remove(client);
//通道关闭时,选择键也被取消
client.close();
}

}

}

}

}


publicvoidregisterClient(SocketChannelclient)throwsIOException
{//配置和注册代表客户连接的通道对象
client.configureBlocking(false);//设置此通道使用非阻塞模式
client.register(readSelector,SelectionKey.OP_READ);//将这个通道注册到选择器上
clients.add(client);//保存这个通道对象
}

publicvoidlisten()throwsIOException
{//服务器开始监听端口,提供服务
ServerSocketsocket;
SocketChannelclient;
channel
=ServerSocketChannel.open();//打开通道
socket=channel.socket();//得到与通到相关的socket对象
socket.bind(newInetSocketAddress(port),10);//将scoket榜定在制定的端口上
//配置通到使用非阻塞模式,在非阻塞模式下,可以编写多道程序同时避免使用复杂的多线程
channel.configureBlocking(false);
try
{
while(true)
{//与通常的程序不同,这里使用channel.accpet()接受客户端连接请求,而不是在socket对象上调用accept(),这里在调用accept()方法时如果通道配置为非阻塞模式,那么accept()方法立即返回null,并不阻塞
client=channel.accept();
if(client!=null)
{
registerClient(client);
//注册客户信息
}

serviceClients();
//为以连接的客户服务
}

}

finally
{
socket.close();
//关闭socket,关闭socket会同时关闭与此socket关联的通道
}

}

publicstaticvoidmain(String[]args)throwsIOException
{
System.out.println(
"服务器启动");
SelectorServerserver
=newSelectorServer(SelectorServer.DEFAULT_SERVERPORT);
server.listen();
//服务器开始监听端口,提供服务


}


}

修改版本:

<!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />-->packagecom.vista.Server;

importjava.io.BufferedWriter;
importjava.io.FileInputStream;
importjava.io.IOException;
importjava.io.OutputStreamWriter;
importjava.io.PrintWriter;
importjava.net.InetSocketAddress;
importjava.net.ServerSocket;
importjava.nio.ByteBuffer;
importjava.nio.CharBuffer;
importjava.nio.channels.FileChannel;
importjava.nio.channels.SelectionKey;
importjava.nio.channels.Selector;
importjava.nio.channels.ServerSocketChannel;
importjava.nio.channels.SocketChannel;
importjava.nio.charset.Charset;
importjava.nio.charset.CharsetDecoder;
importjava.util.Iterator;
importjava.util.LinkedList;
importjava.util.Set;

publicclassSelectorServer
ExpandedBlockStart.gifContractedBlock.gif
{
privatestaticintDEFAULT_SERVERPORT=6018;//默认端口
privatestaticintDEFAULT_BUFFERSIZE=1024;//默认缓冲区大小为1024字节
privatestaticStringDEFAULT_CHARSET="GB2312";//默认码集
privatestaticStringDEFAULT_FILENAME="bigfile.dat";
privateServerSocketChannelchannel;
privateLinkedList<SocketChannel>clients;
privateSelectorselector;//选择器
privateByteBufferbuffer;//字节缓冲区
privateintport;
privateCharsetcharset;//字符集
privateCharsetDecoderdecoder;//解码器


publicSelectorServer(intport)throwsIOException
ExpandedSubBlockStart.gifContractedSubBlock.gif
{
this.port=port;
this.clients=newLinkedList<SocketChannel>();
this.channel=null;
this.selector=Selector.open();//打开选择器
this.buffer=ByteBuffer.allocate(DEFAULT_BUFFERSIZE);
this.charset=Charset.forName(DEFAULT_CHARSET);
this.decoder=this.charset.newDecoder();

}


privateclassHandleClient
ExpandedSubBlockStart.gifContractedSubBlock.gif
{
privateStringstrGreeting="welcometoVistaQQ";
publicHandleClient()throwsIOException
ExpandedSubBlockStart.gifContractedSubBlock.gif
{
}

publicStringreadBlock()
ExpandedSubBlockStart.gifContractedSubBlock.gif
{//读块数据
returnthis.strGreeting;
}

publicvoidclose()
ExpandedSubBlockStart.gifContractedSubBlock.gif
{

}

}


protectedvoidhandleKey(SelectionKeykey)throwsIOException
ExpandedSubBlockStart.gifContractedSubBlock.gif
{//处理事件
if(key.isAcceptable())
ExpandedSubBlockStart.gifContractedSubBlock.gif
{//接收请求
ServerSocketChannelserver=(ServerSocketChannel)key.channel();//取出对应的服务器通道
SocketChannelchannel=server.accept();
channel.configureBlocking(
false);
channel.register(selector,SelectionKey.OP_READ);
//客户socket通道注册读操作
}

elseif(key.isReadable())
ExpandedSubBlockStart.gifContractedSubBlock.gif
{//读信息
SocketChannelchannel=(SocketChannel)key.channel();
intcount=channel.read(this.buffer);
if(count>0)
ExpandedSubBlockStart.gifContractedSubBlock.gif
{
this.buffer.flip();
CharBuffercharBuffer
=decoder.decode(this.buffer);
System.
out.println("Client>>"+charBuffer.toString());
SelectionKeywKey
=channel.register(selector,
SelectionKey.OP_WRITE);
//为客户sockt通道注册写操作
wKey.attach(newHandleClient());
}

else
ExpandedSubBlockStart.gifContractedSubBlock.gif
{//客户已经断开
channel.close();
}

this.buffer.clear();//清空缓冲区
}

elseif(key.isWritable())
ExpandedSubBlockStart.gifContractedSubBlock.gif
{//写事件
SocketChannelchannel=(SocketChannel)key.channel();
HandleClienthandle
=(HandleClient)key.attachment();//取出处理者
ByteBufferblock=ByteBuffer.wrap(handle.readBlock().getBytes());
channel.write(block);
//channel.socket().getInputStream().(block);
//PrintWriterout=newPrintWriter(newBufferedWriter(newOutputStreamWriter(
//channel.socket().getOutputStream())),true);
//out.write(block.toString());

}


}

publicvoidlisten()throwsIOException
ExpandedSubBlockStart.gifContractedSubBlock.gif
{//服务器开始监听端口,提供服务
ServerSocketsocket;
channel
=ServerSocketChannel.open();//打开通道
socket=channel.socket();//得到与通到相关的socket对象
socket.bind(newInetSocketAddress(port));//将scoket榜定在制定的端口上
//配置通到使用非阻塞模式,在非阻塞模式下,可以编写多道程序同时避免使用复杂的多线程
channel.configureBlocking(false);
channel.register(selector,SelectionKey.OP_ACCEPT);
try
ExpandedSubBlockStart.gifContractedSubBlock.gif
{
while(true)
ExpandedSubBlockStart.gifContractedSubBlock.gif
{//与通常的程序不同,这里使用channel.accpet()接受客户端连接请求,而不是在socket对象上调用accept(),这里在调用accept()方法时如果通道配置为非阻塞模式,那么accept()方法立即返回null,并不阻塞
this.selector.select();
Iteratoriter
=this.selector.selectedKeys().iterator();
while(iter.hasNext())
ExpandedSubBlockStart.gifContractedSubBlock.gif
{
SelectionKeykey
=(SelectionKey)iter.next();
iter.remove();
this.handleKey(key);

}

}

}

catch(IOExceptionex)
ExpandedSubBlockStart.gifContractedSubBlock.gif
{
ex.printStackTrace();
}

}

publicstaticvoidmain(String[]args)throwsIOException
ExpandedSubBlockStart.gifContractedSubBlock.gif
{
System.
out.println("服务器启动");
SelectorServerserver
=newSelectorServer(SelectorServer.DEFAULT_SERVERPORT);
server.listen();
//服务器开始监听端口,提供服务
}


}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值