//server
public class NBChatServer {
private Selector sel;
private ServerSocketChannel server;
private ByteBuffer buf = ByteBuffer.allocate(1024);
// 保存 <name:channel> 的键值对,用于某一用户向另一用户发信息时,找到目标用户的channel
private Hashtable<String, SocketChannel> sockets = new Hashtable<String, SocketChannel>();
// 保存 <key:name> 的键值对, 用于记录某信息是哪个用户发出的。
private Hashtable<SelectionKey, String> clients = new Hashtable<SelectionKey, String>();
public static boolean active = true;
public static final boolean NON_BLOCKING = false;
public static final String key_ip = "server.ip";
public static final String key_port = "server.port";
public static final String LOGIN_NO = "/login.no";
public static final String LOGIN_OK = "/login.ok";
private static Properties props = new Properties();
private static Pattern p = Pattern.compile("^\\>(.*?):(.*)$");
NBChatServer(String name) {
initConfig(name);
initServer();
startServer();
}
private static void initConfig(String fName) {
try {
InputStream in = NBChatServer.class.getClassLoader().getResourceAsStream(fName);
props.load(in);
} catch (IOException e) {
e.printStackTrace();
System.exit(-1);
}
}
private void initServer() {
String portStr = props.getProperty(key_port);
int port = Integer.parseInt(portStr);
try {
sel = Selector.open();
server = ServerSocketChannel.open();
server.configureBlocking(NON_BLOCKING);
InetAddress ip = InetAddress.getLocalHost();
InetSocketAddress sIp = new InetSocketAddress(ip, port);
server.socket().bind(sIp);
server.register(sel, SelectionKey.OP_ACCEPT);
} catch (IOException e) {
e.printStackTrace();
System.exit(-1);
}
}
private void startServer() {
int readyCount = 0;
while (active) {
try {
readyCount = sel.select();
} catch (IOException e) {
if (sel.isOpen())
continue;
else
e.printStackTrace();
}
if (readyCount == 0)
continue;
Set readyKeys = sel.selectedKeys();
Iterator keys = readyKeys.iterator();
while (keys.hasNext()) {
SelectionKey key = (SelectionKey) keys.next();
if (!key.isValid())
continue;
keys.remove();
// Acceptable: Tests whether this key's channel is ready to
// accept a new socket connection.
//
// Connectable:Tests whether this key's channel has either
// finished, or failed to finish, its socket-connection
// operation.
//
// Readable: Tests whether this key's channel is ready for
// reading.
//
// Writeable: Tests whether this key's channel is ready for
// writing.
try {
if (key.isAcceptable()) {
ServerSocketChannel ssc = (ServerSocketChannel) key.channel();
SocketChannel socket = (SocketChannel) ssc.accept();
socket.configureBlocking(NON_BLOCKING);
// socket 默认就有向缓冲区写数据的权限,
// 如果为socket向selector注册OP_WRITE模式, 则selector将总能检测到可写操作,
// 于是select将总是立即返回, 导至CPU100%占用。
// 这是该模式的一个bug.
socket.register(sel, SelectionKey.OP_READ);
}
if (key.isReadable()) {
SocketChannel srcSocket = (SocketChannel) key.channel();
buf.clear();
int nBytes = srcSocket.read(buf);
// 当客户端关闭的时候,会向server端发最后一个空的信息,这时nBytes==-1;
if (nBytes == -1) {
teardownConn(key);
continue;
}
String input = ChatUtil.decode(buf);
String name = "all", msg = "", fromWho = null;
// 如果是login信息。则信息直接发给源socket。
if (input.startsWith("/login")) {
// login
String[] acct = input.substring(7).split("/");
name = acct[0];
String pwd = acct[1];
if (name.equals(pwd)) {
storeClient(name, srcSocket, key);
fromWho = this.getClientName(key);
msg = LOGIN_OK;
} else
msg = LOGIN_NO;
System.out.println(">>>" + msg);
srcSocket.write(ByteBuffer.wrap(msg.getBytes()));
}
// 如果是正常的聊天信息。则要从信息中解析出,信息要发给谁。
else {
// 解析信息开始
Matcher m = p.matcher(input);
if (m.find()) {
name = m.group(1);
msg = m.group(2);
} else {
name = ChatServer.ALL;
msg = input;
}
fromWho = this.getClientName(key);
if (fromWho != null)
msg = (">" + fromWho + " say:\n\t" + msg);
// 解析信息结束
System.out.println(msg);
ByteBuffer msgBuf = ByteBuffer.wrap(msg.getBytes());
if ("all".equals(name)) {
Iterator itr = sockets.keySet().iterator();
while (itr.hasNext()) {
name = (String) itr.next();
SocketChannel channel = this.getClient(name);
channel.write(msgBuf.duplicate());
}
} else {
SocketChannel dstSocket = this.getClient(name);
if (dstSocket == null)
dstSocket = srcSocket;
dstSocket.write(msgBuf);
}
}
// key.selector().wakeup();
}
} catch (IOException e) {
teardownConn(key);
} catch (Exception e) {
e.printStackTrace();
System.exit(-1);
}
}// while (keys.hasNext())
} // while (active)
}
private void teardownConn(SelectionKey key) {
String name = getClientName(key);
if (name != null) {
this.sockets.remove(name);
this.clients.remove(key);
}
key.cancel();
try {
key.channel().close();
} catch (IOException e1) {
e1.printStackTrace();
}
System.out.println("\n$Warn:" + name + " disconnect!");
}
private void storeClient(String name, SocketChannel socket, SelectionKey key) {
sockets.put(name, socket);
clients.put(key, name);
}
private SocketChannel getClient(String key) {
return sockets.get(key);
}
private String getClientName(SelectionKey key) {
return clients.get(key);
}
public static void stopSever() {
active = false;
}
public static void main(String[] args) {
new NBChatServer(args[0]);
}
}
分享到:
相关推荐
聊天室源码下载:java-web-html5-php
java完整聊天室系统(源码) java完整聊天室系统(源码)
Java课程设计Java Socket实现聊天室源码内含项目说明.zipJava课程设计Java Socket实现聊天室源码内含项目说明.zipJava课程设计Java Socket实现聊天室源码内含项目说明.zipJava课程设计Java Socket实现聊天室源码内含...
附SQLServer数据库的Java聊天室课程设计源码和文档,源代码完整,学习网络编程很不错的参考程序。界面像网页版 的聊天室,可对所有人发消息,聊天室的功能都基本具备了。源代码文件包括了:聊天系统登录程序、聊天...
Java聊天室程序源码 2 需求分析 2.1 业务需求 1. 与聊天室成员一起聊天。 2. 可以与聊天室成员私聊。 3. 可以改变聊天内容风格。 4. 用户注册(含头像)、登录。 5. 服务器监控聊天内容。 6. 服务器过滤非法内容。 7...
java聊天室源码 socket java
基于Java的P2P多用户在线聊天室项目源码
实现JAVA 局域网 聊天室 源码 聊天
Java聊天室源码Java聊天室源码
多人聊天室源码.rar
java版的聊天室软件 功能:包括群聊,私聊 ,系统消息发布 里面有源码和打包的jar文件
java 聊天室源码java 聊天室源码java 聊天室源码java 聊天室源码
java多线程聊天室可以支持多人在线聊天 实现局域网聊天
java多线程聊天室源代码 分享一下源代码,Java小白可以参考参考
JAVA版本WebSocket聊天室源码工程,亲测真实有效,解压后直接导入eclipse即可,jdk1.8+tomcat8.5,启动工程后访问http://localhost:8080/Chatroom/home/list.do即可。本源码无需数据库,聊天室定义直接在代码里,...
基于ssh的JAVA实现即时通讯的聊天时系统源码,可WEB操作
java web 聊天室 myeclipse web项目
Java WebSocket匿名聊天室源码项目,运行于JSP环境下的WEB项目,核心代码由JAVA编写,编译成Bean运行于JSP的服务器中,支持匿名聊天,作为Jsp编程资料觉得挺不错,将源代码分享给大家吧。
Java课设-简单聊天室的实现源码.zipJava课设-简单聊天室的实现源码.zipJava课设-简单聊天室的实现源码.zipJava课设-简单聊天室的实现源码.zipJava课设-简单聊天室的实现源码.zipJava课设-简单聊天室的实现源码.zip...
Java课程设计全套源码:聊天室源码 SQL2000数据库,主程序分为服务端和客户端,界面友好,可实现的功能模块:用户注册、资料保存、多用户登录、多用户同聊、私聊、保存用户记录、用户离开记录自动清除、在线人数监视...