java - Java 中的多客户端聊天应用程序

标签 java sockets client-server chat serversocket

我刚开始接触套接字编程,所以为了加深对它的理解,我想构建一个多客户端聊天应用程序。

我打算这样做的方式如下: 应用程序启动后,您有两个选择:创建服务器或加入服务器。

如果您选择创建一个服务器,一个新线程将启动并托管服务器,然后另一个线程将启动,它将创建一个新客户端并自动连接到刚刚构建的服务器。

在这里我遇到了以下问题。 每个客户端都可以向服务器发送消息,但为了使所有客户端的消息保持同步,我正在考虑将检索到的消息从服务器重定向到所有客户端,如下图所示。 enter image description here

问题是,当我尝试在客户端和服务器上监听和发送时,我得到了这个错误。

java.net.SocketException: Connection reset
    at java.base/java.net.SocketInputStream.read(SocketInputStream.java:186)
    at java.base/java.net.SocketInputStream.read(SocketInputStream.java:140)
    at java.base/java.net.SocketInputStream.read(SocketInputStream.java:200)
    at java.base/java.io.DataInputStream.readUnsignedShort(DataInputStream.java:342)
    at java.base/java.io.DataInputStream.readUTF(DataInputStream.java:594)
    at java.base/java.io.DataInputStream.readUTF(DataInputStream.java:569)
    at parctice.Server.lambda$main$0(Server.java:32)
    at java.base/java.lang.Thread.run(Thread.java:835)

这是我的服务器:

int port = 4444;

        try {
            ServerSocket serverSocket = new ServerSocket(port);
            System.out.println("server starts port = " + serverSocket.getLocalSocketAddress());


            while(true){
                Socket socket = serverSocket.accept();
                System.out.println("accepts : " + socket.getRemoteSocketAddress());

                DataInputStream in = new DataInputStream(socket.getInputStream());
                DataOutputStream out = new DataOutputStream(socket.getOutputStream());


                String[] message = {""};

                new Thread(() -> {
                    try {
                        while(in.available() > 0){
                            System.out.println("SERVER > " + in.readUTF());
                            message[0] = in.readUTF();
                        }
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }).start();

                System.err.println(message[0]);
                try {
                    out.writeUTF(message[0] + "REDIRECTED MESSAGE");
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

和我的客户:

int portNumber = 4444;
System.out.println("CLIENT >  Trying to connect to the server...");
try {
    Socket socket = new Socket("localhost", portNumber);

    DataInputStream in = new DataInputStream(socket.getInputStream());
    DataOutputStream out = new DataOutputStream(socket.getOutputStream());


    new Thread(() -> {
        try {
            while(in.available() > 0){
                System.out.println("SERVER > " + in.readUTF());
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }).start();


    try {
        out.writeUTF("test");
    } catch (IOException e) {
        e.printStackTrace();
    }

} catch (IOException e) {
    e.printStackTrace();
}

最后,我想问问你们,你们是否认为我尝试使用的逻辑是正确的,如果不正确,请告诉我哪里出了问题,如果可能的话,你们能解释一下为什么我不能够同时在客户端和服务器上收听和发送?因为当我尝试通过客户端发送数据并在服务器上检索它时它工作得很好,但我想完成双向通信。

最佳答案

我发现这个解决方案似乎适用于这种情况 第一次我们创建服务器:

private List<ClientThread> clients; // or "protected static List<ClientThread> clients;"


public List<ClientThread> getClients(){
    return clients;
}
    private void startServer(){
        clients = new ArrayList<ClientThread>();
        try {
            serverSocket = new ServerSocket(PORT);
            System.out.println("SERVER ON");
            System.out.println("SERVER > Waiting for connections...");


//            ACCEPT ALL CONNECTIONS
            while (true){
                try {
                    Socket socket = serverSocket.accept();
                    System.out.println("SERVER > New connection: " + socket.getRemoteSocketAddress());
                    ClientThread client = new ClientThread(this, socket);
                    Thread thread = new Thread(client);
                    thread.start();
                    clients.add(client);
                } catch (IOException e) {
                    e.printStackTrace();
                    System.out.println("SERVER > Accept failed");
                }
            }

        } catch (IOException e) {
            e.printStackTrace();
        }
    }

然后我们为每个客户创建一个新线程

public class ClientThread implements Runnable {

    private Socket socket;
    private Server server;
    private String clientName;

    public String getClientName() {
        return clientName;
    }

    public void setClientName(String clientName) {
        this.clientName = clientName;
    }

    public Socket getSocket() {
        return socket;
    }

    public void setSocket(Socket socket) {
        this.socket = socket;
    }

    public ClientThread(Server server, Socket socket) {
        this.server = server;
        this.socket = socket;
    }

    @Override
    public void run() {
        try {
            DataInputStream in = new DataInputStream(socket.getInputStream());
            DataOutputStream out = new DataOutputStream(socket.getOutputStream());

            out.writeUTF("HI FROM SERVER");
            while (!socket.isClosed()) {
                try {
                    if (in.available() > 0) {
                        String input = in.readUTF();
                        // UNCOMMENT TO READ ON SERVER
                        // System.out.println("SERVER > " + input);
                        for (ClientThread thatClient : server.getClients()){
                            DataOutputStream outputParticularClient = new DataOutputStream(thatClient.getSocket().getOutputStream());
                            outputParticularClient.writeUTF(input + " GOT FROM SERVER");
                        }
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

客户:

    public void createClient(){
        try {
            socket = new Socket("localhost", portNumber);
//            socket = new Socket(getHost(), portNumber);

            DataInputStream in = new DataInputStream(socket.getInputStream());
            new Thread(()->{
                while(!socket.isClosed()){
                    try {
                        if (in.available() > 0){
                            String input = in.readUTF();
                            System.out.println(getUserName() + " > " + input);
                        }
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }).start();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

关于java - Java 中的多客户端聊天应用程序,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56454187/

相关文章:

java - 服务器回复客户端消息由于关闭套接字而失败 - Java 客户端-服务器示例

java - Android 客户端/服务器 Socket 客户端未接收

database - 不由发布服务器存储的用户 session key 是否可以被认为是安全的?

java - 如何重置/清除 NatTable 中的多列排序?

Java - Google Guava Multimap 问题

java - 有没有框架?

Android 套接字不能很好地播放

c - 使用带套接字的拼接可能会导致饥饿

java - hibernate 工具 : how to ignore foreign keys?

sockets - 使用 `chan pending output` 代替可写文件事件