前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >用Java代码实现一个在线聊天功能案例

用Java代码实现一个在线聊天功能案例

作者头像
框架师
发布2019-10-08 12:16:02
2.7K0
发布2019-10-08 12:16:02
举报
文章被收录于专栏:墨白的Java基地墨白的Java基地

直接用 NIO 实现一个多人聊天案例,话不多说,直接上代码。

代码语言:javascript
复制
  1package com.mobaijun.TCP;
  2
  3import java.io.IOException;
  4import java.net.InetSocketAddress;
  5import java.nio.ByteBuffer;
  6import java.nio.channels.*;
  7import java.text.SimpleDateFormat;
  8import java.util.Date;
  9import java.util.Iterator;
 10
 11/**
 12 * @Author:Auser·杰
 13 * @DATE:2019/10/7 22:59
 14 */
 15public class ChatServer {
 16    private Selector selector;
 17    private ServerSocketChannel listenerChannel;
 18    //服务器端口
 19    private static final int PORT = 9999;
 20
 21    public ChatServer() {
 22        try {
 23            // 得到选择器
 24            selector = Selector.open();
 25            // 打开监听通道
 26            listenerChannel = ServerSocketChannel.open();
 27            // 绑定端口
 28            listenerChannel.bind(new InetSocketAddress(PORT));
 29            // 设置为非阻塞模式
 30            listenerChannel.configureBlocking(false);
 31            // 将选择器绑定到监听通道并监听 accept 事件
 32            listenerChannel.register(selector, SelectionKey.OP_ACCEPT);
 33            printInfo("Chat Server is ready.......");
 34        } catch (IOException e) {
 35            e.printStackTrace();
 36        }
 37    }
 38
 39    public void start() {
 40        try {
 41            //不停轮询
 42            while (true) {
 43                //获取就绪 channel
 44                int count = selector.select();
 45                if (count > 0) {
 46                    Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
 47                    while (iterator.hasNext()) {
 48                        SelectionKey key = iterator.next();
 49                        // 监听到 accept
 50                        if (key.isAcceptable()) {
 51                            SocketChannel sc = listenerChannel.accept();
 52                            //非阻塞模式
 53                            sc.configureBlocking(false);
 54                            //注册到选择器上并监听 read
 55                            sc.register(selector, SelectionKey.OP_READ);
 56                            System.out.println(sc.getRemoteAddress().toString().substring(1) + "上线了...");
 57                            //将此对应的 channel 设置为 accept,接着准备接受其他客户端请求
 58                            key.interestOps(SelectionKey.OP_ACCEPT);
 59                        }
 60                        //监听到 read
 61                        if (key.isReadable()) {
 62                            readMsg(key); //读取客户端发来的数据
 63                        }
 64                        //一定要把当前 key 删掉,防止重复处理
 65                        iterator.remove();
 66                    }
 67                } else {
 68                    System.out.println("独自在寒风中等候...");
 69                }
 70            }
 71        } catch (IOException e) {
 72            e.printStackTrace();
 73        }
 74    }
 75
 76    private void readMsg(SelectionKey key) {
 77        SocketChannel channel = null;
 78        try {
 79            // 得到关联的通道
 80            channel = (SocketChannel) key.channel();
 81            //设置 buffer 缓冲区
 82            ByteBuffer buffer = ByteBuffer.allocate(1024);
 83            //从通道中读取数据并存储到缓冲区中
 84            int count = channel.read(buffer);
 85            //如果读取到了数据
 86            if (count > 0) {
 87                //把缓冲区数据转换为字符串
 88                String msg = new String(buffer.array());
 89                printInfo(msg);
 90                //将关联的 channel 设置为 read,继续准备接受数据
 91                key.interestOps(SelectionKey.OP_READ);
 92                BroadCast(channel, msg); //向所有客户端广播数据
 93            }
 94            buffer.clear();
 95        } catch (IOException e) {
 96            try {
 97                //当客户端关闭 channel 时,进行异常如理
 98                printInfo(channel.getRemoteAddress().toString().substring(1) + "下线了...");
 99                key.cancel(); //取消注册
100                channel.close(); //关闭通道
101            } catch (IOException e1) {
102                e1.printStackTrace();
103            }
104        }
105    }
106
107    public void BroadCast(SocketChannel except, String msg) throws IOException {
108        System.out.println("发送广播...");
109        //广播数据到所有的 SocketChannel 中
110        for (SelectionKey key : selector.keys()) {
111            Channel targetchannel = key.channel();
112            //排除自身
113            if (targetchannel instanceof SocketChannel && targetchannel != except) {
114                SocketChannel dest = (SocketChannel) targetchannel;
115                //把数据存储到缓冲区中
116                ByteBuffer buffer = ByteBuffer.wrap(msg.getBytes());
117                //往通道中写数据
118                dest.write(buffer);
119            }
120        }
121    }
122
123    private void printInfo(String str) {
124        //往控制台打印消息
125        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
126        System.out.println("[" + sdf.format(new Date()) + "] -> " + str);
127    }
128
129    public static void main(String[] args) {
130        ChatServer server = new ChatServer();
131        server.start();
132    }
133}
  • 上述代码使用了 NIO 编写了一个聊天程序的服务器端,可以接受客户端发来的数据,并能把数据广播给所有客户端。
