Java语言实战开发 @网络聊天室 (二) 实现管理员反馈信息给用户 | 引用多线程机制 | 添加注册登陆功能

笔者: unirithe
日期: 11/20/2021

上一篇 Java语言实战开发 @网络聊天室 (一) 简单实现用户发消息给后台管理员

运行环境


  • JDK8
  • IDEA

实现效果


在这里插入图片描述

1. 探索服务端接收消息的机制


在上一次的实践中,使用缓冲输入输出流通过套接字进行网络通信,但是目前只能实现使用客户端发送消息给服务端,服务端无法做出回应,这一次就对此问题进行优化。

由于是初次使用ServerSocket 类,有一些机制需要自己探索,比如在Server中这段:


while(true){
Socket accept = serverSocket.accept();
BufferedReader r = new BufferedReader(new InputStreamReader(accept.getInputStream()));

String message;
while((message = r.readLine()) != null)
	System.out.println("收到: " + message);
}

最外层使用一个while循环不断执行,接收客户端套接字的程序,同时根据套接字的字节输入流而创建缓冲输入流,进而读取来自客户端的数据,那么服务端最终会执行多少次 while (true) 呢?
是否在客户端发送一次消息过后,服务端就结束一次while循环了呢?只需一个变量就可以解决这个疑问,测试代码如下:

int i = 0; 
while(true){
	// ... 前面部分和之前一样

	System.out.println("-----" + (++i));
}

接下来先后打开服务端、客户端,并在客户端发送三条测试消息,测试结果如下:
在这里插入图片描述

答案是:while (true) 只执行了一次,而且一直没有结束.。

这意味着服务端主线程一直在执行 while(message = r.rindLine() != null); 的语句。

现在把while 改成 if ,同时把socket的获取放在前面,否则会一直阻塞进程,测试效果如下:

Socket accept = serverSocket.accept();
while(true){
	//...
	if(message=r.rindLine())!=null){
		// ...
	}
}

在这里插入图片描述

成功解决了服务端接受一个客户端的线程阻塞问题,接下来测试两个客户端,通过修改端口类的连接IP实现。

在这里插入图片描述
很明显,由于 服务端获取socket的代码必须放在外面,否则无法一直执行while(true) 的所有内容(线程阻塞),但是在主线程,服务端必须要随时等待客户端发来的消息,此时也有其他客户端连接的可能,所以这里需要引用多线程的机制,使用Java.lang包下的Thread类实现该过程。

2 多线程机制的应用


实现多线程的方法主要有两种

方式一:实现Runnable接口,重写Run方法

class RunnableImpl implements Runnable{

	@Override
	public void run(){
		// ...
	}
}

// 使用通过 new Thread(new RunnableImpl()).start()

方式二:继承Thread类,重写Run方法

class myThread extends Thread{

	@Override
	public void run(){
		// ...
	}
}
// 使用通过 new myThread().start();

它们的区别是:

实现接口方式,还可以继承其他的类,具有更高的扩展性,但只有多线程的run方法。

继承的方式,具有原生Thread的方法,更加全面,但扩展性低,无法再继承第二个类。

当有多个客户端连接服务端时,即多个用户需要管理员进行管理,对于每个用户,管理员需要进行不同的约束,当然也有共性,在程序实现中,给每个用户分配一个线程,这样管理员就不用一一的对用户进行管理,提高了并发性。

现对服务端添加两个线程类,一是负责随时接待连接的客户端,二是针对响应每个客户端的消息。

ServerReadThread.java

该类负责处理一个客户端的消息发送,其中的socket对象是与服务端连接时创建的socket,所以可以根据该对象进行数据通信。

package App;

import java.io.DataInputStream;
import java.io.IOException;
import java.net.Socket;

