Warm tip: This article is reproduced from serverfault.com, please click

multithreading-如何改善与多个客户端的Java聊天?

(multithreading - How can I improve this java chat with multiple clients?)

发布于 2020-11-23 20:27:14

对于一个学校项目,我不得不在Java中与多个用户聊天。我做到了,它有效,但我认为并不完美。将来我想添加一个GUI,但在此之前我想对其进行更正。所以我的问题是我该如何改善呢?我能做得更好吗?这是我的代码。在客户端,我使用了主线程来等待用户输入,并使用了另一个线程来监听服务器并等待来自其他客户端的消息。在服务器端,我使用主线程接受来自客户端和管理N个客户端的N个线程的新连接请求。请帮我。

客户端:
import java.net.*;
import java.io.*;
class listener implements Runnable{
    private Socket connection;
    private static boolean running;
    listener(Socket connection){
        this.connection=connection;
        running=true;
    }
    public void run(){
        InputStreamReader in;
        BufferedReader sIN;
        String msgToRecv;
        try{
            in=new InputStreamReader(connection.getInputStream());
            sIN=new BufferedReader(in);
        }
        catch(IOException e){
            System.out.println(e);
            return;
        }
        while(running){
            try{
                msgToRecv=sIN.readLine();
            }
            catch(IOException e){
                System.out.println("Connection closed: "+e);
                return;
            }
            System.out.println(msgToRecv);
        }
        return;
    }
    public static void stopListening(){
        running=false;
        return;
    }
}
class Client
{
    public static void main(String[] args)
    {
        String ipAddServer="127.0.0.1";
        int port=8080;
        Socket connection=null;
        String msgToSend;
        String username;
        InputStreamReader input=new InputStreamReader(System.in);
        BufferedReader tastiera=new BufferedReader(input);
        OutputStream out;
        PrintWriter sOUT;
        System.out.println("Enter a username");
        try{
            connection=new Socket(ipAddServer, port);
            out=connection.getOutputStream();
            sOUT=new PrintWriter(out);
            username=tastiera.readLine();
        }
        catch(IOException e){
            System.out.println(e);
            return;
        }
        System.out.println("User: "+username);
        System.out.println("Chat Joined");
        Thread t=new Thread(new listener(connection));
        t.start();
        do{
            try{
                msgToSend=tastiera.readLine();
            }
            catch(IOException e){
                System.out.println(e);
                return;
            }
            sOUT.println(username+": "+msgToSend);
            sOUT.flush();
        }while(!msgToSend.equals("EXIT"));
        listener.stopListening();
        try
        {
            connection.close();
        }
        catch(IOException e)
        {
            System.out.println(e);
        }
        return;
    }
}

服务器端:

import java.net.*;
import java.io.*;
import java.util.ArrayList;
class clientHandler implements Runnable{
    private ServerSocket sSocket;
    private Socket connection;
    private ArrayList<Socket> sockets;
    public clientHandler(ServerSocket sSocket, Socket connection, ArrayList<Socket> sockets) {
        this.sSocket=sSocket;
        this.connection=connection;
        this.sockets=sockets;
    }
    public void run(){
        InputStreamReader in, input;
        BufferedReader sIN, tastiera;
        OutputStream out;
        PrintWriter sOUT;
        String msgToRecv;
        try{
            out=connection.getOutputStream();
            sOUT=new PrintWriter(out);
            in=new InputStreamReader(connection.getInputStream());
            sIN=new BufferedReader(in);
        }
        catch(Exception e){
            System.out.println(e);
            return;
        }
        sockets.add(connection);
        do{
            try{
                msgToRecv=sIN.readLine();
            }
            catch(IOException e){
                System.out.println(e);
                return;
            }
            for(int i=0;i<sockets.size();++i){
                Socket temp=sockets.get(i);
                if(!connection.equals(temp)&&!msgToRecv.equals("null")){
                    OutputStream tmpOut;
                    PrintWriter tmpSOUT;
                    try{
                        tmpOut=temp.getOutputStream();
                        tmpSOUT=new PrintWriter(tmpOut);
                    }
                    catch(IOException e){
                        System.out.println(e);
                        return;
                    }
                    tmpSOUT.println(msgToRecv);
                    tmpSOUT.flush();
                }
            }
        }while(msgToRecv!=null);
        sockets.remove(sOUT);
        return;
    }
}
class Server
{
    public static void main(String[] args)
    {
        int port=8080;
        ServerSocket sSocket;
        ArrayList<Socket> sockets=new ArrayList<Socket>();
        Thread t;
        try{
            sSocket=new ServerSocket(port);
        }
        catch(IOException e){
            System.out.println(e);
            return;
        }
        while(true){
            try{
                t=new Thread(new clientHandler(sSocket, sSocket.accept(), sockets));
            }
            catch(IOException e){
                System.out.println(e);
                return;
            }
            t.start();
        }
    }
}
Questioner
Andrea Goldoni
Viewed
1
Alireza Akhoundi 2020-11-29 17:51:08

当你开发聊天服务器(更好的说是软实时服务器)时,有一些重要的事情:

1.管理资源

2.低延迟

3.可扩展性

4.耐久性

5.可用性

6.存储数据(聊天,文件和...)

在开发用于学校的服务器时,可以跳过第2、3、4和5节。

第1部分:在任何程序中管理资源太重要,在聊天服务器中也是如此。你有N个客户端,并且需要N个线程?它不是一个好主意,有一种使用无阻塞套接字来处理它的好方法,它将帮助你在一个线程中处理许多连接。记住不要在服务器上创建很多线程,也不要浪费CPU和内存。有时你必须更好地了解重用已分配的缓冲区,而不是创建新的缓冲区,从而导致CPU和内存资源的浪费。

第6部分:需要管理在聊天服务器中将数据存储在每秒接收到许多请求的聊天服务器中。有时需要让数据批处理,然后将其推入数据存储。这将使其比逐一推送数据的速度更快,当然,数据库及其驱动程序库将在其层中处理这些问题,但是对于在应用程序层中进行批处理而言,这太重要了。聊天服务器中可以使用许多好的数据库,但是最好的数据库是Apache Cassandra

注意:它只是与开发聊天服务器无关,而是更好地使你的代码更易于理解和编程事件驱动(UserConnect,UserDisconnect,MessageRecived和...)。一种好的做法是通过定义接口(例如:AbstractServer,AbstractDataBase和...)来创建程序逻辑抽象,然后为直接使用此接口的程序创建一个可运行部分,而与该接口在功能上的工作方式无关,然后实现此接口。使用此模型,你可以将程序划分为较小的部分,并使实现程序更容易。