基于 java nio 长连接实现的聊天室

本文介绍了一个基于 Java NIO 实现的聊天室案例,通过 ServerSocketChannel 和 Selector 处理客户端连接和消息读写。文章提供了完整的服务端和客户端代码,并解释了如何启动服务端及多个客户端进行交互。

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

TCP长连接与短连接的区别

基于 java nio 长连接实现的聊天室,如果并发量大的话,可能会有线程问题。

服务端代码

package com.lp.io.socket;

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.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;

public class LpNioServerSocket {

    //线程安全
    private static List<SocketChannel> channels = Collections.synchronizedList( new ArrayList<SocketChannel>() );

    public static void main(String[] args) {

        HandlerSelectionKey handler = new HandlerHandlerSelectionKeyImpl();

        try {
            //创建 ServerSocketChannel
            ServerSocketChannel server = ServerSocketChannel.open();
            server.configureBlocking(false);
            server.bind(new InetSocketAddress("localhost", 12345));
            //创建 Selector
            Selector selector = Selector.open();
            server.register(selector, SelectionKey.OP_ACCEPT);
            //死循环,持续接收 客户端连接
            while(true) {
                //selector.select(); 是阻塞方法
                int keys = selector.select();
                if(keys > 0) {
                    Iterator<SelectionKey> it = selector.selectedKeys().iterator();
                    while(it.hasNext()) {
                        SelectionKey key = it.next();
                        it.remove();
                        //处理 SelectionKey
                        handler.handler(key, selector);
                    }
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * SelectionKey 处理接口
     *
     */
    public static interface HandlerSelectionKey {

        public void handler(SelectionKey key, Selector selector) throws IOException;

    }

    /**
     * SelectionKey 接口 实现类
     *
     */
    public static class HandlerHandlerSelectionKeyImpl implements HandlerSelectionKey {

        @Override
        public void handler(SelectionKey key, Selector selector) throws IOException {
            int keyState = selectionKeyState(key);
            switch (keyState) {
            case SelectionKey.OP_ACCEPT:
                ServerSocketChannel serverSocketChannel = (ServerSocketChannel) key.channel();
                accept(serverSocketChannel, selector);
                break;
            case SelectionKey.OP_READ:
                SocketChannel readSocketChannel = (SocketChannel) key.channel();
                read(readSocketChannel, selector);
                break;
            }
        }
        /**
         * 获取 SelectionKey 是什么事件
         * @param key
         * @return
         */
        private int selectionKeyState(SelectionKey key) {
            if(key.isAcceptable()) {
                return SelectionKey.OP_ACCEPT;
            } else if(key.isReadable()) {
                return SelectionKey.OP_READ;
            }
            return -1;
        }

        /**
         * 接口客户端请求
         * @param serverSocketChannel
         * @param selector
         * @throws IOException
         */
        private void accept(ServerSocketChannel serverSocketChannel, Selector selector) throws IOException {
            SocketChannel socketChannel = serverSocketChannel.accept();
            socketChannel.configureBlocking(false);
            channels.add(socketChannel);
            //将 channel 注册到  Selector
            socketChannel.register(selector, SelectionKey.OP_READ);
        }

        /**
         * 读取客户端发送过来的信息
         * @param socketChannel
         * @param selector
         * @throws IOException
         */
        private void read(SocketChannel socketChannel, Selector selector) throws IOException {
            ByteBuffer readBuffer = ByteBuffer.allocate(8192);
            int readBytes = socketChannel.read(readBuffer);
            String msg = "";//客户端发送来的消息
            if(readBytes > 0) {
                 msg = new String(readBuffer.array(), 0, readBytes);
                System.out.println("客户端发送来的消息");
                System.out.println(msg);
            }
            write(socketChannel, msg);
        }

        /**
         * 响应客户端请求
         * @param socketChannel
         * @param selector
         * @throws IOException
         */
        private void write(SocketChannel socketChannel, String msg) throws IOException {
            msg = "游客" + socketChannel.hashCode()+ "\r\n    " + msg;
            //响应消息
            byte[] responseByte = msg.getBytes();
            ByteBuffer writeBuffer = ByteBuffer.allocate(responseByte.length);
            writeBuffer.put(responseByte);
            writeBuffer.flip();
            //响应客户端
            for(int i=0; i<channels.size(); i++) {
                if(!socketChannel.equals(channels.get(i))) {
                    channels.get(i).write(writeBuffer);
                }
            }
        }

    }


}

客户端代码

package com.lp.io.socket;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.Socket;
import java.net.UnknownHostException;

public class LpSocketClient1 {

    /**
     * @param args
     * @throws IOException 
     * @throws UnknownHostException 
     * @throws InterruptedException 
     */
    public static void main(String[] args) throws UnknownHostException, IOException, InterruptedException {

        final Socket socket = new Socket("localhost", 12345);
        Thread inT = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    while(true) {
                        InputStream inputStream = socket.getInputStream();
                        byte[] b = new byte[8192];
                        int readSize = inputStream.read(b);
                        System.out.println(new String(b,0,readSize));
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        });
        inT.start();
        while(true) {
            InputStreamReader stdin = new InputStreamReader(System.in);//键盘输入 
            BufferedReader bufin = new BufferedReader(stdin); 
            String str = bufin.readLine(); 
            System.out.println(str);

            OutputStream outStream = socket.getOutputStream();
            outStream.write(str.getBytes());
            outStream.flush();
        }

    }

}

启动服务端代码,然后启动多个客户端。在某个客户端代码控制台输入英文(输入中文乱码),点击回车,可以看到其他客户端控制台有信息输出。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值