NIO 聊天室代码实现

服务器端

package com.ronnie.nio.groupChat;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.Iterator;

public class GroupChatServer {

    private Selector selector;
    private ServerSocketChannel listenChannel;
    private static final  int PORT = 9999;

    /* 构造器
       初始化任务
     */
    public GroupChatServer(){
        try {
            // 得到选择器
            selector = Selector.open();
            // serverSocketChannel
            listenChannel = ServerSocketChannel.open();
            // 绑定端口
            listenChannel.socket().bind(new InetSocketAddress(PORT));
            // 设置非阻塞模式
            listenChannel.configureBlocking(false);
            // 将该listenChannel 注册到Selector
            listenChannel.register(selector, SelectionKey.OP_ACCEPT);

        }catch (IOException e){
            e.printStackTrace();
        }
    }

    /**
     *  监听
     */
    public void listen(){
        try {
            // 循环处理
            while (true){
                int count = selector.select(2000);
                if (count > 0){ // 有事件处理
                    // 遍历得到的selectionKey集合
                    Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
                    while (iterator.hasNext()){
                        // 取出selectionKey
                        SelectionKey key = iterator.next();

                        // 监听到OP_ACCEPT
                        if (key.isAcceptable()){
                            SocketChannel sc = listenChannel.accept();
                            sc.configureBlocking(false);
                            // 将该socketChannel注册到Selector
                            sc.register(selector, SelectionKey.OP_READ);
                            // 提示
                            System.out.println(sc.getRemoteAddress() + " connected to the chat");
                        }
                        // 通道可读
                        if (key.isReadable()){
                            // TODO处理读
                            readData(key);
                        }
                        // 当前的key删除, 防止重复处理
                        iterator.remove();
                    }
                } else {
                    System.out.println("Waiting......");
                }
            }
        }catch (Exception e){
            e.printStackTrace();
        } finally {
            // 发生异常处理
        }
    }

    /**
     *  读取客户端消息
     * @param key
     */
    private void readData(SelectionKey key){
        // 定义一个SocketChannel
        SocketChannel channel = null;
        try {
            // 得到channel
            channel = (SocketChannel) key.channel();

            // 创建缓冲buffer
            ByteBuffer buffer = ByteBuffer.allocate(1024);

            int count = channel.read(buffer);
            // 根据count的值做处理
            if (count > 0){
                // 把缓冲区数据转为字符串并输出
                String msg = new String(buffer.array());
                // 输出该消息
                System.out.println("from Client: " + msg);

                // 向其他的客户端转发消息(去掉自己)
                sendInfoToOtherClients(msg,channel);
            }
        } catch (IOException e){
            try {
                System.out.println(channel.getRemoteAddress() + " is offline");
                // 取消注册
                key.cancel();
                // 关闭通道
                channel.close();
            } catch (IOException ex) {
                ex.printStackTrace();
            }
        }
    }

    /**
     *  转发消息给其他客户端(channel)
     * @param msg
     * @param self
     */
    private void sendInfoToOtherClients(String msg, SocketChannel self) throws IOException {
        System.out.println("Server is transferring messages......");
        // 遍历, 所有注册到selector上的 SocketChannel, 并派出 自己
        for (SelectionKey key : selector.keys()){

            // 通过key取出对应的SocketChannel
            Channel targetChannel = key.channel();

            // 排除自己
            if (targetChannel instanceof SocketChannel && targetChannel != self){

                // 转型
               SocketChannel dest = (SocketChannel) targetChannel;

                // 将消息存储到buffer
                ByteBuffer buffer = ByteBuffer.wrap(msg.getBytes());

                // 将buffer数据写入到通道
                dest.write(buffer);
            }
        }
    }

    public static void main(String[] args) {

        // 创建服务器对象
        GroupChatServer groupChatServer = new GroupChatServer();
        groupChatServer.listen();
    }
}

客户端

package com.ronnie.nio.groupChat;

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.Arrays;
import java.util.Iterator;
import java.util.Scanner;

public class GroupChatClient {

    // 定义相关属性

    private final int PORT = 9999;
    private Selector selector;
    private SocketChannel socketChannel;
    private java.lang.String username;

    /**
     *  构造器, 完成初始化工作
     */
    public GroupChatClient() throws IOException {

        selector = Selector.open();

        // 连接服务器
        socketChannel = SocketChannel.open(new InetSocketAddress("127.0.0.1", PORT));

        // 设置非阻塞
        socketChannel.configureBlocking(false);

        // 将channel 注册到selector
        socketChannel.register(selector, SelectionKey.OP_READ);

        // 得到username
        username = socketChannel.getLocalAddress().toString().substring(1);

        System.out.println(username + " is fine");

    }

