返回顶部
首页 > 资讯 > 数据库 >简易版聊天系统实现 Socket VS NIO两种实现方式
  • 309
分享到

简易版聊天系统实现 Socket VS NIO两种实现方式

2024-04-02 19:04:59 309人浏览 薄情痞子
摘要

说是简单聊天系统,压根不能算是一个系统,顶多算个雏形。本文重点不在聊天系统设计和实现上,而是通过实现类似效果,展示下NIO 和Socket两种编程方式的差异性。说是Socket与Nio的编程方式,不太严谨,

说是简单聊天系统,压根不能算是一个系统,顶多算个雏形。本文重点不在聊天系统设计和实现上,而是通过实现类似效果,展示下NIOSocket两种编程方式的差异性。说是Socket与Nio的编程方式,不太严谨,因为NIO的底层也是通过Socket实现的,但又想不出非常好的题目,就这样吧。

主要内容

Socket方式实现简易聊天效果

NIO方式实现简易聊天效果

两种方式的性能对比


前言

预期效果,是客户端之间进行“广播”式聊天,类似于QQ群聊天。希望以后有机会,以此简易版为基础,不断演进,演练下在线聊天系统。

1.Socket方式实现简易聊天效果

1.1服务端 Server.java

package com.example.socket.server;
import java.io.IOException;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;
public class Server {
    private static int port =9999;
    // 可接受请求队列的最大长度
    private static int backlog=100;
    // 绑定到本机的IP地址
    private static final String bindAddr = "127.0.0.1";
    //socket字典列表
    private static List<Socket> nodes= new ArrayList<Socket>();
    public static void main(String[] args) {
        try {
            ServerSocket ss = new ServerSocket(port, backlog,InetAddress.getByName(bindAddr));
            for(;;){
                //发生阻塞,等待客户端连接            
                Socket sc = ss.accept();
                nodes.add(sc);
                InetAddress addr = sc.getLocalAddress();
                System.out.println("create new session from "+addr.getHostName()+":"+sc.getPort()+"\n");            
               //针对一个Socket 客户端 启动两个线程,分别是接收信息,发送信息
                new Thread(new ServerMessageReceiver(sc,nodes)).start();
                new ServerMessageSender(sc).start();
            }            
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

1.2 消息接收端 ServerMessageReceiver.java

额外负责信息广播

package com.example.socket.server;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;

public class ServerMessageReceiver implements Runnable{
    private Socket socket;
    //socket字典列表
    private List<Socket> nodes= new ArrayList<Socket>();
    public ServerMessageReceiver(Socket sc,List<Socket> nodes){
        this.socket=sc;
        this.nodes=nodes;
    }
    
    @Override
    public void run() {    
        try {
            BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream(), "UTF-8"));
            //接收到的消息
            String content;
            while (true) {
                if(socket.isClosed()){
                    System.out.println("Socket已关闭,无法获取消息");
                    reader.close();
                    socket.close();
                    break;
                }
                content=reader.readLine();
                if(content!=null && content.equals("bye")){
                    System.out.println("对方请求关闭连接,无法继续进行聊天");
                    reader.close();
                    socket.close();
                    break;
                }
                String message =socket.getPort()+":"+content;
                //广播信息
                for(Socket n:this.nodes){
                    if(n !=this.socket){
                        BufferedWriter writer=new BufferedWriter(new OutputStreamWriter(n.getOutputStream(),"UTF-8"));
                        writer.write(message);
                        writer.newLine();
                        writer.flush();        
                    }
                }
            }
        } catch (IOException e) {            
            e.printStackTrace();
        }
        
    }

}

1.3消息发送服务端 ServerMessageSender.java

主要作用:发送欢迎信息

package com.example.socket.server;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.net.Socket;
public class ServerMessageSender extends Thread{
    private Socket socket;

    public ServerMessageSender(Socket socket) {
        this.socket = socket;
    }

