`
kree
  • 浏览: 128847 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

Java非阻塞聊天室源码 Server

阅读更多

//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 nio聊天室源码

    在这个“java nio聊天室源码”项目中,我们可以看到如何使用NIO构建一个实时、高效的多人在线聊天应用。 首先,我们要理解Java NIO的基本概念。NIO包括以下关键组件: 1. **通道(Channels)**:通道是数据传输的...

    web聊天室源码

    【Web聊天室源码详解】 在信息技术领域,Web聊天室是一种基于Web的实时通信平台,允许用户无需安装额外软件即可进行在线交流。本篇将详细探讨Web聊天室的实现原理、关键技术以及源码分析。 一、Web聊天室的实现...

    Web聊天室系统源码

    10. **异步处理**:为了提高系统响应速度,服务器端可能会采用异步处理机制,如Java的CompletableFuture或使用非阻塞I/O。 11. **测试与调试**:单元测试、集成测试和压力测试都是确保代码质量的重要步骤,JUnit和...

    Java聊天室源代码

    同时,如果你希望扩展这个聊天室,可以考虑引入多线程服务器模型(如线程池或异步I/O模型),或者使用NIO(非阻塞I/O)以提高服务器处理大量并发连接的能力。 总之,这个Java聊天室项目是一个很好的实践平台,能让...

    JAVA基于Netty实现的聊天室

    【Java基于Netty实现的聊天室】是一种使用Java编程语言并借助Netty框架构建的实时通信应用。Netty是一个高性能、异步事件驱动的网络应用框架,为开发高并发、低延迟的网络应用提供了强大的支持。这个简易聊天室项目...

    java NIO 学习 聊天室程序 (3)

    在这个“Java NIO 学习 聊天室程序”项目中,我们将深入探讨NIO如何用于创建一个聊天室程序。这个程序可能包含以下几个关键部分: 1. **服务器端**:服务器端使用NIO的ServerSocketChannel监听客户端连接。当新的...

    JAVA上百实例源码以及开源项目源代码

    Java非对称加密源码实例 1个目标文件 摘要:Java源码,算法相关,非对称加密 Java非对称加密源程序代码实例,本例中使用RSA加密技术,定义加密算法可用 DES,DESede,Blowfish等。 设定字符串为“张三,你好,我是李四”...

    在线聊天室

    【在线聊天室】是一个基于JavaWeb技术实现的网络交流平台,它允许用户通过互联网进行实时的文本、语音甚至视频通信。下面将详细讲解这个系统的关键知识点。 1. **Servlet与JSP**:JavaWeb开发中,Servlet是服务器端...

    Java WebSocket编程,java实现websocket,Java源码.zip

    这种特性非常适合需要实时交互的应用,如在线游戏、股票交易、聊天室等。 二、Java API for WebSocket (JSR 356) JSR 356为Java开发者提供了一套用于创建WebSocket服务器和客户端的API。主要涉及以下组件: 1. **...

    java源码:java Socket通信实现.rar

    - 使用NIO(New IO)或NIO.2可以实现非阻塞I/O,提升服务器的并发能力。 - 安全性方面,SSL/TLS协议可以为Socket通信提供加密保护,防止数据被窃取。 通过学习并实践这个"java Socket通信实现"的源码,你可以深入...

    C++做的一个聊天室程序

    2. **异步I/O**:另一种可能的方法是使用异步I/O,如Boost.Asio库,以非阻塞方式处理多个客户端请求。 3. **数据结构**:聊天室可能使用队列或缓冲区来存储待发送和接收到的消息,确保消息的有序传递。 4. **对象...

    java Socket通信实现源码示例

    Java Socket通信广泛应用于各种网络应用,如聊天室、文件传输、在线游戏等。在实际开发中,我们可能会结合线程池、NIO(非阻塞I/O)等技术来优化性能。 九、源码示例 这里提供一个简单的Java Socket通信的服务器端...

    FrixChat:私人聊天室

    Node.js的非阻塞I/O模型使得它非常适合处理大量并发连接,因此在聊天应用中非常适用。 3. **CSS/HTML**:通过CSS(层叠样式表)和HTML(超文本标记语言)来构建聊天室的用户界面。CSS用于控制布局和视觉样式,HTML...

    java socket

    Java Socket是Java编程语言中用于网络通信的...通过理解Socket的工作原理,我们可以创建自定义的网络应用,如聊天室、文件传输等。在MyEclipse中,你可以直接导入提供的源码,通过运行和调试来加深对Socket通信的理解。

    socket编程实现简单私聊群聊源码

    在Java中,可以使用多线程或者NIO(非阻塞I/O)来处理并发连接。 在Socket编程中,错误处理至关重要,如连接失败、数据传输异常等都需要妥善处理。此外,为了保证聊天应用的可用性和稳定性,还需要考虑关闭连接的...

    tornado源代码

    4. **WebSockets**:Tornado 对 WebSocket 协议提供了原生支持,使得开发者可以轻松地创建实时双向通信应用,如聊天室、股票报价等。 5. **Autoreloader**:在开发过程中,Tornado 提供了自动重载功能,当检测到源...

    netty-socketio-1.6.5.zip

    开发者可以使用熟悉的Java编程语言,享受到Socket.IO提供的实时双向通信特性,这对于构建实时应用,如聊天室、协作工具、在线游戏等非常有用。 在解压后的文件"netty-socketio-master"中,通常会包含以下内容: 1....

Global site tag (gtag.js) - Google Analytics