Java NIO练手(聊天室)

本文介绍了使用Java NIO实现的DD聊天室,包括数据传输协议如授权、广播、定点传输和数据同步等。提供了服务端和客户端的代码实现,服务端通过Selector监听连接,处理读写操作。客户端通过连接服务端,发送认证信息并接收及发送消息。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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();
        
    }
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值