public class ServerReadThread extends Thread{
    private Socket socket;
    private DataInputStream in;
    public ServerReadThread(Socket socket){
        this.socket = socket;
        try {
            in = new DataInputStream(socket.getInputStream());
        } catch (IOException e) {
            System.out.println("获取套接字输入流对象失败");
        }
    }
    @Override
    public void run() {
        while (true) {
            String info;
            try {
                if ((info = in.readUTF()) != null)
                    System.out.println(info);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

ServerReadyThread.java

该类负责处理每个客户端的连接,其中的serverSocket对象是创建服务端时产生的套接字,通过该对象可以调用accept()方法接收到连接的客户端套接字。

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;

public class ServerReadyThread extends Thread{
    private ServerSocket serverSocket;
    public ServerReadyThread(ServerSocket serverSocket){
        this.serverSocket = serverSocket;
    }
    @Override
    public void run() {
        while (true){
            try {
                Socket accept = serverSocket.accept();
                new ServerReadThread(accept).start();
                Server.list_socket.add(accept);
            } catch (IOException e) {
                e.printStackTrace();
            }

        }
    }
}

Server.java

该类是服务端的主要实现类,采用多线程过后,主线程可以不用while(true)循环,

其中的 list_socket 存储当前连接的所有客户端套接字,在客户端连接时会添加进来,这样方便主线程可以对客户端进行消息反馈。

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;

public class Server {
    private final static int port = 6666;
    public final static ArrayList<Socket> list_socket = new ArrayList<>(5);

    public static void main(String[] args) throws IOException {
        System.out.println("服务端启动");
        int i = 0;
        ServerSocket serverSocket = new ServerSocket(port);
        // 启动负责连接的线程
        new ServerReadyThread(serverSocket).start();
    }
}

3. 服务端的反馈机制


通过多线程机制,现在服务端的主线程已经可以继续执行其他的操作,比如再添加更多的多线程。

之前所有客户端套接字存储在了 list_sockets对象里,既然是相通的套接字,那么服务端同样可以发送消息给出去,只要客户端也做相应的接收即可。

再次使用多线程机制,这次在客户端类设计多线程负责接收来自服务端的消息,同时在服务端添加发送消息的代码。

Server.java

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketAddress;
import java.util.ArrayList;
import java.util.Scanner;

public class Server {
    private final static int port = 6666;
    public final static ArrayList<Socket> list_socket = new ArrayList<>(5);
    public static Scanner in = new Scanner(System.in);
    public static void main(String[] args) throws IOException {
        System.out.println("服务端启动");
        int i = 0;
        ServerSocket serverSocket = new ServerSocket(port);
        // 启动负责连接的线程
        new ServerReadyThread(serverSocket).start();
        tips();
        while (true) {
            String operation = in.nextLine();
            switch (operation){
                case "1":
                    sendMsgToClient();
                    break;
                default:
                    System.out.print(">> 不支持当前指令,请重新输入\n>> ");
            }
            tips();
        }
    }
    public static void tips(){
        System.out.println("==========欢迎访问服务端========");
        System.out.println("|      现在可进行的操作如下     |");
        System.out.println("|   1. 向指定客户端发送消息     |");
        System.out.println("================================");
        System.out.printf(">> ");
    }
    public static void sendMsgToClient(){
        showAllClient();
        System.out.print(">> 请选择要发送到的客户端(e可取消操作)\n>> ");
        boolean flag = true;
        while (flag) {
            String choice = in.nextLine();
            switch (choice) {
                case "e":
                    flag = false;
                    break;
                default:
                    int i = Integer.parseInt(choice) -1;
                    if(i < list_socket.size()){
                        System.out.print(">> 选择成功, 接下来请输入要发送的内容\n>> ");
                        try {
                            DataOutputStream out = new DataOutputStream(list_socket.get(i).getOutputStream());
                            out.writeUTF(in.nextLine());
                            System.out.println(">> 消息发送成功!");
                            flag = false;
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    } else{
                        System.out.println("选择的客户端不存在, 当前客户端总数目为: " + list_socket.size());
                    }

            }
        }
    }
    public static void showAllClient(){
        for (int i = 0; i < list_socket.size(); i++) {
            SocketAddress name = list_socket.get(i).getRemoteSocketAddress();
            System.out.printf("%d号客户端:%s\n", (i+1), name);
        }
    }
}

ClientReadServer.java
该类负责处理客户端接收套接字的消息

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;

public class ClientReadThread extends Thread{
    private Socket socket;
    private DataInputStream in;
    public ClientReadThread(Socket socket){
        this.socket = socket;
        try {
            in = new DataInputStream(socket.getInputStream());
        } catch (IOException e) {
            System.out.println("获取套接字输出流失败.");
        }
    }
    @Override
    public void run() {
        while(true){
            String info;
            try {
                if((info = in.readUTF())!=null)
                    System.out.println("服务端发送了:" + info);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

最后需要在 Client.java中添加 new ClientReadThread(socket).start();确保线程启动。

测试结果:
在这里插入图片描述

4. 实现注册登陆功能


通过上面的测试,可以发现在客户端发送消息时,并不知道自己的名称,这显然不符合用户的定义,聊天工具一般都有个人的账号系统,所以在服务端和客户端连接前,最好指定一个登陆状态对象,并且服务端可以通过该对象读取已连接客户端的信息,类似于对上线的用户进行的操作。

PersonInfo.java 用户信息实体类

存储管理员和用户两个身份的信息,主要包括登陆ID、登陆密码和身份

public class PersonInfo {
    private String id;
    private String pw;
    private String identity;
    private boolean isLogin = false;
    public PersonInfo() {};

    public PersonInfo(String id, String pw, String identity) {
        this.id = id;
        this.pw = pw;
        this.identity = identity;
    }

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getPw() {
        return pw;
    }

    public void setPw(String pw) {
        this.pw = pw;
    }

    public String getIdentity() {
        return identity;
    }

    public void setIdentity(String identity) {
        this.identity = identity;
    }

    public void setLogin(boolean x){
        this.isLogin = x;
    }
}

Login.java 登陆功能实现类

该类可以通过判断账号ID与密码是否匹配而实现登陆,若账号不存在则可以直接创建,若密码错误则提示重新输入,代码看起来比较简洁,因为大多是调用自定义的数据工具类DataUtils.java的里的静态方法

import java.io.*;
import java.util.ArrayList;
import java.util.Scanner;

public class Login {
    public static Scanner in = new Scanner(System.in);

    public static PersonInfo go() {
        System.out.println("========登陆系统=======");
        PersonInfo user = new PersonInfo(null, null, "admin");
        boolean needLogin = true;
        while (needLogin) {
            System.out.print("id>> ");  user.setId(in.nextLine()) ;
            System.out.print("pw>> ");  user.setPw(in.nextLine());
            if (DataUtils.checkPersonInfo(user) != null) {
                System.out.println(">> 登陆成功!");
                return user;
            } else if (DataUtils.checkId(user.getId()) == false) {
                System.out.println(">> 密码错误");
            } else {
                System.out.println(">> 该用户不存在,是否创建?(y/n)");
                switch (in.nextLine()) {
                    case "y": ;
                        DataUtils.writeRegister(user);
                        System.out.println(">> 已登陆.");
                        needLogin = false;
                        break;
                    default:
                        System.out.println(">> 请重新登陆");
                        break;
                }
            }
        }
        return user;
    }
}

DataUtils.java 数据工具类

该类存储了所有的登陆用户的信息、以及连接的所有套接字,并提供一些相关的操作,比如读取注册用户,写入用户,验证登陆等。

import java.io.*;
import java.net.Socket;
import java.net.SocketAddress;
import java.util.ArrayList;

public class DataUtils {
    // 变量: 存储所有的注册用户 (包括管理员下同)
    private final static ArrayList<PersonInfo> list_origin_info = new ArrayList<>();
    // 变量: 存储所有上线的用户
    private final static ArrayList<PersonInfo> list_online_info = new ArrayList<>();
    // 变量: 存储所有的客户端与服务端的套接字
    public final static ArrayList<Socket> list_socket = new ArrayList<>(5);
    // 功能: 从本地读取所有的注册用户
    public static void readRegister(){
        try {
            BufferedReader in = new BufferedReader(new FileReader("personInfo.txt"));
            String info;
            while ((info = in.readLine()) != null) {
                String[] split = info.split(",");
                if (split != null && split.length == 3)
                    list_origin_info.add(new PersonInfo(split[0], split[1], split[2]));
            }
        } catch (IOException e) { e.printStackTrace();}
    }
    // 功能: 返回所有注册用户
    public static ArrayList<PersonInfo> getOriginInfo() {return list_origin_info; }
    // 功能: 将新用户写入配置文件
    public static void writeRegister(PersonInfo p){
        try {
            BufferedWriter out = new BufferedWriter(new FileWriter("personInfo.txt", true));
            out.write(p.getId() + "," + p.getPw() + "," + p.getIdentity() + "\n");
            out.flush();
            p.setLogin(true);
            list_online_info.add(p);
            System.out.println(p.getId() + " 注册成功!");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    // 功能: 返回所有线上用户
    public static ArrayList<PersonInfo> getOnlienInfo() {return list_online_info; }
    // 功能: 检查ID是否存在
    public static boolean checkId(String id){
        for (PersonInfo personInfo : list_origin_info) {
            if (id.equals(personInfo.getId()))
                return false;
        }
        return true;
    }
    // 功能: 显示所有客户端的名称
    public static void showAllClient(){
        for (int i = 0; i < list_socket.size(); i++) {
            SocketAddress name =list_socket.get(i).getRemoteSocketAddress();
            System.out.printf("%d号客户端:%s\n", (i+1), name);
        }
    }
    // 功能: 检查用户是否存在,即验证登陆正确性
    public static PersonInfo checkPersonInfo(PersonInfo user){
        readRegister();

        String id = user.getId();
        String pw = user.getPw();
        String identity = user.getIdentity();

        for (PersonInfo p : DataUtils.getOriginInfo()) {
            if (id.equals(p.getId()) && pw.equals(p.getPw()) &&
                    identity.equals(p.getIdentity())) {
                PersonInfo pi = new PersonInfo(id, pw, identity);
                pi.setLogin(true);
                return pi;
            }
        }
        return null;
    }
}

Server.java 服务端类, 部分有修改

public class Server {
   //...
    private static PersonInfo admin = null;

    public static void main(String[] args) throws IOException {
        admin = Login.go();
        //...
        while (true) {
            //...
        }
    }
}

测试效果

在这里插入图片描述

5. 总结 、所有实现代码


5.1 项目结构

项目结构:

在这里插入图片描述
各种类的作用描述表

类名 作用描述
Client 客户端实现类
ClientReadThread 负责客户端接收来自服务端的消息
DataUtils 存储所有的信息结果 ,并包括一些注册登陆相关的操作方法
Login 实现登陆的类
PersonInfo 用户信息实体类
Server 服务端实现类
ServerReadThread 负责服务端接收来自客户端的消息
ServerReadyThread 负责接收来自客户端的套接字对象,接收时会启动一个上面的线程

5.2 存在的不足

  • IO流的选择问题,在上一篇中,笔者用到了 BufferedReader 字符缓冲输入流,适合字符数据的传输,另外,DataInputStream似乎更适合数据的传输,但是又有一个缺点,它必须遵循严格的读写类型,比如若在socket的一端是调用readInt() 那么另一端就必须是 writeInt(),否则会抛出异常,同时还有ObjectInputStream这类输入流存在,可以尝试一番。
  • 项目结构层次比较混乱,没有接口,未体现出多态的特性。
  • 未考虑到多线程机制的安全性问题,目前只是按需求创建线程,以便处理事件,还没有考虑到线程是否能够合理地使用系统资源。

5.3 所有实现代码

Client.java

import java.io.*;

import java.net.Socket;

import java.util.Scanner;
public class Client {

    private static PersonInfo user = null;
    private static Socket socket;
    public static void main(String[] args) throws IOException {
        System.out.println("客户端启动");
        user = Login.go();
        // 连接服务端
        socket = new Socket("localhost", 6666);
        DataOutputStream out = new DataOutputStream(socket.getOutputStream());
        // 启动接收服务端消息的线程
        new ClientReadThread(socket).start();
        Scanner in = new Scanner(System.in);
        out.writeUTF("\n>> " + user.getId() + " 已加入聊天室!");
        while(true){
            System.out.print(user.getId() + " >> ");
            String info = in.nextLine();
            out.writeUTF("\n>> 收到来自 [ " + user.getId() +" ] 的消息: " + info);
        }
    }
}

ClientReadThread.java

import java.io.*;
import java.net.Socket;

public class ClientReadThread extends Thread{
    private Socket socket;
    private DataInputStream in;
    public ClientReadThread(Socket socket){
        this.socket = socket;
        try {
            in = new DataInputStream(socket.getInputStream());
        } catch (IOException e) {
            System.out.println("获取套接字输出流失败.");
        }
    }
    @Override
    public void run() {
        while(true){
            String info;
            try {
                if((info = in.readUTF())!=null)
                    System.out.println("\n服务端发送了:" + info);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

DataUtils.java

import java.io.*;
import java.net.Socket;
import java.net.SocketAddress;
import java.util.ArrayList;

/**
 * @author Uni
 * @create 2021/11/20 10:55
 */
public class DataUtils {
    // 变量: 存储所有的注册用户 (包括管理员下同)
    private final static ArrayList<PersonInfo> list_origin_info = new ArrayList<>();
    // 变量: 存储所有上线的用户
    private final static ArrayList<PersonInfo> list_online_info = new ArrayList<>();
    // 变量: 存储所有的客户端与服务端的套接字
    public final static ArrayList<Socket> list_socket = new ArrayList<>(5);
    // 功能: 从本地读取所有的注册用户
    public static void readRegister(){
        try {
            BufferedReader in = new BufferedReader(new FileReader("personInfo.txt"));
            String info;
            while ((info = in.readLine()) != null) {
                String[] split = info.split(",");
                if (split != null && split.length == 3)
                    list_origin_info.add(new PersonInfo(split[0], split[1], split[2]));
            }
            in.close();
        } catch (IOException e) { e.printStackTrace();}
    }
    // 功能: 返回所有注册用户
    public static ArrayList<PersonInfo> getOriginInfo() {return list_origin_info; }
    // 功能: 将新用户写入配置文件
    public static void writeRegister(PersonInfo p){
        try {
            BufferedWriter out = new BufferedWriter(new FileWriter("personInfo.txt", true));
            out.write(p.getId() + "," + p.getPw() + "," + p.getIdentity() + "\n");
            out.flush();
            p.setLogin(true);
            list_online_info.add(p);
            System.out.println(p.getId() + " 注册成功!");
            out.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    // 功能: 返回所有线上用户
    public static ArrayList<PersonInfo> getOnlienInfo() {return list_online_info; }
    // 功能: 检查ID是否存在
    public static boolean checkId(String id){
        for (PersonInfo personInfo : list_origin_info) {
            if (id.equals(personInfo.getId()))
                return false;
        }
        return true;
    }
    // 功能: 显示所有客户端的名称
    public static void showAllClient(){
        for (int i = 0; i < list_socket.size(); i++) {
            SocketAddress name =list_socket.get(i).getRemoteSocketAddress();
            System.out.printf("%d号客户端:%s\n", (i+1), name);
        }
    }
    // 功能: 检查用户是否存在,即验证登陆正确性
    public static PersonInfo checkPersonInfo(PersonInfo user){
        readRegister();

        String id = user.getId();
        String pw = user.getPw();
        String identity = user.getIdentity();

        for (PersonInfo p : DataUtils.getOriginInfo()) {
            if (id.equals(p.getId()) && pw.equals(p.getPw()) &&
                    identity.equals(p.getIdentity())) {
                PersonInfo pi = new PersonInfo(id, pw, identity);
                pi.setLogin(true);
                return pi;
            }
        }
        return null;
    }
}

Login.java

import java.io.*;
import java.util.ArrayList;
import java.util.Scanner;

public class Login {
    public static Scanner in = new Scanner(System.in);

    public static PersonInfo go() {
        System.out.println("========登陆系统=======");
        PersonInfo user = new PersonInfo(null, null, "admin");
        boolean needLogin = true;
        while (needLogin) {
            System.out.print("id>> ");  user.setId(in.nextLine()) ;
            System.out.print("pw>> ");  user.setPw(in.nextLine());
            if (DataUtils.checkPersonInfo(user) != null) {
                System.out.println(">> 登陆成功!");
                return user;
            } else if (DataUtils.checkId(user.getId()) == false) {
                System.out.println(">> 密码错误");
            } else {
                System.out.println(">> 该用户不存在,是否创建?(y/n)");
                switch (in.nextLine()) {
                    case "y": ;
                        DataUtils.writeRegister(user);
                        System.out.println(">> 已登陆.");
                        needLogin = false;
                        break;
                    default:
                        System.out.println(">> 请重新登陆");
                        break;
                }
            }
        }
        return user;
    }
}

PersonInfo.java

public class PersonInfo {
    private String id;
    private String pw;
    private String identity;
    private boolean isLogin = false;
    public PersonInfo() {};

    public PersonInfo(String id, String pw, String identity) {
        this.id = id;
        this.pw = pw;
        this.identity = identity;
    }

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getPw() {
        return pw;
    }

    public void setPw(String pw) {
        this.pw = pw;
    }

    public String getIdentity() {
        return identity;
    }

    public void setIdentity(String identity) {
        this.identity = identity;
    }

    public void setLogin(boolean x){
        this.isLogin = x;
    }
}

Server.java

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketAddress;
import java.util.ArrayList;
import java.util.Scanner;

public class Server {
    private final static int port = 6666;
    private static PersonInfo admin = null;
    public static Scanner in = new Scanner(System.in);

    public static void main(String[] args) throws IOException {
        System.out.println("服务端启动");
        admin = Login.go();
        int i = 0;
        ServerSocket serverSocket = new ServerSocket(port);
        // 启动负责连接的线程
        new ServerReadyThread(serverSocket).start();
        tips();
        while (true) {
            String operation = in.nextLine();
            switch (operation) {
                case "1":
                    sendMsgToClient();
                    break;
                default:
                    System.out.print(">> 不支持当前指令,请重新输入\n>> ");
            }
            tips();
        }
    }

    public static void tips() {
        System.out.println("==========欢迎访问服务端========");
        System.out.println("|      现在可进行的操作如下     |");
        System.out.println("|   1. 向指定客户端发送消息     |");
        System.out.println("================================");
        System.out.printf(">> ");
    }

    public static void sendMsgToClient() {
        DataUtils.showAllClient();
        System.out.print(">> 请选择要发送到的客户端(e可取消操作)\n>> ");
        boolean flag = true;
        while (flag) {
            String choice = in.nextLine();
            switch (choice) {
                case "e":
                    flag = false;
                    break;
                default:
                    int i = Integer.parseInt(choice) - 1;
                    if (i < DataUtils.list_socket.size()) {
                        System.out.print(">> 选择成功, 接下来请输入要发送的内容\n>> ");
                        try {
                            DataOutputStream out = new DataOutputStream(DataUtils.list_socket.get(i).getOutputStream());
                            while (true){
                                String info = in.nextLine();
                                if(!info.equals("e")){
                                    out.writeUTF(info);
                                    System.out.print(">> 消息发送成功!\n>> ");
                                }
                                else break;
                            }
                            flag = false;
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    } else {
                        System.out.println("选择的客户端不存在, 当前客户端总数目为: " + DataUtils.list_socket.size());
                    }

            }
        }
    }
}

ServerReadThread.java

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

public class ServerReadThread extends Thread{
    private static Socket socket;
    private static DataInputStream in = null;
    public ServerReadThread(Socket socket){
        this.socket = socket;
        try {
            in = new DataInputStream(this.socket.getInputStream());
        } catch (IOException e) {
            System.out.println("获取套接字输入流对象失败");
        }
    }
    @Override
    public void run() {
        while (true) {
            try {
                String info;
                if (in != null && (info = in.readUTF()) != null)
                    System.out.println(info);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

ServerReadyThread.java

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
public class ServerReadyThread extends Thread{
    private static ServerSocket serverSocket;
    public ServerReadyThread(ServerSocket serverSocket){
        this.serverSocket = serverSocket;
    }
    @Override
    public void run() {
        while (true){
            try {
                Socket accept = serverSocket.accept();
                new ServerReadThread(accept).start();
                DataUtils.list_socket.add(accept);
            } catch (IOException e) {
                e.printStackTrace();
            }

        }
    }
}
posted @ 2021-11-20 18:06  Unirithe  阅读(102)  评论(0编辑  收藏  举报