代码语言:javascript
复制
 1package com.mobaijun.TCP;
 2
 3import java.io.IOException;
 4import java.net.InetSocketAddress;
 5import java.nio.ByteBuffer;
 6import java.nio.channels.SelectionKey;
 7import java.nio.channels.Selector;
 8import java.nio.channels.SocketChannel;
 9import java.util.Iterator;
10import java.util.Set;
11
12/**
13 * @Author:Auser·杰
14 * @DATE:2019/10/7 23:05
15 */
16public class ChatClient {
17    private final String HOST = "127.0.0.1"; //服务器地址
18    private int PORT = 9999; //服务器端口
19    private Selector selector;
20    private SocketChannel socketChannel;
21    private String userName;
22
23    public ChatClient() throws IOException {
24        //得到选择器
25        selector = Selector.open();
26        //连接远程服务器
27        socketChannel = SocketChannel.open(new InetSocketAddress("127.0.0.1", PORT));
28        //设置非阻塞
29        socketChannel.configureBlocking(false);
30        //注册选择器并设置为 read
31        socketChannel.register(selector, SelectionKey.OP_READ);
32        //得到客户端 IP 地址和端口信息,作为聊天用户名使用
33        userName = socketChannel.getLocalAddress().toString().substring(1);
34        System.out.println("---------------Client(" + userName + ") is ready---------------");
35    }
36
37    //向服务器端发送数据
38    public void sendMsg(String msg) throws Exception {
39        //如果控制台输入 bye 就关闭通道,结束聊天
40        if (msg.equalsIgnoreCase("bye")) {
41            socketChannel.close();
42            socketChannel = null;
43            return;
44        }
45        msg = userName + "说: " + msg;
46        try {
47            //往通道中写数据
48            socketChannel.write(ByteBuffer.wrap(msg.getBytes()));
49        } catch (IOException e) {
50            e.printStackTrace();
51        }
52    }
53
54    //从服务器端接收数据
55    public void receiveMsg() {
56        try {
57            int readyChannels = selector.select();
58            if (readyChannels > 0) { //有可用通道
59                Set selectedKeys = selector.selectedKeys();
60                Iterator keyIterator = selectedKeys.iterator();
61                while (keyIterator.hasNext()) {
62                    SelectionKey sk = (SelectionKey) keyIterator.next();
63                    if (sk.isReadable()) {
64                        //得到关联的通道
65                        SocketChannel sc = (SocketChannel) sk.channel();
66                        //得到一个缓冲区
67                        ByteBuffer buff = ByteBuffer.allocate(1024);
68                        //读取数据并存储到缓冲区
69                        sc.read(buff);
70                        //把缓冲区数据转换成字符串
71                        String msg = new String(buff.array());
72                        System.out.println(msg.trim());
73                    }
74                    keyIterator.remove(); //删除当前 SelectionKey,防止重复处理
75                }
76            } else {
77                System.out.println("人呢?都去哪儿了?没人聊天啊...");
78            }
79        } catch (IOException e) {
80            e.printStackTrace();
81        }
82    }
83}
  • 上述代码通过是 NIO 编写了一个聊天程序的客户端,可以向服务器端发送数据,并能接收服务器广播的数据。
代码语言:javascript
复制
 1package com.mobaijun.TCP;
 2
 3import java.util.Scanner;
 4
 5/**
 6 * @Author:Auser·杰
 7 * @DATE:2019/10/7 23:07
 8 */
 9public class TestMoBai {
10    public static void main(String[] args) throws Exception {
11        //创建一个聊天客户端对象
12        ChatClient chatClient = new ChatClient();
13        new Thread() { //单独开一个线程不断的接收服务器端广播的数据
14            @Override
15            public void run() {
16                while (true) {
17                    chatClient.receiveMsg();
18                    try { //间隔 3 秒
19                        sleep(3000);
20                    } catch (InterruptedException e) {
21                        e.printStackTrace();
22                    }
23                }
24            }
25        }.start();
26        Scanner scanner = new Scanner(System.in);
27        //在控制台输入数据并发送到服务器端
28        while (scanner.hasNextLine()) {
29            String msg = scanner.nextLine();
30            chatClient.sendMsg(msg);
31        }
32    }
33}
  • 上述代码运行了聊天程序的客户端,并在主线程中发送数据,在另一个线程中不断接收服务器端的广播数据,该代码运行一次就是一个聊天客户端,可以同时运行多个聊天客户端,聊天效果如下图所示。
本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-10-08,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 框架师 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
数据保险箱
数据保险箱(Cloud Data Coffer Service,CDCS)为您提供更高安全系数的企业核心数据存储服务。您可以通过自定义过期天数的方法删除数据,避免误删带来的损害,还可以将数据跨地域存储,防止一些不可抗因素导致的数据丢失。数据保险箱支持通过控制台、API 等多样化方式快速简单接入,实现海量数据的存储管理。您可以使用数据保险箱对文件数据进行上传、下载,最终实现数据的安全存储和提取。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档