• Java Socket实现简易多人聊天室传输聊天内容或文件


    Java Socket实现简易多人聊天室传输聊天内容或文件

    Java小练手项目:用Java Socket实现多人聊天室,聊天室功能包括传输聊天内容或者文件。相比于其它的聊天室,增加了传输文件的功能供参考。

    模块拆解

    分成服务端和客户端两部分来写

    服务端包括监听线程和处理收发信线程:

    1. 创建监听线程,监听客户端的连接。将每个连接的客户端加入维护的列表,并为每个连接的客户端开启一个处理收发信的线程。
    2. 在每个客户端的收发信线程中,接收每个客户端发回的消息,并对其进行转发到相应接收的客户端上,以此实现多人聊天室。
    3. 添加处理传输文件的判断,通过在传输的字节数组中添加标志位来区分传输的是文本消息,还是文件。

    客户端包括发送消息线程和接收消息线程:

    1. 发送消息线程,用来处理用户的输入信息,判断输入的是文本信息还是文件,并修改传输的字节数组标志位进行区分。最后将信息传输给服务器。
    2. 接收消息线程,用来处理服务器发回的信息,根据标志位判断输入的是文本信息还是文件,并做相应处理。如果是文本信息,则显示在控制台,如果是文件,则保存在指定目录下。

    项目的目录结构如下所示

    在这里插入图片描述

    接下来,给出实际的代码进行分析

    项目代码

    服务器端

    监听线程

    package 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 MultiServer {
        public static void main(String[] args) {
            ServerSocket ss = null;
            Socket s = null;
    
            // 定义一个List列表来保存每个客户端,每新建一个客户端连接,就添加到List列表里。
            List<Socket> listSocket = new ArrayList<>();
            try {
                // 1. 创建ServerSocket类型的对象并提供端口号
                ss = new ServerSocket(9999);
                // 2. 等待客户端的连接请求,调用accept方法
                // 采用多线程的方式,允许多个用户请求连接。
                int i = 0;
                while (true) {
                    System.out.println("等待客户端的连接请求...");
                    s = ss.accept();
                    listSocket.add(s);
                    //sArr[i] = s;
                    i++;
                    System.out.printf("欢迎用户%d加入群聊!\n", i);
                    System.out.printf("目前群聊中共有%d人\n", listSocket.size());
                    InetAddress inetAddress = s.getInetAddress();
                    System.out.println("客户端" + inetAddress + "连接成功!");
                    // 调用多线程方法,每一个连上的客户端,服务器都有一个线程为之服务
                    new MultiServerThread(s, inetAddress, listSocket).start();
                }
    
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                // 关闭流
                try {
                    ss.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
                try {
                    s.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
    
    
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56

    上述代码实现服务器监听客户端连接,利用accept方法,每加入一个客户端,服务器都创建一个线程为之服务,同时将其加入一个List集合中,用来保存已加入聊天室的所有客户端。

    处理收发信的线程

    package Server;
    
    import java.io.BufferedInputStream;
    import java.io.BufferedOutputStream;
    import java.io.IOException;
    import java.net.InetAddress;
    import java.net.Socket;
    import java.util.Arrays;
    import java.util.List;
    
    public class MultiServerThread extends Thread {
        private Socket s;
        private InetAddress inetAddress;
        private List<Socket> listSockets;
    
        public MultiServerThread(Socket s, InetAddress inetAddress, List<Socket> listSockets) {
            this.s = s;
            this.inetAddress = inetAddress;
            this.listSockets = listSockets;
        }
    
        public void BroadCast(Socket s, byte[] by, int res)
        {
            // 将服务器接收到的消息发送给除了发送方以外的其他客户端
            int i = 0;
            for (Socket socket: listSockets)
            {
                if (s!=socket)  // 判断不是当前发送的客户端
                {
                    System.out.println("发送给用户: " + listSockets.indexOf(socket));
                    BufferedOutputStream ps = null;
                    try {
                        ps = new BufferedOutputStream(socket.getOutputStream());
                        ps.write(by, 0, res);   // 写入输出流,将内容发送给客户端的输入流
                        ps.flush();
    
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    
        // 服务器与客户端的交互线程
        @Override
        public void run() {
            BufferedInputStream ois = null;
            BufferedOutputStream oos = null;
            try {
                ois = new BufferedInputStream(s.getInputStream());
    
                oos = new BufferedOutputStream(s.getOutputStream());
    
                int i = 0;
                while (true) {
                    //System.out.println("进入MultiChatServerThread");
                    byte[] by = new byte[1024+2];
                    //System.out.println("by.length: " + by.length);
                    int res = 0;
                    res = ois.read(by);
                    // 对读取到的字节数组第一位位置进行修改,标识该数据流是由哪个用户发送来的
                    by[0] = (byte)listSockets.indexOf(s);
    
                    if (by[1] == 2){
                        // 因为前两个位置是标志位,所以length的大小为读取的字节数-2,同时offset也从第三个位置(下标是2)开始读
                        String receive = new String(by, 2, res-2);
    
                        if (receive.equalsIgnoreCase("bye"))
                        {
                            // 如果客户端发送的是bye, 说明其下线,则从listSockets里删除对应的socket.
                            oos.write(receive.getBytes());  // 把bye给客户端的读取线程,从而可以关闭掉读取线程
                            oos.flush();
                            System.out.printf("用户%d下线, ", listSockets.indexOf(s));
                            listSockets.remove(s);
                            System.out.printf("目前聊天室仍有%d人\n", listSockets.size());
                        }
                    }
                    System.out.println("i" + i + "res = " + res);
                    System.out.println("by.length: " + by.length);
                    System.out.println("Socket[]: " + Arrays.toString(listSockets.toArray()));
                    // 调用函数,将接受到的消息发送给所有客户端
                    BroadCast(s, by, res);
    
                }
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                try {
                    if (oos != null) {
                        oos.close();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
                try {
                    if (ois != null) {
                        ois.close();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
    
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106

    在处理收发信的线程中,利用BroadCast()方法将服务器接收到的消息发送给除了发送方以外的其他客户端。

    run()方法中,创建字节数组来接收客户端发送的数据流。定义字节数组时,byte[] by = new byte[1024+2];这里+2的原因是,为了区分发送的用户以及传输的数据类型是消息文本还是文件。其中,第一位标志位用来表示用户的id,第二位标志位用1,2来表示发送的是消息还是文件,1表示发送的是消息,2表示发送的是文件。 下面客户端代码时可以更好理解。

    判断用户下线的标志是用户发送bye,说明其下线,则服务端从listSockets里删除对应的socket。同时,将把bye发送给客户端的读取线程,提示其可以关闭掉读取线程。

    以上就是服务端的实现逻辑。整体思路就是:

    1. 首先创建监听线程,接收每个客户端的连接请求,并创建一个List集合保存。
    2. 创建一个处理收发信的线程,即每个客户端发送的聊天内容,都先统一发回给服务器端,再由服务器端进行集中转发给每个客户端。

    客户端

    客户端的两个线程包括发送消息给服务器和读取服务器发送的消息,用主线程和子线程来分别实现。

    客户端1

    package Client;
    
    import java.io.*;
    import java.net.Socket;
    import java.text.SimpleDateFormat;
    import java.util.Date;
    import java.util.Scanner;
    
    public class MultiClient extends Thread {
        private Socket ss;
    
        public MultiClient() {
        }
    
        public MultiClient(Socket ss) {
            this.ss = ss;
        }
    	
        public byte[] reviseArr(byte[] by, int res) {
            byte[] newByteArr = new byte[by.length + 2];
            // 将by字节数组的内容都往后移动两位,即头部的两个位置空出来作为标志位
            for (int i = 0; i < by.length; i++)
            {
                newByteArr[i+2] = by[i];
            }
            return newByteArr;
        }
    	
        // 子线程执行读操作,读取服务端发回的数据
        @Override
        public void run() {
            BufferedInputStream bis = null;
            BufferedOutputStream bosFile = null;    // 与输出文件流相关联
            try {
                bis = new BufferedInputStream(ss.getInputStream());
                //bosFile = new BufferedOutputStream(new FileOutputStream("./directoryTest/src/用户1 IO流的框架图.png"));
                // 等待接收服务器发送回来的消息
                while(true) {
                    byte[] by = new byte[1024+2];
                    int res = bis.read(by);
                    int sendUser = by[0];
                    Date date = new Date();
                    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-mm-dd HH:mm:ss");
                    String format = sdf.format(date);
                    if (by[1] == 1) // 说明传的是文件
                    {
                        //String filePath = String.format("./directoryTest/src/用户%d传送来的IO流的框架图.png", sendUser);
                        bosFile = new BufferedOutputStream(new FileOutputStream("./directoryTest/用户" + sendUser + "-传输的文件.png", true));
                        bosFile.write(by, 2, res-2);
                        bosFile.flush();
                        if (res<1026)   // 说明是最后一次在传送文件,所以传送的字节数才会小于字节数组by的大小
                        {
                            System.out.println("用户" + sendUser + "\t" + format + ":");
                            System.out.printf("用户%d发送的文件传输完成\n", sendUser);
                        }
                    }
                    else    // 说明传输的是聊天内容,则按字符串的形式进行解析
                    {
                        // 利用String构造方法的形式,将字节数组转化成字符串打印出来
                        String receive = new String(by, 2, res);
                        System.out.println("用户" + sendUser + "\t" + format + ":");
                        System.out.println(receive);
                    }
                }
            } catch (IOException e) {
                e.printStackTrace();
            }finally{
                try {
                    bis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    
        public static void main(String[] args) {
            // 主线程执行写操作,发送消息到服务器
            Socket ss = null;
            BufferedOutputStream bos = null;
            BufferedInputStream bis = null;     // 与文件关联的流
            MultiClient mcc = null;
            try {
                ss = new Socket("127.0.0.1", 9999);
                System.out.println("服务器连接成功");
                System.out.println("-----------聊天室-----------");
                bos = new BufferedOutputStream(ss.getOutputStream());
                Scanner sc = new Scanner(System.in);
                mcc = new MultiClient(ss);
                mcc.start();
                byte[] by = new byte[1024];
                int res = 0;
                int i = 0;
                while(true) {
                    // 由用户输入选择执行不同的传输任务
                    // 若用户输入传输文件,则传输指定文件,否则,则正常聊天任务
                    String str = sc.nextLine();
                    if (str.equals("传输文件")) {
                        bis = new BufferedInputStream(new FileInputStream("./directoryTest/壁纸1.png"));
    
                        while ((res = bis.read(by)) != -1) {
                            //System.out.println("i" + i + " res: " + res);
                            byte[] newByteArr = mcc.reviseArr(by, res);;
                            newByteArr[1] = 1;  // 表示第二个位置上的值为1时表示传输的是文件
                            bos.write(newByteArr, 0, res+2);
                            bos.flush();
    
                        }
                    }
                    else{
                        byte[] sb = str.getBytes(); // 转化为字节数组
                        byte[] newByteArr = mcc.reviseArr(sb, sb.length);
                        newByteArr[1] = 2;      // 表示第二个位置上的值为2时表示传输的是聊天内容
                        bos.write(newByteArr);  // 把内容发给服务器
                        bos.flush();
                        if (str.equalsIgnoreCase("bye"))
                        {
                            System.out.println("用户下线!");
                            break;
                        }
                    }
    
                }
            }catch (IOException e) {
                e.printStackTrace();
            }finally {
                try {
                    bos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
                try {
                    mcc.stop();
                    ss.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140

    在客户端实现中,主线程执行写操作,发送消息到服务器,接收键盘的标准输入。在主线程发送消息时,判断用户的输入,如果输入的文本内容是传输文件,则会去读取指定路径下的文件,并利用BufferedOutputStream方法将文件转化为字节缓冲输出流发送。

    这里reviseArr方法,是将读入文件输入流的102大小的字节数组往后移动两位,实现前两位作为标志位,区分用户id和传输数据类型的目的。如果传输的是文件,会在第二个标志位赋1,如果传输的是消息文本,则第二个标志位赋2。

    若用户发送的消息是"bye",则表示用户下线,利用break跳出主线程循环,并在finally中调用mcc.stop()关闭子线程,从而关闭该客户端。

    在子线程中,若用户发送的是文件,则利用字节缓冲输入流BufferedInputStream将文件写入到指定路径中,并在文件名中简易标识发送用户。若接收的字节数组长度小于设定的1026,说明是最后一次在传送文件,在将最后一次文件的输入流写入后,在控制台打印文件传输完成的提示信息。

    以上是客户端1的实现代码,其它客户端的实现代码类似

    客户端2

    package Client;
    
    import java.io.*;
    import java.net.Socket;
    import java.text.SimpleDateFormat;
    import java.util.Date;
    import java.util.Scanner;
    
    public class MultiClient2 extends Thread {
        private Socket ss;
    
        public MultiClient2() {
        }
    
        public MultiClient2(Socket ss) {
            this.ss = ss;
        }
    
        public byte[] reviseArr(byte[] by, int res) {
            byte[] newByteArr = new byte[by.length + 2];
            // 将by字节数组的内容都往后移动两位,即头部的两个位置空出来作为标志位
            for (int i = 0; i < by.length; i++)
            {
                newByteArr[i+2] = by[i];
            }
            return newByteArr;
        }
    
        @Override
        public void run() {
            BufferedInputStream bis = null;
            BufferedOutputStream bosFile = null;    // 与输出文件流相关联
            try {
                bis = new BufferedInputStream(ss.getInputStream());
    
                // 等待接收服务器发送回来的消息
    
                while(true) {
                    byte[] by = new byte[1024+2];
                    int res = bis.read(by);
                    int sendUser = by[0];
                    Date date = new Date();
                    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-mm-dd HH:mm:ss");
                    String format = sdf.format(date);
                    if (by[1] == 1) // 说明传的是文件
                    {
                        //String filePath = String.format("./directoryTest/src/用户%d传送来的IO流的框架图.png", sendUser);
                        bosFile = new BufferedOutputStream(new FileOutputStream("./directoryTest/用户" + sendUser + "-传输的文件.png", true));
                        bosFile.write(by, 2, res-2);
                        bosFile.flush();
                        if (res<1026)   // 说明是最后一次在传送文件,所以传送的字节数才会小于字节数组by的大小
                        {
                            System.out.println("用户" + sendUser + "\t" + format + ":");
                            System.out.printf("用户%d发送的文件传输完成\n", sendUser);
                        }
                    }
                    else    // 说明传输的是聊天内容,则按字符串的形式进行解析
                    {
                        // 利用String构造方法的形式,将字节数组转化成字符串打印出来
                        String receive = new String(by,2, res);
                        System.out.println("用户" + sendUser + "\t" + format + ":");
                        System.out.println(receive);
                    }
    
                }
            } catch (IOException e) {
                e.printStackTrace();
            }finally{
                try {
                    bis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    
        public static void main(String[] args) {
            // 主线程写操作
    
            Socket ss = null;
            BufferedOutputStream bos = null;
            BufferedInputStream bis = null;     // 与文件关联的流
            MultiClient2 mcc = null;
            try {
                ss = new Socket("127.0.0.1", 9999);
                System.out.println("服务器连接成功");
                System.out.println("-----------聊天室-----------");
                bos = new BufferedOutputStream(ss.getOutputStream());
                Scanner sc = new Scanner(System.in);
                mcc = new MultiClient2(ss);
                mcc.start();
                byte[] by = new byte[1024];
                int res = 0;
                int i = 0;
                while(true) {
                    String str = sc.nextLine();
                    if (str.equals("传输文件")) {
                        bis = new BufferedInputStream(new FileInputStream("./directoryTest/壁纸1.png"));
                        while ((res = bis.read(by)) != -1) {
                            i += 1;
                            //System.out.println("i" + i + " res: " + res);
                            byte[] newByteArr = mcc.reviseArr(by, res);;
                            newByteArr[1] = 1;  // 表示第二个位置上的值为1时表示传输的是文件
                            bos.write(newByteArr, 0, res+2);
                            bos.flush();
    
                        }
                    }
                    else{
                        byte[] sb = str.getBytes(); // 转化为字节数组
                        byte[] newByteArr = mcc.reviseArr(sb, sb.length);
                        //System.out.println("newByteArr: " + Arrays.toString(newByteArr));
                        newByteArr[1] = 2;      // 表示第二个位置上的值为2时表示传输的是聊天内容
                        bos.write(newByteArr);  // 把内容发给服务器
                        bos.flush();
                        if (str.equalsIgnoreCase("bye"))
                        {
                            System.out.println("用户下线!");
                            break;
                        }
                    }
    
                }
            }catch (IOException e) {
                e.printStackTrace();
            }finally {
                try {
                    bos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
                try {
                    mcc.stop();
                    ss.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141

    客户端3

    package Client;
    
    import java.io.*;
    import java.net.Socket;
    import java.text.SimpleDateFormat;
    import java.util.Date;
    import java.util.Scanner;
    
    public class MultiClient3 extends Thread {
        private Socket ss;
    
        public MultiClient3() {
        }
    
        public MultiClient3(Socket ss) {
            this.ss = ss;
        }
    
        public byte[] reviseArr(byte[] by, int res) {
            byte[] newByteArr = new byte[by.length + 2];
            // 将by字节数组的内容都往后移动两位,即头部的两个位置空出来作为标志位
            for (int i = 0; i < by.length; i++)
            {
                newByteArr[i+2] = by[i];
            }
            return newByteArr;
        }
    
        @Override
        public void run() {
            BufferedInputStream bis = null;
            BufferedOutputStream bosFile = null;    // 与输出文件流相关联
            try {
                bis = new BufferedInputStream(ss.getInputStream());
                // 等待接收服务器发送回来的消息
                while(true) {
                    byte[] by = new byte[1024+2];
                    int res = bis.read(by);
                    int sendUser = by[0];
                    Date date = new Date();
                    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-mm-dd HH:mm:ss");
                    String format = sdf.format(date);
                    if (by[1] == 1) // 说明传的是文件
                    {
                        bosFile = new BufferedOutputStream(new FileOutputStream("./directoryTest/用户" + sendUser + "-传输的文件.png", true));
                        bosFile.write(by, 2, res-2);
                        bosFile.flush();
                        if (res<1026)   // 说明是最后一次在传送文件,所以传送的字节数才会小于字节数组by的大小
                        {
                            //System.out.println("客户端接收到的信息" + receive);
                            System.out.println("用户" + sendUser + "\t" + format + ":");
                            System.out.printf("用户%d发送的文件传输完成\n", sendUser);
                        }
                    }
                    else    // 说明传输的是聊天内容,则按字符串的形式进行解析
                    {
                        // 利用String构造方法的形式,将字节数组转化成字符串打印出来
                        String receive = new String(by, 2, res);
                        System.out.println("用户" + sendUser + "\t" + format + ":");
                        System.out.println(receive);
                    }
                }
            } catch (IOException e) {
                e.printStackTrace();
            }finally{
                try {
                    bis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    
        public static void main(String[] args) {
            // 主线程写操作
            //MultiClient mc = new MultiClient();
            Socket ss = null;
            BufferedOutputStream bos = null;
            BufferedInputStream bis = null;     // 与文件关联的流
            MultiClient3 mcc = null;
            try {
                ss = new Socket("127.0.0.1", 9999);
                System.out.println("服务器连接成功");
                System.out.println("-----------聊天室-----------");
                bos = new BufferedOutputStream(ss.getOutputStream());
                Scanner sc = new Scanner(System.in);
                mcc = new MultiClient3(ss);
                mcc.start();
                byte[] by = new byte[1024];
                int res = 0;
                int i = 0;
                while(true) {
                    String str = sc.nextLine();
                    if (str.equals("传输文件")) {
                        bis = new BufferedInputStream(new FileInputStream("./directoryTest/壁纸1.png"));
                        while ((res = bis.read(by)) != -1) {
                            byte[] newByteArr = mcc.reviseArr(by, res);;
                            newByteArr[1] = 1;  // 表示第二个位置上的值为1时表示传输的是文件
                            bos.write(newByteArr, 0, res+2);
                            bos.flush();
    
                        }
                    }
                    else{
                        byte[] sb = str.getBytes(); // 转化为字节数组
                        byte[] newByteArr = mcc.reviseArr(sb, sb.length);
                        newByteArr[1] = 2;      // 表示第二个位置上的值为2时表示传输的是聊天内容
                        bos.write(newByteArr);  // 把内容发给服务器
                        bos.flush();
                        // 如果用户输入bye则表示用户下线
                        if (str.equalsIgnoreCase("bye"))
                        {
                            System.out.println("用户下线!");
                            break;
                        }
                    }
    
                }
            }catch (IOException e) {
                e.printStackTrace();
            }finally {
                try {
                    bos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
                try {
                    mcc.stop();
                    ss.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136

    功能展示

    开启服务器和3个客户端,初始状态

    聊天室

    每有一个客户端连接,会打印相应的提示信息

    在这里插入图片描述

    三个客户端连接成功,打印提示信息

    在这里插入图片描述

    接下来,进行聊天功能的展示
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

    可以看到,聊天室里标识出每个用户以及发送的时间和消息,可以实现基本的聊天功能。

    传输文件

    在客户端1输入传输文件
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

    进入到写入的目录下,存在相应的文件:
    在这里插入图片描述

    即实现了聊天室和传输文件的功能,最后客户端1发送bye,该客户端断开连接
    在这里插入图片描述

    
    
    
    
    
    • 1
    • 2
    • 3
    • 4
  • 相关阅读:
    SpringBoot-20-模块开发-404页面
    微信小程序-上拉加载和下拉刷新
    React事件绑定的方式有哪些?区别?
    基于SWAT-MODFLOW地表水与地下水耦合
    vite+vue3+ts中的vue-router基本配置
    Pytest 和 Selenium 的结合的一个例子及注意事项
    24/8/9算法笔记 决策树VS线性回归
    有了这款工具,自动化识别验证码再也不是问题
    Docker(三) 创建Docker镜像
    公共命名空间和输入法
  • 原文地址:https://blog.csdn.net/Urbanears/article/details/126139371