Java非阻塞聊天室源码 Server

本文详细介绍了NBChatServer服务器的实现过程,包括配置文件读取、服务器初始化、启动逻辑、客户端连接管理、消息接收处理及广播机制。通过实例展示了如何使用Java创建一个多用户在线聊天应用。

//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]);
    }
} 
 
【博士论文复现】【阻抗建模、验证扫频法】光伏并网逆变器扫频与稳定性分析(包含锁相环电流环)(Simulink仿真实现)内容概要:本文档是一份关于“光伏并网逆变器扫频与稳定性分析”的Simulink仿真实现资源,重点复现博士论文中的阻抗建模与扫频法验证过程,涵盖锁相环和电流环等关键控制环节。通过构建详细的逆变器模型,采用小信号扰动方法进行频域扫描,获取系统输出阻抗特性,并结合奈奎斯特稳定判据分析并网系统的稳定性,帮助深入理解光伏发电系统在弱电网条件下的动态行为与失稳机理。; 适合人群:具备电力电子、自动控制理论基础,熟悉Simulink仿真环境,从事新能源发电、微电网或电力系统稳定性研究的研究生、科研人员及工程技术人员。; 使用场景及目标:①掌握光伏并网逆变器的阻抗建模方法;②学习基于扫频法的系统稳定性分析流程;③复现高水平学术论文中的关键技术环节,支撑科研项目或学位论文工作;④为实际工程中并网逆变器的稳定性问题提供仿真分析手段。; 阅读建议:建议读者结合相关理论教材与原始论文,逐步运行并调试提供的Simulink模型,重点关注锁相环与电流控制器参数对系统阻抗特性的影响,通过改变电网强度等条件观察系统稳定性变化,深化对阻抗分析法的理解与应用能力。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值