您的位置:首页 > Web前端

NIO框架(4)---聊天室

2016-05-17 14:53 246 查看
前几篇文章介绍了NIO的几个主要构件,包括缓冲区Buffer、传输通道Channel、消息选择器Selector以及消息类别SelectionKey,接下来我们通过一个聊天室的例子来了解一下以上各种组件的用法。聊天室分为服务器端和客户端,服务器端接收客户端发来的请求消息,并生成一个Channel来处理客户端的消息;客户端生成Channel来连接服务器,发送消息,然后再注册一个新的Channel来接收服务器端发来的消息。

服务器

1.首先生成一个ServerSocketChannel,绑定地址,然后将它注册到一个打开的选择器上来接收客户端的连接:

Selector selector = Selector.open();
ServerSocketChannel channel;
channel = ServerSocketChannel.open();
channel.bind(new InetSocketAddress("127.0.0.1", 9999));
channel.configureBlocking(false);
channel.register(selector, SelectionKey.OP_ACCEPT);

System.out.println("Server is running!");

while (true) {
int readyChannels = selector.select();
if (readyChannels == 0)
continue;
Set<SelectionKey> selectedKeys = selector.selectedKeys();
Iterator<SelectionKey> keyIterator = selectedKeys.iterator();
while (keyIterator.hasNext()) {
SelectionKey key = keyIterator.next();
keyIterator.remove();
dealConnection(selector, key);
}
}


2.处理客户端发来的消息

/**
* 处理连接消息
* @param selector
* @param key
* @throws IOException
* @throws Exception
*/
public void dealConnection(Selector selector, SelectionKey key)
throws IOException, Exception {
if(key.isAcceptable())
{
//生成新的Channel来接收客户端发来的消息
registerChannel(selector, key);
}

if (key.isConnectable()) {
System.out.println("a connection was established!");
} else if (key.isReadable()) {
readDataFromSocket(key);
} else if (key.isWritable()) {
System.out.println("a channel was writing for data!");
readDataFromSocket(key);
}
}


3.生成新的Channel处理客户端发来的消息

/**
* 服务器端注册channel
* @param selector
* @param key
* @throws IOException
* @throws ClosedChannelException
*/
public void registerChannel(Selector selector, SelectionKey key)
throws IOException, ClosedChannelException {
ServerSocketChannel server = (ServerSocketChannel) key.channel();
SocketChannel sc = server.accept();
if (sc == null) {
return;
}
sc.configureBlocking(false);
sc.register(selector, SelectionKey.OP_READ);
key.interestOps(SelectionKey.OP_ACCEPT);
System.out.println("Server is listening from client :" + sc.getRemoteAddress());
sc.write(charset.encode("请输入姓名"));
}


4.将客户端消息广播到其他客户端

/**
* 广播消息
* @param selector
* @param srcChannel
* @param content
* @throws IOException
*/
public void broadCast(Selector selector, SocketChannel srcChannel, String content) throws IOException {
for(SelectionKey key : selector.keys())
{
Channel targetchannel = key.channel();
if(targetchannel instanceof SocketChannel && targetchannel!=srcChannel)
{
SocketChannel dest = (SocketChannel)targetchannel;
if(dest.isConnected())
dest.write(charset.encode(content));
}
}
}


到此服务器端的功能基本完成

客户端

1.注册Channel连接服务端

sc = SocketChannel.open();
selector = Selector.open();
sc.connect(new InetSocketAddress("127.0.0.1", 9999));
sc.configureBlocking(false);
sc.register(selector, SelectionKey.OP_WRITE | SelectionKey.OP_READ);


2.创建一个Channel来接收服务端发来的消息

new Thread(new Runnable() {
@Override
public void run() {
try
{
while(isRun) {
int readyChannels = selector.select();
if(readyChannels == 0) continue;
Set<SelectionKey> selectedKeys = selector.selectedKeys();
Iterator<SelectionKey> keyIterator = selectedKeys.iterator();
while(keyIterator.hasNext()) {
SelectionKey sk = (SelectionKey) keyIterator.next();
keyIterator.remove();
dealMsg(sk);
}
}
}
catch (IOException io)
{}
}
}).start();


3.处理服务器发来的消息

/**
* 处理服务器发来的消息
* @param sk
* @throws IOException
*/
private void dealMsg(SelectionKey sk) throws IOException {
if(sk.isReadable())
{
String msg = uitl.readDataFromChannel((SocketChannel)sk.channel());
System.out.println(msg);
sk.interestOps(SelectionKey.OP_READ);
}
}


以上就完成了客户端基本功能。

具体代码请点击下载
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息