昨天学习nio实现 上线提醒 功能时候,粗心导致bug,记录一下
功能主要是实现 在一个客户端上线时候,服务器会给其他客户端推送上线信息
/**
* 上线提醒
*/
private void onlineReminder(SelectionKey key) throws IOException {
Iterator<SelectionKey> iterator = selector.keys().iterator();
SelectableChannel c = key.channel();
while (iterator.hasNext()) {
SocketChannel channel = (SocketChannel) iterator.next().channel();//这里把所有遍历出来的key.channel都统一强转为SocketChannel
if (c != channel)
channel.write(ByteBuffer.wrap((c.getRemoteAddress() + "上线了。").getBytes()));
}
}
上面bug主要在于:没有考虑到遍历出来的key是包含ServerSocketChannel
的。强转为 SocketChannel 并进行写入会导致连接中断,出现java.io.IOException: 远程主机强迫关闭了一个现有的连接。
正确的处理办法是:在强转之前加一个检验,判断其是否是SocketChannel
/**
* 上线提醒
*/
private void onlineReminder(SelectionKey key) throws IOException {
SelectableChannel c = key.channel();//当前上线的channel
for (SelectionKey selectionKey : selector.keys()) {
SelectableChannel channel = selectionKey.channel();
if ((c != channel) && (channel instanceof SocketChannel)) {
((SocketChannel) channel).write(ByteBuffer.wrap((((SocketChannel) c).getRemoteAddress() + " 上线了。").getBytes()));
}
}
/*for (SelectionKey key : selector.keys()) {
SelectableChannel channel = key.channel();
if (channel instanceof SocketChannel && channel != self) {
SocketChannel socketChannel = (SocketChannel) channel;
try {
socketChannel.write(ByteBuffer.wrap((self.getRemoteAddress() + " 上线了。").getBytes()));
} catch (IOException e) {
e.printStackTrace();
}
}
}*/
}