    @Override
    public void run() {
        try {
            BufferedWriter writer=new BufferedWriter(new OutputStreamWriter(socket.getOutputStream(),"UTF-8"));
//            BufferedReader inputReader=new BufferedReader(new InputStreamReader(System.in));
            try {
                String msg="server :welcome "+socket.getPort();
                writer.write(msg);
                writer.newLine();
                writer.flush();
            } catch (IOException e) {
                e.printStackTrace();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

1.4 客户端 Client.java

package com.example.socket.client;
import java.net.InetAddress;
import java.net.Socket;

public class Client {
    // 监听端口号
    private static final int port = 9999;
    // 绑定到本机的IP地址
    private static final String bindAddr = "127.0.0.1";

    public static void main(String[] args) {
        try {
            System.out.println("正在连接Socket服务器");
            Socket socket=new Socket(InetAddress.getByName(bindAddr),port);
            System.out.println("已连接\n==================================");
            new ClientMessageSender(socket).start();
            new ClientMessageReceiver(socket).start();
        } catch (Exception e) {
            e.printStackTrace();
        }
        
    }
}

1.4 消息接收客户端  ClientMessageReceiver.java

仅仅是输出

package com.example.socket.client;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.Socket;

public class ClientMessageReceiver extends Thread {
    
    private Socket socket;
    
    public ClientMessageReceiver(Socket socket) {
        this.socket=socket;    
    }

    @Override
    public void run() {
        try {
            // 获取socket的输 出\入流
            BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream(), "UTF-8"));
            //接收到的消息
            String content;
            while (true) {
                if(socket.isClosed()){
                    System.out.println("Socket已关闭,无法获取消息");
                    reader.close();
                    socket.close();
                    break;
                }
                content=reader.readLine();
                if(content.equals("bye")){
                    System.out.println("对方请求关闭连接,无法继续进行聊天");
                    reader.close();
                    socket.close();
                    break;
                }
                System.out.println(content+"\n");
            }
            reader.close();
            socket.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    
}

1.5 消息发送客户端 ClientMessageSender.java

通过输入流输入,将信息传入Socket

package com.example.socket.client;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.Socket;

public class ClientMessageSender extends Thread {
    
    private Socket socket;

    public ClientMessageSender(Socket socket) {
        this.socket = socket;
    }

    @Override
    public void run() {
        try {
            BufferedWriter writer=new BufferedWriter(new OutputStreamWriter(socket.getOutputStream(),"UTF-8"));
            BufferedReader inputReader=new BufferedReader(new InputStreamReader(System.in));
            try {
                String msg;
                for(;;){
                    msg=inputReader.readLine();
                    if(msg.toLowerCase().equals("exit")){
                        System.exit(0);
                    }
                    if(socket.isClosed()){
                        System.out.println("Socket已关闭,无法发送消息");
                        writer.close();
                        socket.close();
                        break;
                    }
                    writer.write(msg);
                    writer.newLine();
                    writer.flush();
                    System.out.println();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    
}

1.6 效果

简易版聊天系统实现  Socket VS NIO两种实现方式

2.NIO方式实现简易聊天效果

2.1服务端 NServer.java

package com.example.nio;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.Channel;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.nio.charset.Charset;


public class NServer {
    private Selector selector;
    private Charset charset = Charset.forName("UTF-8");
    
    public void init() throws Exception {
        selector = Selector.open();
        ServerSocketChannel server = ServerSocketChannel.open();
        InetSocketAddress isa = new InetSocketAddress("127.0.0.1", 3000);
        server.socket().bind(isa);
        server.configureBlocking(false);
        server.reGISter(selector, SelectionKey.OP_ACCEPT);
        while (selector.select() > 0) {
            for (SelectionKey key : selector.selectedKeys()) {
                selector.selectedKeys().remove(key);
                if (key.isAcceptable()) {
                    SocketChannel sc = server.accept();
                    System.out.println("create new session from "+sc.getRemoteAddress()+"\n");
                    sc.configureBlocking(false);
                    sc.register(selector, SelectionKey.OP_READ);
                    key.interestOps(SelectionKey.OP_ACCEPT);        
                    sc.write(charset.encode("welcome"+sc.getRemoteAddress()));
                }
                
                if (key.isReadable()) {
                    SocketChannel sc = (SocketChannel)key.channel();
                    ByteBuffer buff = ByteBuffer.allocate(1024);
                    String content = "";
                    try {
                        while (sc.read(buff) > 0) {
                            buff.flip();
                            content += charset.decode(buff);
                            buff.clear();
                        }                        
                        key.interestOps(SelectionKey.OP_READ);
                    } catch (IOException e) {
                        key.cancel();
                        if (key.channel() != null)
                            key.channel().close();
                    }
                    if (content.length() > 0) {
                        for (SelectionKey sk : selector.keys()) {
                            Channel targetchannel = sk.channel();
                           if (targetchannel instanceof SocketChannel && targetchannel!=sc) {
                                SocketChannel dest = (SocketChannel)targetchannel;
                                dest.write(charset.encode(content));
                            }
                        }
                    }
                }
            }
        }
    }
    
    public static void main(String[] args) throws Exception {
        new NServer().init();
    }
}

2.2 客户端 NClient.java

package com.example.nio;
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.nio.charset.Charset;
import java.util.Scanner;


public class NClient {
    private Selector selector;
    private Charset charset = Charset.forName("UTF-8");
    private SocketChannel sc = null;
    
    public void init() throws IOException {
        selector = Selector.open();
        InetSocketAddress isa = new InetSocketAddress("127.0.0.1", 3000);
        sc = SocketChannel.open(isa);
        sc.configureBlocking(false);
        sc.register(selector, SelectionKey.OP_READ);
        new ClientThread().start();
        @SuppressWarnings("resource")
        Scanner scan = new Scanner(System.in);
        while (scan.hasNextLine()) {
            sc.write(charset.encode(scan.nextLine()));
        }
    }
    
    private class ClientThread extends Thread {
        public void run() {
            try {
                while (selector.select() > 0) {
                    for (SelectionKey sk : selector.selectedKeys()) {
                        selector.selectedKeys().remove(sk);
                        if (sk.isReadable()) {
                            SocketChannel sc = (SocketChannel)sk.channel();
                            ByteBuffer buff = ByteBuffer.allocate(1024);
                            String content = "";
                            while (sc.read(buff) > 0) {
                                sc.read(buff);
                                buff.flip();
                                content += charset.decode(buff);
                                buff.clear();
                            }
                            System.out.println("chat info: " + content);
                            sk.interestOps(SelectionKey.OP_READ);
                        }
                    }
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    
    public static void main(String[] args) throws IOException {
        new NClient().init();
    }
}

代码来自

https://GitHub.com/xeostream/chat
2.3 效果

简易版聊天系统实现  Socket VS NIO两种实现方式


3. 对比

api操作上来看,NIO偏复杂,面向的是异步编程方式,重点围绕Selector,SelecTKEy操作。

性能对比,主要简单模拟下Echo情景:客户端连接成功,服务端返回一条信息。

3.1Socket性能测试入口

可以关闭ServerMessageReceiver线程

package com.example.socket.client;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

public class BenchmarkClient {
    // 监听端口号
    private static final int port = 9999;
    // 绑定到本机的IP地址
    private static final String bindAddr = "127.0.0.1";

    
    public static <T> void main(String[] args) {
        try {
            long s=System.currentTimeMillis();
            for (int i = 0; i < 1000; i++) {
                final Socket socket = new Socket(
                        InetAddress.getByName(bindAddr), port);
                Future<String> future = Executors.newFixedThreadPool(4).submit(
                        new Callable<String>() {
                            @Override
                            public String call() throws Exception {
                                BufferedReader reader = new BufferedReader(
                                        new InputStreamReader(socket
                                                .getInputStream(), "UTF-8"));
                                String content = reader.readLine();
                                return Thread.currentThread().getName()+"--->"+content;
                            }
                        });                
                System.out.println(i+":"+future.get());
                socket.close();
            }
            long e=System.currentTimeMillis();
            System.out.println(e-s);
        } catch (UnknownHostException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }

    }

}

3.2 NIO性能测试入口

package com.example.nio;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
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.nio.charset.Charset;
import java.util.Scanner;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;


public class BenchMarkNClient {
    private Selector selector;
    private Charset charset = Charset.forName("UTF-8");
    private SocketChannel sc = null;
    
    public void init() throws IOException {
        long s = System.currentTimeMillis();
        selector = Selector.open();
        InetSocketAddress isa = new InetSocketAddress("127.0.0.1", 3000);
        for (int i = 0; i < 10000; i++) {    
            sc = SocketChannel.open(isa);
            sc.configureBlocking(false);
            sc.register(selector, SelectionKey.OP_READ);
            Future<String> future = Executors.newFixedThreadPool(4).submit(new ClientTask());
            try {
                System.out.println(i+":"+future.get());
            } catch (InterruptedException e) { 
                e.printStackTrace();
            } catch (ExecutionException e) {
                e.printStackTrace();
            }
        }
        long e= System.currentTimeMillis();
        System.out.println(e-s);
    }
    
    private class ClientTask implements Callable<String> {
        public String call() {
            try {
                while (selector.select() > 0) {
                    for (SelectionKey sk : selector.selectedKeys()) {
                        selector.selectedKeys().remove(sk);
                        if (sk.isReadable()) {
                            SocketChannel sc = (SocketChannel)sk.channel();
                            ByteBuffer buff = ByteBuffer.allocate(1024);
                            String content = "";
                            while (sc.read(buff) > 0) {
                                sc.read(buff);
                                buff.flip();
                                content += charset.decode(buff);
                                buff.clear();
                            }

                            sk.interestOps(SelectionKey.OP_READ);
                            return content;
                        }
                    }
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
            return null;
        }
    }
    
    public static void main(String[] args) throws IOException {
        new BenchMarkNClient().init();
    }
}

3.3 性能对比

次数
NIO
SOCKET(ms)
1000
525
637
2000
14111215
2000(休眠时间为100毫秒)
205928206313
5000
6731
2976

次数较少时,NIO性能较好。但随着次数增加,性能下降非常厉害。(存疑)

当通讯时间变长时,发现NIO性能又相对提高了。

可见一个技术的好坏,是和业务场景分不开的。

您可能感兴趣的文档:

--结束END--

本文标题: 简易版聊天系统实现 Socket VS NIO两种实现方式

本文链接: https://lsjlt.com/news/44660.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • 简易版聊天系统实现 Socket VS NIO两种实现方式
    说是简单聊天系统,压根不能算是一个系统,顶多算个雏形。本文重点不在聊天系统设计和实现上,而是通过实现类似效果,展示下NIO 和Socket两种编程方式的差异性。说是Socket与NIO的编程方式,不太严谨,...
    99+
    2024-04-02
  • Java Socket实现简易聊天室
    Java-Socket编程实现简易聊天室(TCP),供大家参考,具体内容如下 实现一个服务器接收多个客户端 测试: 首先启动服务器,然后启动三个客户端,输入三个不同的用户名,分别在聊...
    99+
    2024-04-02
  • Java NIO实现聊天系统
    使用Java的NIO写的一个小的聊天系统,供大家参考,具体内容如下 一、服务端 public class GroupChatServer { // 定义相关的属性 ...
    99+
    2024-04-02
  • node+socket实现简易聊天室功能
    本文实例为大家分享了node+socket实现简易聊天室的具体代码,供大家参考,具体内容如下 服务端 const net = require('net') const serv...
    99+
    2024-04-02
  • Java Socket怎样实现简易聊天室
    这篇文章主要介绍了Java Socket怎样实现简易聊天室,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。Java-Socket编程实现简易聊天室(TCP),具体内容如下实现一...
    99+
    2023-06-14
  • java+socket实现简易局域网聊天室
    本文实例为大家分享了java+socket实现简易局域网聊天室的具体代码,供大家参考,具体内容如下 服务器端 ServerFrame.java package com.eze.cha...
    99+
    2024-04-02
  • Java Socket实现多人聊天系统
    本文实例为大家分享了Java Socket实现多人聊天系统的具体代码,供大家参考,具体内容如下 前言 GitHub地址 开发环境:Eclipse Java 2019-06 注意:本项...
    99+
    2024-04-02
  • html+css+js实现简易版ChatGPT聊天机器人
    OpenAI的一款聊天机器人模型ChatGPT爆火,本篇文章用一百行html+css+js代码给大家制作一款简易的聊天机器人。 <!DOCTYPE html> <h...
    99+
    2023-02-25
    html+css+js实现聊天 简易版ChatGPT聊天机器人
  • WebSocket实现简单客服聊天系统
    一 需求 一个多商家的电商系统,比如京东商城,不同商家之间的客服是不同的,所面对的用户也是不同的。要实现这样一个电商系统的客服聊天系统,那该系统就必须是一个支持多客服、客服一对多用户的聊天系统。 二 思路 ...
    99+
    2022-06-04
    客服 简单 系统
  • C++实现图书管理系统简易版
    本文实例为大家分享了C++实现图书管理系统的具体代码,供大家参考,具体内容如下 包括管理员端和学生端,可以对图书进行借阅,归还,还可以修改账号登陆密码等 #include<io...
    99+
    2024-04-02
  • 怎么使用html+css+js实现简易版ChatGPT聊天机器人
    本篇内容介绍了“怎么使用html+css+js实现简易版ChatGPT聊天机器人”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!代码如下:&l...
    99+
    2023-07-05
  • C++实现图书管理系统简易版的方法
    本文小编为大家详细介绍“C++实现图书管理系统简易版的方法”,内容详细,步骤清晰,细节处理妥当,希望这篇“C++实现图书管理系统简易版的方法”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。包括管理员端和学生端,可以...
    99+
    2023-06-29
  • Java实现简易版的【图书管理系统】
    目录 🌎1.分析图书管理系统的功能 🌍 2.在IDEA中进行功能类的创建 🦄2.1  创建一个名为book的包,里面存放书相关的 🦄 2.2 创建一个名为Operation...
    99+
    2023-09-11
    java
  • JavaScript实现简易计算器功能的两种方法
    本文实例为大家分享了两种JavaScript实现简易计算器功能的具体代码,供大家参考,具体内容如下 1. 使用基本数据类型 注意点: prompt 用户从浏览器输入的任何数据都是字符...
    99+
    2024-04-02
  • Java模仿微信实现零钱通简易功能(两种版本)
    目录1. 需求描述2. 需求分析3. 实现零钱通主要功能3.1 写一个菜单3.2 零钱通明细3.3 收益入账3.4 消费3.5 用户退出改进3.6 改进金额判断4. 面向过程版实现5...
    99+
    2024-04-02
  • 基于UDP协议实现聊天系统的方法
    这篇文章给大家分享的是有关基于UDP协议实现聊天系统的方法的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。代码展示下面展示一些 Client类。class Client extends ...
    99+
    2023-06-14
  • python实现简易五子棋小游戏(三种方式)
    tkinter库:Python的标准Tk GUI工具包的接口 示例: from tkinter import *root = Tk()#你的ui代码Label(root,text = 'hello world!').pack()root.m...
    99+
    2023-09-07
    python numpy 开发语言 pygame visual studio
  • Android实现跑马灯效果的两种简单方式
    第一种:较简单,但是局限性强,貌似只能从右至左跑,且有一个要求:字体的长度需大于控件的长度, 不然没有效果,重要的代码为深色部分,具体代码在文章最后。 重要代码的介绍 1. and...
    99+
    2024-04-02
  • 怎么使用Java实现简易版的图书管理系统
    这篇文章主要介绍“怎么使用Java实现简易版的图书管理系统”,在日常操作中,相信很多人在怎么使用Java实现简易版的图书管理系统问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”怎么使用Java实现简易版的图书管...
    99+
    2023-07-02
  • Python简单实现安全开关文件的两种方式
    本文实例讲述了Python简单实现安全开关文件的两种方式。分享给大家供大家参考,具体如下: 以下代码经Python3.3测试。 方式1: try: file = open('config.ini',...
    99+
    2022-06-04
    两种 简单 方式
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作