Java BIO实现聊天程序

本文实例为大家分享了Java BIO实现聊天程序的具体代码,供大家参考,具体内容如下

我们使用一个聊天程序来说本文的主题

1、BIO 客户端服务器通讯

public class ChatServer {
    public static void main(String[] args) throws IOException {
        ServerSocket serverSocket = new ServerSocket(9000);
        while (true) {
            try {
                System.out.println("聊天服务已启动,等待客户连接....");
                Socket socket = serverSocket.accept();
                System.out.printf("建立了与%s的连接!\n",socket.getRemoteSocketAddress());
                loopReadRequest(socket);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    public static String loopReadRequest(Socket socket) throws IOException {
        InputStreamReader reader = new InputStreamReader(socket.getInputStream());
        StringBuilder sb = new StringBuilder();
        char[] cbuf = new char[256];

        // 循环读取socket的输入数据流
        while (true) {
            // read方法,读出内容写入 char 数组,read 方法会一直阻塞
            // 直到有输入内容 或 发生I/O错误 或 输入流结束(对方关闭了socket)
            // 正常读取时方法会返回读取的字符数,当输入流结束时(对方关闭了socket)方法返回 -1
            int readed = reader.read(cbuf);
   SocketAddress remoteSocketAddress = socket.getRemoteSocketAddress();
   // 客户端执行了socket.close()
            if (readed == -1) {
                System.out.println(remoteSocketAddress + " 断开了连接!");
                reader.close();
                socket.close();
                break;
            }

            String readedStr = new String(cbuf, 0, readed);
            sb.append(readedStr);

      // ready()用来判断流是否可被读取,如果reader缓冲区不是空则返回true,否则返回false
            if (!reader.ready()) {//reader缓冲区为空,表示数据流已读完
                // 数据流已读完,此时向客户端发送响应
                socket.getOutputStream().write((remoteSocketAddress+"你好,"+sb+"已收到").getBytes());
                System.out.println("收到内容:"+sb);
                // 清除sb的内容,准备接收下一个请求内容
                sb.setLength(0);
                System.out.println("等待客户端消息....");
            }
        }
        return sb.toString();
    }
}

public class ChatClient {
    public static void main(String[] args) {
        try {
            Socket socket = new Socket("localhost", 9000);
            Scanner scanner = new Scanner(System.in);
            while (true) {
                System.out.print(">");
                String line = scanner.nextLine();
                if("".equals(line)){
                    continue;
                }
                if ("quit".equals(line)) {
                    scanner.close();
                    socket.close();
                    break;
                }
                socket.getOutputStream().write(line.getBytes());
                System.out.println(readRequest(socket));
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static String readRequest(Socket socket) throws IOException {
        InputStreamReader reader = new InputStreamReader(socket.getInputStream());
        StringBuilder sb = new StringBuilder();
        char[] cbuf = new char[256];
        while (true) {
            int readed = reader.read(cbuf);
            // 读出内容写入 char 数组,read 方法会一直阻塞
            // 直到有输入内容 或 发生I/O错误 或 输入流结束(对方关闭了socket)
            // 正常读取,方法会返回读取的字符数,而当输入流结束(对方关闭了socket)则返回 -1
            if (readed == -1) {
                System.out.println(socket.getRemoteSocketAddress() + " 断开了连接!");
                reader.close();
                socket.close();
                break;
            }

            String readedStr = new String(cbuf, 0, readed);
            sb.append(readedStr);
            if(!reader.ready()){
                break;
            }
        }
        return sb.toString();
    }
}

ChatServer与ChatClient建立了长连接,且ChatServer阻塞等待ChatClient发送消息过来,程序中 Server端只能与一个Client建立连接。程序这么写,只能实现一个客户端和服务端进行通信。

如何支持多个Client的连接呢? 使用独立的线程去读取socket

2、多线程实现单聊,群聊

单聊发送 格式:-c 对方端口号 消息内容, 群聊直接发送信息就可以了,具体发送逻辑看下面的程序

public class ChatServer {
    private static Map<String, Socket> connnectedSockets = new ConcurrentHashMap<>();

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

        // 1、服务端初始化工作
        ServerSocket serverSocket = new ServerSocket(9000);
        ExecutorService executorService = getExecutorService();

        // 2、主线程- 循环阻塞接收新的连接请求
        while (true) {
            Socket socket = serverSocket.accept();
            cacheSocket(socket);

            // 3、一个socket对应一个读取任务,交给线程池中的线程执行
            // 如果使用fixed线程池,会操作读取任务分配不到线程的情况
            // 现象就是发送的消息别人收不到(暂存在Socket缓存中)
            executorService.submit(createLoopReadTask(socket));
        }
    }

    private static Runnable createLoopReadTask(Socket socket) {
        return new Runnable() {
            public void run() {
                try {
                    loopReadRequestAndRedirect(socket);
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        };
    }

    private static ExecutorService getExecutorService() {
        ExecutorService executorService = Executors.newCachedThreadPool();
        int nThreads = Runtime.getRuntime().availableProcessors();
        nThreads = 1;
        // 如果只设置一个线程,那么最先连接进来的客户端可以发送消息
        // 因为程序阻塞读取第一个socket连接的数据流,没有其他线程资源去读后面建立的socket了
        executorService = Executors.newFixedThreadPool(nThreads);
        return executorService;
    }

    private static void cacheSocket(Socket socket) {
        SocketAddress remoteSocketAddress = socket.getRemoteSocketAddress();
        String[] split = remoteSocketAddress.toString().split(":");
        connnectedSockets.put(split[1], socket);
    }

    public static String loopReadRequestAndRedirect(Socket socket) throws IOException {
        InputStreamReader reader = new InputStreamReader(socket.getInputStream());
        StringBuilder sb = new StringBuilder();
        char[] cbuf = new char[256];
        while (true) {
            SocketAddress remoteSocketAddress = socket.getRemoteSocketAddress();
            System.out.println(Thread.currentThread() + "执行 " + remoteSocketAddress + "发送的消息");
            // 读出内容写入 char 数组,read 方法会一直阻塞
            // 直到有输入内容 或 发生I/O错误 或 输入流结束(对方关闭了socket)
            // 正常读取时方法会返回读取的字符数,当输入流结束(对方关闭了socket)时返回 -1
            int readed = reader.read(cbuf);

            if (readed == -1) {
                System.out.println(remoteSocketAddress + " 断开了连接!");
                reader.close();
                socket.close();
                break;
            }

            String readedStr = new String(cbuf, 0, readed);
            sb.append(readedStr);

            //ready()用来判断流是否可被读取,如果reader缓冲区不是空则返回true,否则返回false
            boolean oneReqeustStreamReaded = !reader.ready();
            if (oneReqeustStreamReaded) {
                String requestContent = sb.toString().trim();
                String prifix = requestContent.substring(0, 2);
                // 单聊
                if ("-c".equals(prifix)) {
                    requestContent = requestContent.substring(3);
                    String port = requestContent.substring(0, requestContent.indexOf(" "));
                    requestContent = requestContent.replaceFirst(port, "");
                    sendToOneSocket(connnectedSockets.get(port), requestContent);
                    // 群聊
                } else {
                    // 向客户端发送响应
                    socket.getOutputStream().write(("您发送的消息-'" + sb + "' 已收到").getBytes());
                    sendToAllSocket(sb.toString(), socket);
                }
                sb.setLength(0);
            }
        }
        return sb.toString();
    }

    /**
     * 发送消息给某个socket
     *
     * @param socket
     * @param msg
     */
    private static void sendToOneSocket(Socket socket, String msg) {
        // 对于同一个socket,同一时刻只有一个线程使用它发送消息
        synchronized (socket) {
            try {
                socket.getOutputStream().write(msg.getBytes("UTF-8"));
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 发送消息给所有的socket
     *
     * @param msg
     */
    private static void sendToAllSocket(String msg, Socket selfSocket) {
        for (String key : connnectedSockets.keySet()) {
            Socket socket = connnectedSockets.get(key);
            if (socket.equals(selfSocket)) {
                continue;
            }
            sendToOneSocket(socket, msg);
        }
    }
}

public class ChatClient {
    public static void main(String[] args) throws IOException {
        new ChatClient().start();
    }

    public void start() throws IOException {
        Socket socket = new Socket("localhost", 9000);
        ExecutorService executorService = Executors.newFixedThreadPool(2);

        Runnable readTask = new Runnable() {
            public void run() {
                try {
                    loopReadRequest(socket);
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        };
        executorService.submit(readTask);

        Runnable sendMsgTask = new Runnable() {
            public void run() {
                try {
                    Scanner scanner = new Scanner(System.in);
                    while (true) {
                        System.out.print(">");
                        String line = scanner.nextLine();
                        if ("".equals(line)) {
                            continue;
                        }
                        if ("quit".equals(line)) {
                            scanner.close();
                            socket.close();
                            break;
                        }
                        socket.getOutputStream().write(line.getBytes());
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        };
        executorService.submit(sendMsgTask);

    }

    public void loopReadRequest(Socket socket) throws IOException {
        InputStreamReader reader = new InputStreamReader(socket.getInputStream());
        StringBuilder sb = new StringBuilder();
        char[] cbuf = new char[256];
        while (true) {
            int readed = reader.read(cbuf);
            // 读出内容写入 char 数组,read 方法会一直阻塞
            // 直到有输入内容 或 发生I/O错误 或 输入流结束(对方关闭了socket)
            // 正常读取,方法会返回读取的字符数,而当输入流结束(对方关闭了socket)则返回 -1
            if (readed == -1) {
                System.out.println(socket.getRemoteSocketAddress() + " 断开了连接!");
                reader.close();
                socket.close();
                break;
            }

            String readedStr = new String(cbuf, 0, readed);
            sb.append(readedStr);
            if (!reader.ready()) {
                System.out.println(sb);
                sb.setLength(0);
            }
        }
    }
}

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • java基于netty NIO的简单聊天室的实现

    一.为何要使用netty开发 由于之前已经用Java中的socket写过一版简单的聊天室,这里就不再对聊天室的具体架构进行细致的介绍了,主要关注于使用netty框架重构后带来的改变.对聊天室不了解的同学可以先看下我的博客(<JAVA简单聊天室的实现>) 本篇博客所使用的netty版本为4.1.36,完整工程已上传到Github(https://github.com/Alexlingl/Chatroom),其中lib文件夹下有相应的netty jar包和source包,自行导入即可. 1.为何要

  • java NIO实现简单聊天程序

    本文实例为大家分享了java NIO实现简单聊天程序的具体代码,供大家参考,具体内容如下 服务端 功能: 1.接受客户端连接 2.发送消息 3.读取客户端消息 Server.java public class Server { private Selector selector; private ByteBuffer writeBuffer = ByteBuffer.allocate(1024); private ByteBuffer readBuffer = ByteBuffer.alloca

  • Java NIO实现聊天功能

    本文实例为大家分享了Java NIO实现聊天功能的具体代码,供大家参考,具体内容如下 server code :  package com.tch.test.nio; import java.io.IOException; import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.nio.channels.ClosedChannelException; import java.nio.channels.S

  • Java NIO实现多人聊天室

    本文实例为大家分享了Java NIO实现多人聊天室的具体代码,供大家参考,具体内容如下 1. 服务器端代码 ChatServer类: package nio.test.server; import java.io.Closeable; import java.io.IOException; import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.nio.channels.*; import java.n

  • Java实现NIO聊天室的示例代码(群聊+私聊)

    功能介绍 功能:群聊+私发+上线提醒+下线提醒+查询在线用户 文件 Utils 需要用maven导入下面两个包 <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.16.18</version> </dependency> <dependency> <group

  • Java NIO实现聊天室功能

    本文实例为大家分享了Java NIO实现聊天室功能的具体代码,供大家参考,具体内容如下 代码里面已经包含了必要的注释,这里不详述了.实现了基本的聊天室功能. 常量类: public class Constant { public static final int serverPort = 44444; } 服务端: package server; import java.io.IOException; import java.net.InetSocketAddress; import java.

  • Java NIO实战之聊天室功能详解

    本文实例讲述了Java NIO实战之聊天室功能.分享给大家供大家参考,具体如下: 在工作之余花了两个星期看完了<Java NIO>,总体来说这本书把NIO写的很详细,没有过多的废话,讲的都是重点,只是翻译的中文版看的确实吃力,英文水平太低也没办法,总算也坚持看完了.<Java NIO>这本书的重点在于第四章讲解的"选择器",要理解透还是要反复琢磨推敲:愚钝的我花了大概3天的时间才将NIO的选择器机制理解透并能较熟练的运用,于是便写了这个聊天室程序. 下面直接上代

  • Java NIO实现聊天系统

    使用Java的NIO写的一个小的聊天系统,供大家参考,具体内容如下 一.服务端 /** * 群聊的服端 * * @author :breakpoint/赵立刚 * @date : 2020/08/13 */ public class GroupChatServer { // 定义相关的属性 private Selector selector; private ServerSocketChannel listenChannel; private static final int port = 66

  • Java BIO实现聊天程序

    本文实例为大家分享了Java BIO实现聊天程序的具体代码,供大家参考,具体内容如下 我们使用一个聊天程序来说本文的主题 1.BIO 客户端服务器通讯 public class ChatServer { public static void main(String[] args) throws IOException { ServerSocket serverSocket = new ServerSocket(9000); while (true) { try { System.out.prin

  • java控制台实现聊天程序

    本文实例为大家分享了java控制台实现聊天程序的具体代码,供大家参考,具体内容如下 发送端 package ip.chat; import java.io.BufferedReader; import java.io.InputStreamReader; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.InetSocketAddress; public class TalkSend i

  • Java编写网络聊天程序实验

    本文实例为大家分享了Java编写网络聊天程序的具体代码,供大家参考,具体内容如下 课程名称 高级Java程序设计 实验项目 Java网络编程 实验目的: 使用客户机/服务器模式.基于TCP协议编写一对多“群聊”程序.其中客户机端单击“连接服务器”或“断开连接”按钮,均能即时更新服务器和所有客户机的在线人数和客户名. 实验要求: 设计一对多的网络聊天程序,要求: 1.基于TCP/IP设计聊天程序2.采用图形界面设计3.能够进行一对多聊天 项目截图 服务器端代码: import javax.swin

  • 详解基于java的Socket聊天程序——初始设计(附demo)

    写在前面: 可能是临近期末了,各种课程设计接踵而来,最近在csdn上看到2个一样问答,那就是编写一个基于socket的聊天程序,正好最近刚用socket做了一些事,出于兴趣,自己抽了几个晚上的空闲时间敲了一个,目前仅支持单聊,群聊,文件传送这些功能.首先,贴出一个丑丑的程序图(UI是用java swing写的,这个早就忘光了,无奈看着JDK的API写了一个),如下图:  服务端设计: 服务端主要有两个操作,一是阻塞接收客户端的socket并做响应处理,二是检测客户端的心跳,如果客户端一段时间内没

  • 详解基于java的Socket聊天程序——客户端(附demo)

    写在前面: 上周末抽点时间把自己写的一个简单Socket聊天程序的初始设计和服务端细化设计记录了一下,周二终于等来毕业前考的软考证书,然后接下来就是在加班的日子度过了,今天正好周五,打算把客户端的详细设计和Common模块记录一下,因为这个周末开始就要去忙其他东西了. 设计: 客户端设计主要分成两个部分,分别是socket通讯模块设计和UI相关设计. 客户端socket通讯设计: 这里的设计其实跟服务端的设计差不多,不同的是服务端是接收心跳包,而客户端是发送心跳包,由于客户端只与一个服务端进行通

  • 详解基于java的Socket聊天程序——服务端(附demo)

    写在前面: 昨天在博客记录自己抽空写的一个Socket聊天程序的初始设计,那是这个程序的整体设计,为了完整性,今天把服务端的设计细化记录一下,首页贴出Socket聊天程序的服务端大体设计图,如下图: 功能说明: 服务端主要有两个操作,一是阻塞接收客户端的socket并做响应处理,二是检测客户端的心跳,如果客户端一段时间内没有发送心跳则移除该客户端,由Server创建ServerSocket,然后启动两个线程池去处理这两件事(newFixedThreadPool,newScheduledThrea

  • java实现实时通信聊天程序

    利用TCP传输数据,编写客户端和服务器的程序,实现两个程序间的实时通信. 在每个程序中实现了实时的发送与接收数据的功能. 客户端的io界面 服务器端的io界面 io演示 程序以两端分别输入close字符串作为结束标识.在一端输入close时,终止发送.一端接收到close时,终止接收.所演示的客户端来自个人电脑IDEA调试运行.服务器端来自阿里云服务器centos系统下的jdk环境.该程序需要用到java的 I/O 流.多线程.网络编程的相关知识. 一.客户端 由于发送和接收数据是并行的,为了使

  • java实现简单TCP聊天程序

    本文实例为大家分享了java实现TCP聊天程序的具体代码,供大家参考,具体内容如下 服务端代码: package com.test.server; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import java.net.ServerSocket; import java.net.Socket; public class Server { public

  • java实现基于Tcp的socket聊天程序

    对于步入编程行业不深的初学者或是已经有所领会的人来说,当学习一项新的技术的时候,非常渴望有一个附上注释完整的Demo.本人深有体会,网上的例子多到是很多,但是很杂不完整,写代码这种东西来不得半点马虎,要是错了一点,那也是运行不了的.这对于初学者来说更加的头疼,因为他根本不知道错在哪里,盲目的改只能错上加错.最后不得不去找找看看有没有能够直接运行的例子再加以模仿. 下面是博主在学习Java的socket时写的一个完整的例子,并且带上了完整的注释.它是一个简单的聊天程序,但是它可以设置任意多用户同时

随机推荐