    /**
     * 向服务器发送消息
     * @param info
     */
    public void sendInfo(java.lang.String info){
        info = username + " said: " + info;

        try {
            socketChannel.write(ByteBuffer.wrap(info.getBytes()));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public void  readInfo(){

        try{
            int readChannels = selector.select();

            // 有可用的通道
            if (readChannels > 0){
                Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
                while (iterator.hasNext()){
                    SelectionKey key = iterator.next();
                    // 客户端只考虑可读
                    if (key.isReadable()){
                        // 得到相关通道
                        SocketChannel sc = (SocketChannel) key.channel();
                        // 得到一个Buffer
                        ByteBuffer buffer = ByteBuffer.allocate(1024);
                        // 读取
                        sc.read(buffer);
                        // 把读到的缓冲区数据转成字符串
                        String msg = Arrays.toString((buffer.array()));
                        System.out.println(msg.trim());
                    }
                }
                iterator.remove(); // 删除当前的selectionKey, 防止重复操作
            } else {
                System.out.println("No channel available");
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) throws IOException {

        // 启动客户端
        GroupChatClient chaClient = new GroupChatClient();

        // 启动一个线程
        new Thread(){
            @Override
            public void run() {
                while (true){
                    chaClient.readInfo();

                    try {
                        Thread.currentThread().sleep(3000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }.start();

        // 发送数据给服务器端
        Scanner scanner = new Scanner(System.in);

        while (scanner.hasNext()){
            String s = scanner.nextLine();
            chaClient.sendInfo(s);
        }
    }

}
  • PS: 这种代码不是天天敲是不可能很熟的, 只是找找感觉, 哪天真用到了回来看看以前的博客......

原文地址:https://www.cnblogs.com/ronnieyuan/p/12005580.html

时间: 2024-10-11 00:48:44

NIO 聊天室代码实现的相关文章

java swing聊天室代码

原文:java swing聊天室代码 源代码下载地址:http://www.zuidaima.com/share/1550463525358592.htm 真正的java聊天室代码,实现简单,阅读方便,原理简单! 先启动server.ServerMain的效果如下: 再启动client.ClientMain的效果如下: 注册界面: 群聊界面: 已注册用户列表: 多个账号群聊界面: 多个账号群聊界面2:

从聊天室代码看epoll的使用和对比

网上找到了一份基于epoll的简单的多人聊天室代码,感觉对epoll的学习十分有用,代码会附在后面,简单看一下epoll相关的API. epoll相关的API主要有三个:epoll_create.epoll_ctl和epoll_wait int epoll_create(int size); 参数size:用来告诉内核要监听的数目一共有多少个.返回值:成功时,返回一个非负整数的文件描述符,作为创建好的epoll句柄.调用失败时,返回-1,错误信息可以通过errno获得. int epoll_ct

JAVA聊天室代码

聊天室界面: 源码: public class ClientFrame extends Frame { private TextField textFieldContent = new TextField(); private TextArea textAreaContent = new TextArea(); private Socket socket = null; private OutputStream out = null; private DataOutputStream dos =

nodejs 基于socket.io实现聊天室

由于之后要做的网页视频直播项目要用到socket.io模块,所以特地花时间研究了下,参照网上的代码做了些改进,自己写了个聊天室代码.不得不承认后端事实推送能力有点厉害,这是以前我用php一直苦恼的事情.下面简单介绍下我的项目,顺带讲解下nodejs. 事实上,在看别人写的代码之前,我一直不知道nodejs是干嘛的,直到真正接触到才明白这也可以算作是服务端代码,丰富的第三方库使其功能极其强大.它可以像golang的beego一样直接通过命令行开启服务器,不过要用到express模块.加载模块的方式

夺命雷公狗---node下的一聊天室-首发

今晚感觉挺蛋疼,就用了点时间,在node下开发了个聊天室来玩玩,不过之是简单的开发了套而已,并没多做什么考虑,, 但是发现了一个好处就是用node来写聊天室代码居然少得可怜,这个不佩服node都不行,效果图如下所示: 说句实话,我都有点爱上node了,因为深深的被她的魅力感染了,嘻嘻,不做过多的介绍,代码其实也很简单: 说实话感觉node真的很强大,, 源码下载地址: 链接:http://pan.baidu.com/s/1cvEdTO 密码:wx9s

【总结】学习Socket编写的聊天室小程序

1.前言 在学习Socket之前,先来学习点网络相关的知识吧,自己学习过程中的一些总结,Socket是一门很高深的学问,本文只是Socket一些最基础的东西,大神请自觉绕路. 传输协议 TCP:Transmission Control Protocol 传输控制协议TCP是一种面向连接(连接导向)的.可靠的.基于字节流的运输层(Transport layer)通信协议. 特点: 面向连接的协议,数据传输必须要建立连接,所以在TCP中需要连接时间. 传输数据大小限制,一旦连接建立,双方可以按统一的

Spring整合DWR comet 实现无刷新 多人聊天室

用dwr的comet(推)来实现简单的无刷新多人聊天室,comet是长连接的一种.通常我们要实现无刷新,一般会使用到Ajax.Ajax 应用程序可以使用两种基本的方法解决这一问题:一种方法是浏览器每隔若干秒时间向服务器发出轮询以进行更新,另一种方法是服务器始终打开与浏览器的连接并在数据可用时发送给浏览器.第一种方法一般利用setTimeout或是setInterval定时请求,并返回最新数据,这无疑增加了服务器的负担,浪费了大量的资源.而第二种方法也会浪费服务器资源,长期的建立连接:而相对第一种

C 基于UDP实现一个简易的聊天室

引言 本文是围绕Linux udp api 构建一个简易的多人聊天室.重点看思路,帮助我们加深 对udp开发中一些api了解.相对而言udp socket开发相比tcp socket开发注意的细节要少很多. 但是水也很深. 本文就当是一个demo整合帮助开发者回顾和继续了解 linux udp开发的基本流程. 首先我们来看看 linux udp 和 tcp的异同. /* 这里简单比较一下TCP和UDP在编程实现上的一些区别: TCP流程 建立一个TCP连接需要三次握手,而断开一个TCP则需要四个

python 实现聊天室

所用模块 asyncore 官方介绍, 源码 英文捉鸡点 这里  源码中可以看到其实本质上就对 select 以及 socket 的进一步封装 简单说明 Python的asyncore模块提供了以异步的方式写入套接字服务的客户端和服务器的基础结构. 主要包括 asyncore.loop(…) - 用于循环监听网络事件.loop()函数负责检测一个字典,字典中保存dispatcher的实例. asyncore.dispatcher类 - 一个底层套接字对象的简单封装.这个类有少数由异步循环调用的,