NIO实现的 DD聊天室
下载地址https://download.youkuaiyun.com/download/qq_31408331/11020169(不知道为啥默认需要用积分)
https://download.youkuaiyun.com/download/qq_31408331/11020167
---------------------------------------------------------------------------------------------------------------------
package org.lxs.nio;
/**
* DD数据传输协议
* example:
* out~username~password
* @author lxs
*
*/
public enum DDProtocol {
//授权协议
AUTH {
@Override
public String message(String ... str) {
StringBuffer retV = new StringBuffer("AUTH");
for(int i=0; i<str.length; i++) {
retV.append(proSeparator+str[i]);
}
return retV.toString();
}
},
//
OUT {
@Override
public String message(String... str) {
StringBuffer retV = new StringBuffer("OUT");
for(int i=0; i<str.length; i++) {
retV.append(proSeparator+str[i]);
}
return retV.toString();
}
},
//广播协议
BROADCAST {
@Override
public String message(String ... str) {
StringBuffer retV = new StringBuffer("BROADCAST");
for(int i=0; i<str.length; i++) {
retV.append(proSeparator+str[i]);
}
return retV.toString();
}
},
//定点传输协议
FIXEDPOINT {
@Override
public String message(String ... str) {
StringBuffer retV = new StringBuffer("FIXEDPOINT");
for(int i=0; i<str.length; i++) {
retV.append(proSeparator+str[i]);
}
return retV.toString();
}
},
//数据同步
DATASYNC {
@Override
public String message(String ... str) {
StringBuffer retV = new StringBuffer("DATASYNC");
for(int i=0; i<str.length; i++) {
retV.append(proSeparator+str[i]);
}
return retV.toString();
}
};
public static final String proSeparator = "`";
public abstract String message(String ... str);
}
-----------------------------------------------------------------------------------------------------------------------------
package org.lxs.nio;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.text.DateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Scanner;
import java.util.function.Consumer;
import java.util.logging.Logger;
/**
* 服务端
* @author lxs
*
*/
public class DDServer {
private static final Logger log = Logger.getGlobal();
private int port;
private ServerSocketChannel ssc;
//private SocketChannel sc;
private Selector selector;
private volatile List<Map<String,Object>> hosts;//登入用户记录
private List<String> whiteList;//白名单
private List<String> blackList;//黑名单
private static final DateFormat df = DateFormat.getDateTimeInstance();
public DDServer(int port) {
this.port = port;
this.hosts = new ArrayList<Map<String,Object>>();
}
public DDServer start() {
try {
ssc = ServerSocketChannel.open();
ssc.configureBlocking(false);
ssc.bind(new InetSocketAddress(port));
selector = Selector.open();
ssc.register(selector, SelectionKey.OP_ACCEPT);
ssc.accept();
log.info("服务端启动完毕!端口号为:"+port);
} catch (IOException e) {
//e.printStackTrace();
}
return this;
}
public void listen() {
try {
log.info("服务端开启监听连接!");
while(true) {
selector.select();
Iterator<SelectionKey> iter = selector.selectedKeys().iterator();
while(iter.hasNext()) {
SelectionKey key = iter.next();
iter.remove();
if(key.isValid()) {
if(key.isAcceptable()) {
beforLogin(key);//客户端连接前处理
}
else if(key.isReadable()) {
read(key);
}
else if(key.isWritable()) {
write(key);
}
}
}
}
} catch (IOException e) {
//e.printStackTrace();
}finally {
//TODO ???
}
}
private void write(SelectionKey key) {
}
private void read(SelectionKey key) {
SocketChannel sc = (SocketChannel) key.channel();
ByteBuffer bbf = ByteBuffer.allocate(8192);
try {
sc.read(bbf);
String msg = new String(bbf.array()).trim();
//认证协议
if(msg.startsWith(DDProtocol.AUTH.toString())) {
//auth^&^username^&^password
String[] array = msg.split(DDProtocol.proSeparator);
String name = array[1];
String password = array[2];
Map<String,Object> user = new HashMap<String,Object>();
user.put("name", name);
user.put("password", password);
user.put("channel", sc);
//TODO 黑白名单校验
log.info(df.format(new Date())+": 用户"+name+"登入");
hosts.add(user);
sc.write(ByteBuffer.wrap((df.format(new Date())+":Welcome login DD").getBytes()));
}
//广播协议
else if(msg.startsWith(DDProtocol.BROADCAST.toString())) {
String[] array = msg.split(DDProtocol.proSeparator);
String time = df.format(new Date());
String username = array[1];
String message="";
if(array.length == 3) {
message = array[2];
}
String retV = "["+username+":"+time+"(广播)]:"+message;
send(retV,username);
}
//定点传输协议
else if(msg.startsWith(DDProtocol.FIXEDPOINT.toString())) {
String[] array = msg.split(DDProtocol.proSeparator);
String time = df.format(new Date());
String username = array[1];
String message = "";
String toUser = "";
if(array.length==4) {
message = array[2];
toUser = array[3];
}
String retV = "["+username+":"+time+"(私聊)]:"+message;
send(retV,username,toUser);
}
//数据同步协议
else if(msg.startsWith(DDProtocol.DATASYNC.toString())) {
String name = msg.split(DDProtocol.proSeparator)[1];
StringBuffer retV = new StringBuffer("-----当前用户信息-----\n\t");
hosts.forEach(new Consumer<Map<String,Object>>() {
@Override
public void accept(Map<String, Object> t) {
retV.append("userName:"+t.get("name")+"\n\t");
retV.append("-------------------------------\n\t");
}
});
send(retV.toString(),"系统消息",name);
}
//断开连接
else if(msg.startsWith(DDProtocol.OUT.toString())) {
String[] array = msg.split(DDProtocol.proSeparator);
String time = df.format(new Date());
String username = array[1];
String password = array[2];
Iterator<Map<String, Object>> iter = hosts.iterator();
while(iter.hasNext()) {
Map<String,Object> one = iter.next();
if(username.equals(one.get("name")) && password.equals(one.get("password"))) {
SocketChannel scl = (SocketChannel) one.get("channel");
scl.close();
iter.remove();
log.info(time+": 用户 "+username+"断开了连接");
}
}
}
sc.register(selector, SelectionKey.OP_READ);
} catch (IOException e) {
//e.printStackTrace();
}finally {
//?
}
}
private void send(String msg,String self) {
this.send(msg,self,null);
}
/**
* 客户端连接服务端是的验证处理
* @param key
*/
private void beforLogin(SelectionKey key) {
ServerSocketChannel ssc = (ServerSocketChannel) key.channel();
SocketChannel sc = null;
try {
sc = ssc.accept();//获得客户端连接
sc.configureBlocking(false);//非阻塞
sc.register(selector, SelectionKey.OP_READ);
} catch (IOException e) {
//e.printStackTrace();
}finally {
//TODO ???
}
}
/**
*
*/
private List<Map<String,Object>> getHosts(){
return this.hosts;
}
/**
*
*/
protected void send(String msg,String self,String target) {
try {
//判断name是否登入
hosts.forEach(new Consumer<Map<String,Object>>() {
@Override
public void accept(Map<String, Object> t) {
if(target != null) {
if(t.get("name").equals(target)) {
SocketChannel sc = (SocketChannel) t.get("channel");
try {
sc.write(ByteBuffer.wrap(msg.getBytes()));
} catch (IOException e) {
//e.printStackTrace();
}
}
}
else {
//忽略自己
if(!t.get("name").equals(self)) {
SocketChannel sc = (SocketChannel) t.get("channel");
try {
sc.write(ByteBuffer.wrap(msg.getBytes()));
} catch (IOException e) {
//e.printStackTrace();
}
}
}
}
});
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
log.info("输入要监听的端口号");
Scanner scan = new Scanner(System.in);
String portstr = scan.nextLine();
if(portstr.equals("")) {
log.info("已使用默认端口8000");
portstr="8000";
}
int port = Integer.parseInt(portstr);
DDServer server = new DDServer(port);
Thread console = new Thread(new Runnable() {
@Override
public void run() {
Scanner scan = null;
try {
log.info("Console 控制台已经开启!");
scan = new Scanner(System.in);
while(true) {
String msg = scan.nextLine();
if("exit".equals(msg)) {
log.info("正在停止服务。。。");
Thread.sleep(1000);
log.info("ok");
System.exit(-1);
}
else if("getUsers".equals(msg)){
StringBuffer retV = new StringBuffer("-----当前用户信息-----\n\t");
server.hosts.forEach(new Consumer<Map<String,Object>>() {
@Override
public void accept(Map<String, Object> t) {
retV.append("userName:"+t.get("name")+"\n\t");
retV.append("passWord:"+t.get("password")+"\n\t");
retV.append("channel:"+t.get("channel")+"\n\t");
retV.append("-------------------------------\n\t");
}
});
log.info(retV.toString());
}
else
{
String time = df.format(new Date());
String retV = "[系统消息:"+time+"(大喇叭)]:"+msg;
server.send(retV,"系统");
}
}
} catch (Exception e) {
e.printStackTrace();
}finally {
scan.close();
}
}
},"serverConsole");
Thread jiankong = new Thread(new Runnable() {
@Override
public void run() {
log.info("用户监控已经开启!");
while(true) {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
//e.printStackTrace();
}
List<Map<String, Object>> hosts = server.getHosts();
if(!hosts.isEmpty()) {
Iterator<Map<String, Object>> iter = hosts.iterator();
while(iter.hasNext()) {
Map<String, Object> t = iter.next();
SocketChannel sc = (SocketChannel) t.get("channel");
try {
ByteBuffer bbf = ByteBuffer.allocate(1024);
sc.read(bbf);
new String(bbf.array()).trim();
}catch(Exception e) {
System.out.println(e);
iter.remove();
}
}
}
}
}
},"jiankong");
console.start();//开启服务端Console输入
jiankong.start();//用户监控程序
server.start().listen();
}
}
--------------------------------------------------------------------------------------------------------------------------
package org.lxs.nio;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Random;
import java.util.Scanner;
import java.util.logging.Logger;
/**
* 客户端
* @author lxs
*/
public class DDClient {
private static final Logger log = Logger.getGlobal();
private String host;
private int port;
private SocketChannel sc;
private Selector selector;
private String userName;
private String password;
private SocketChannel master;
public DDClient(String host, int port,String userName,String password) {
this.host = host;
this.port = port;
this.password = password;
this.userName = userName;
}
private DDClient start() {
try {
sc = SocketChannel.open();
sc.configureBlocking(false);
sc.connect(new InetSocketAddress(host, port));
selector = Selector.open();
sc.register(selector,SelectionKey.OP_CONNECT);
log.info("客户端启动完毕...");
} catch (IOException e) {
e.printStackTrace();
System.exit(-1);
}finally {
//???
}
return this;
}
//监听
private void listen() {
try {
log.info("正在连接服务端...");
while(true) {
selector.select();
Iterator<SelectionKey> iter = selector.selectedKeys().iterator();
while(iter.hasNext()) {
SelectionKey key = iter.next();
iter.remove();
if(key.isValid()) {
if(key.isConnectable()) {
beforLogin(key,userName,password);
}
else if(key.isReadable()) {
read(key);
}
else if(key.isWritable()) {
}
else if(key.isAcceptable()) {
}
}
}
}
} catch (IOException e) {
e.printStackTrace();
System.exit(-1);
}finally {
//???
}
}
//处理读状态
private void read(SelectionKey key) {
SocketChannel sc = (SocketChannel) key.channel();
try {
ByteBuffer buffer = ByteBuffer.allocate(8192);
sc.read(buffer);
String msg = new String(buffer.array()).trim();
System.out.println(msg);
sc.register(selector, SelectionKey.OP_READ);
} catch (Exception e) {
e.printStackTrace();
System.exit(-1);
}
}
//完成连接
private void beforLogin(SelectionKey key,String userName,String password) {
SocketChannel sc = (SocketChannel) key.channel();
try {
if(sc.isConnectionPending()) {
sc.finishConnect();
}
sc.write(ByteBuffer.wrap((DDProtocol.AUTH.message(userName,password)).getBytes()));
master = sc;
sc.register(selector, SelectionKey.OP_READ);
} catch (IOException e) {
//e.printStackTrace();
log.info("您的客户端不在服务区,请稍后再试...");
try {
Thread.sleep(1000);
} catch (InterruptedException e1) {
e1.printStackTrace();
}
System.exit(-1);
}
}
//发送消息
protected void send(String msg) throws IOException {
master.write(ByteBuffer.wrap(msg.getBytes()));
master.register(selector, SelectionKey.OP_READ);
}
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
log.info("输入连接主机ip");
String host = scan.nextLine();
if(host.equals("")) {
log.info("已使用默认服务器地址:172.16.14.164");
host="172.16.14.164";
}
log.info("输入连接端口");
String portstr = scan.nextLine();
if(portstr.equals("")) {
log.info("已使用默认服务器端口:8000");
portstr="8000";
}
int port = Integer.parseInt(portstr);
log.info("输入用户名");
String userName = scan.nextLine();
if(userName.equals("")) {
userName= "游客"+new Random().nextInt(10000);
log.info("使用游客身份登录:"+userName);
}
if("系统消息".equals(userName)) {
boolean isError = true;
log.info("非法用户名:"+userName+",请重新输入");
while(isError) {
userName = scan.nextLine();
if(!userName.equals("系统消息")) {
isError = false;
}else {
log.info("非法用户名:"+userName+",请重新输入");
}
}
}
//scan.close();
DDClient client = new DDClient(host, port,userName,"a123456");
Thread console = new Thread(new Runnable() {
@Override
public void run() {
Scanner scan = new Scanner(System.in);
try {
log.info("Console 控制台已经开启!");
while(true) {
String msg = scan.nextLine();
if("exit".equals(msg.trim())) {
log.info("正在关闭客户端。。。");
//告诉服务器,我要退出了
Thread.sleep(2000);
log.info("success");
client.send(DDProtocol.OUT.message(client.userName,client.password));
System.exit(-1);
}
else if("getUsers".equals(msg.trim())) {
//同步用户信息
client.send(DDProtocol.DATASYNC.message(client.userName));
}
else {
//转换协议进行发送
if(msg.startsWith("@")) {//定点
int index = msg.indexOf(":");
if(index==-1) {
log.info("@使用错误:@user:message");
}else {
String target = msg.substring(1, index);
String msgValue = msg.substring(index+1);
client.send(DDProtocol.FIXEDPOINT.message(client.userName,msgValue,target));
}
}else {//广播
client.send(DDProtocol.BROADCAST.message(client.userName,msg.trim()));
}
}
}
} catch (Exception e) {
e.printStackTrace();
System.exit(-1);
}finally {
scan.close();
}
}
},"serverConsole");
console.start();
System.out.println("--------------帮助开始-----------------");
System.out.println("广播消息:your message");
System.out.println("定向消息:@username:your messsage");
System.out.println("获取当前用户列表:getUsers");
System.out.println("退出:exit");
System.out.println("--------------帮助结束-----------------");
client.start().listen();
}
}