<b>Java NIO</b><b>简易聊天室</b>
NIO方式实现简易通讯。代码注释较少,将就看看吧。
哎,越来越懒了。也没什么东西可写的,直接贴贴代码==。不过,有附件工程,觉着这都没什么必要。
<b>一、</b><b>Chat.java</b>
服务器、客户端的基类。继承Observable,作为被观察者。定义些状态啊什么的。
<b></b>
public abstract class Chat extends Observable {
public static final int SEV_ON = 0;
public static final int SEV_OFF = 1;
public static final int CLT_CONNECT = 2;
public static final int CLT_DISCONNECT = 3;
public static final int MSG_SEND = 4;
public static final int MSG_RECEIVE = 5;
public static final int ERROR = 6;
/** 缓存区大小 */
protected static final int BUFFERSIZE = 1024 * 10;
/** 字符编码 */
protected static final String CHARSET = "UTF-8";
/** 字符编码器 */
protected static CharsetEncoder encoder;
/** 字符解码器 */
protected static CharsetDecoder decoder;
static {
encoder = Charset.forName(CHARSET).newEncoder();
decoder = Charset.forName(CHARSET).newDecoder();
}
/** 当前状态 */
protected int status;
/** 获得当前状态 */
public int getStatus() {
return status;
/**
* 通知状态改变
* @param status 状态
* @param arg 参数
*/
protected void notifyStateChanged(int status, Object arg) {
this.status = status;
notifyStateChanged(arg);
protected void notifyStateChanged(Object arg) {
setChanged();
notifyObservers(arg);
}
<b>二、</b><b>ChatServer.java</b>
服务器核心类。
<b></b>
public class ChatServer extends Chat implements Runnable {
private boolean isPrepared = false;
private ServerSocketChannel ssc;
private Selector selector;
private ArrayList<SelectionKey> serverKeyList;
private String receiveMessage;
* 服务器构造函数
*
* @param port 端口
public ChatServer(int port) {
try {
selector = Selector.open();
ssc = ServerSocketChannel.open();
ssc.configureBlocking(false);
ssc.socket().bind(new InetSocketAddress(port));
ssc.register(selector, SelectionKey.OP_ACCEPT);
serverKeyList = new ArrayList<SelectionKey>();
isPrepared = true;
} catch (IOException e) {
notifyStateChanged(ERROR, e);
e.printStackTrace();
}
public void start() {
if (isPrepared)
new Thread(this).start();
/** 针对同一个SelectionKey在一次写操作之前,后attach进去的消息会被覆盖。 */
public void send(String msg, InetSocketAddress... toIps) {
notifyStateChanged(MSG_SEND, msg);
if (null == serverKeyList || serverKeyList.size() <= 0) {
return;
if (null != toIps && toIps.length >= 1) {
/* 发送给部分 */
for (SelectionKey serverKey : serverKeyList) {
SocketChannel sc = (SocketChannel) serverKey.channel();
SocketAddress ip = sc.socket().getRemoteSocketAddress();
for (InetSocketAddress toIp : toIps) {
if (toIp.equals(ip)) {
serverKey.attach(msg);
serverKey.interestOps(SelectionKey.OP_READ
| SelectionKey.OP_WRITE);
serverKey.selector().wakeup();
break;
}
}
}
} else {
/* 发送给全部 */
serverKey.attach(msg);
serverKey.interestOps(SelectionKey.OP_READ
| SelectionKey.OP_WRITE);
serverKey.selector().wakeup();
@Override
public void run() {
notifyStateChanged(SEV_ON, null);
while (isPrepared) {
int keysCount = selector.select();
if (keysCount < 1) {
continue;
Set<SelectionKey> set = selector.selectedKeys();
Iterator<SelectionKey> it = set.iterator();
while (it.hasNext()) {
SelectionKey key = it.next();
if (key.isAcceptable()) {
doAccept(key);
if (key.isValid() && key.isReadable()) {
doRead(key);
if (key.isValid() && key.isWritable()) {
doWrite(key);
set.clear();
} finally {
// close();
notifyStateChanged(SEV_OFF, null);
public void close() {
isPrepared = false;
if (null != serverKeyList) {
for (SelectionKey key : serverKeyList) {
key.channel().close();
if (null != selector) {
selector.close();
if (null != ssc) {
ssc.close();
private void doAccept(SelectionKey key) {
ServerSocketChannel ssc = (ServerSocketChannel) key.channel();
SocketChannel sc = ssc.accept();
sc.configureBlocking(false);
SelectionKey newKey = sc.register(selector, SelectionKey.OP_READ);
// newKey.attach(new ArrayList<String>());
serverKeyList.add(newKey);
notifyStateChanged(CLT_CONNECT, sc.socket()
.getRemoteSocketAddress());
private void doRead(SelectionKey key) {
SocketChannel sc = (SocketChannel) key.channel();
ByteBuffer bb = ByteBuffer.allocate(BUFFERSIZE);
StringBuffer sb = new StringBuffer();
int count = 0;
while ((count = sc.read(bb)) > 0) {
bb.flip();
sb.append(decoder.decode(bb));
if (count == -1) {
disconnect(key, sc);
} else {
receiveMessage = sb.toString().trim();
notifyStateChanged(MSG_RECEIVE, sc.socket()
.getRemoteSocketAddress());
disconnect(key, sc);
// e.printStackTrace();
private void doWrite(SelectionKey key) {
String msg = (String) key.attachment();
if (null == msg) {
key.interestOps(SelectionKey.OP_READ);
sc.write(encoder.encode(CharBuffer.wrap(msg)));
key.interestOps(SelectionKey.OP_READ);
/** 断开连接 */
private void disconnect(SelectionKey key, SocketChannel sc) {
serverKeyList.remove(key);
notifyStateChanged(CLT_DISCONNECT, sc.socket().getRemoteSocketAddress());
key.cancel();
sc.close();
public String getReceiveMessage() {
return receiveMessage;
<b>三、</b><b>ChatClient.java</b>
客户端核心类。
public class ChatClient extends Chat implements Runnable {
private SelectionKey clientKey;
private InetSocketAddress address;
* 客户端构造函数
* @param host 服务器地址
* @param port 服务器端口
public ChatClient(String host, int port) {
address = new InetSocketAddress(host, port);
new Thread(this).start();
public void send(String msg) {
if (null == clientKey) {
clientKey.attach(msg);
clientKey.interestOps(SelectionKey.OP_READ | SelectionKey.OP_WRITE);
clientKey.selector().wakeup();
SocketChannel sc = SocketChannel.open();
sc.connect(address);
clientKey = sc.register(selector, SelectionKey.OP_CONNECT);
if (key.isConnectable()) {
doConnect(key);
notifyStateChanged(CLT_DISCONNECT, null);
if (null != clientKey) {
clientKey.channel().close();
private void doConnect(SelectionKey key) {
// http://www.velocityreviews.com/forums/t145075-whats-the-proper-way-to-use-socketchannel-finishconnect.html
sc.finishConnect();
notifyStateChanged(CLT_CONNECT, null);
disconnect(key);
disconnect(key);
notifyStateChanged(MSG_RECEIVE, sb.toString().trim());
private void disconnect(SelectionKey key) {
notifyStateChanged(CLT_DISCONNECT, null);
key.channel().close();
本文转自winorlose2000 51CTO博客,原文链接:http://blog.51cto.com/vaero/921770,如需转载请自行联系原作者