多客户端服务器,使用JavaFX的套接字实现。

huangapple 未分类评论44阅读模式
英文:

MultiClient-Server, socket implementation in javaFX

问题

我正在使用JavaFX创建一个桌面应用程序。在这个应用程序中,我需要检查来自其他客户端发给当前用户的新请求;如果当前用户(接收者)和另一个远程客户端(发送者)都是活动的,则使用套接字。

(疑问在末尾指明)

我会尽量保持事情简单。

与套接字实现相关的四个类如下:

1) 服务器(Server)

public class Server {
    private static final int PORT = 9090;
    private static HashMap<String, ClientHandler> clients = new HashMap<>();

    private static ExecutorService pool = Executors.newFixedThreadPool(4);

    public static void main(String[] args) throws IOException {
        ServerSocket listener = new ServerSocket(PORT);
        BufferedReader in;

        while (true) {
            System.out.println("server is waiting for client to connect");
            Socket client = listener.accept();
            in = new BufferedReader(new InputStreamReader(client.getInputStream()));
            String id = in.readLine();
            System.out.println(id);
            ClientHandler clientThread = new ClientHandler(client, clients, id);
            clients.put(id, clientThread);
            pool.execute(clientThread);
        }
    }
}

2) 客户端处理器(ClientHandler)

public ClientHandler(Socket clientSocket, HashMap<String, ClientHandler> clients, String id) throws IOException {
    this.id = id;
    this.client = clientSocket;
    this.clients = clients;
    in = new BufferedReader(new InputStreamReader(client.getInputStream()));
    out = new PrintWriter(client.getOutputStream(), true);
}

@Override
public void run() {
    try {
        while (true) {
            String request = in.readLine();
            System.out.println(request);

            if (request.startsWith("say")) {
                outToAll(request);
            }
        }
    } catch (Exception e) {
        System.out.println(e);
    } finally {
        try {
            in.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        out.close();
    }
}

public void outToAll(String msg) {
    clients.forEach((key, client) -> {
        client.out.println(msg);
    });
}

3) 客户端(Client)

public Client(String id) throws IOException {
    this.id = id;
    this.socket = new Socket(IP_ADDRESS, PORT);
    this.serverConnection = new ServerConnection(socket);
    this.keyboard = new BufferedReader(new InputStreamReader(System.in));
    this.out = new PrintWriter(socket.getOutputStream(), true);
}

@Override
public void run() {
    new Thread(serverConnection).start();

    try {
        out.println(id);

        while (true) {
            String command = keyboard.readLine();

            if (command.equals("quit")) {
                break;
            }

            out.println(command);
        }
    } catch (Exception e) {
        System.out.println(e);
    }

    out.close();

    try {
        socket.close();
        keyboard.close();
    } catch (IOException e) {
        e.printStackTrace();
    }
}

public void stopSocket() throws IOException {
    socket.close();
}

4) 服务器连接(ServerConnection)

public ServerConnection(Socket server) throws IOException {
    this.server = server;
    this.in = new BufferedReader(new InputStreamReader(this.server.getInputStream()));
}

@Override
public void run() {
    try {
        while (true) {
            String msg = in.readLine();

            if (msg == null) {
                break;
            }

            System.out.println("Server says > " + msg);
        }
    } catch (Exception e) {
        System.out.println(e);
    }
}

这些是与套接字服务相关的类。

由于我使用JavaFX,每个客户端的Runnable类应该在自己的线程上运行,因此为了实现这一点,我在名为HomeController的JavaFX控制器内部创建了一个线程,该线程在客户端登录时调用。HomeController然后创建一个客户端线程,如下所示:

希望这是正确的方法,因为我需要另一个线程,这样客户端就不会阻塞JavaFX UI线程。

// 由于我可以访问`id`并且已经创建了一个Client对象
// 我希望这是正确的方法
this.client = new Client(id);
new Thread(client).start();

当客户端注销时,我关闭客户端套接字,如下所示:

// 上面创建的客户端
client.stopSocket();

这样以这种方式为每个连接到服务器的客户端创建了单独的客户端线程。

疑问

我启动了多个JavaFX应用程序实例,并使用不同的用户ID登录。

根据实现,当我在客户端控制台中输入内容时,它应该打印在其他每个客户端的控制台中。但这并不总是发生。有些客户端可以收到消息,有些则不行,有时候没有任何人收到消息。我猜测有些客户端被阻塞了,我无法理解。

出于测试目的,我创建了另一个项目,其中包含相同的4个类,而不是将Client类定义为Runnable,我在其中创建了一个主方法。然后启动了多个Client类的实例。保持其余部分不变。这个实现完全没有任何问题。

请帮助我解决这个问题,我已经面临这个问题已经有4天了。

英文:

I am creating a desktop app with javaFX. In this app I have to check for new requests from other clients to the current user; if both the current user (receiver) and another remote client (sender) are active, using sockets.

(doubt is specified at the end)

I will try to keep the things as simple as possible here.

There are four Classes related with the socket implementation as follows

> Server

> ClientHandler

> Client

> ServerConnection

1) Server

public class Server {
    private static final int PORT = 9090;
    private static HashMap&lt;String,ClientHandler&gt; clients = new HashMap&lt;&gt;();

    private static ExecutorService pool = Executors.newFixedThreadPool(4);
    public static void main(String[] args) throws IOException {
        ServerSocket listener = new ServerSocket(PORT);
        BufferedReader in;
        while (true) {
            System.out.println(&quot;server is waiting for client to connect&quot;);
            Socket client = listener.accept();
            in = new BufferedReader(new InputStreamReader(client.getInputStream()));
            String id = in.readLine();
            System.out.println(id);
            ClientHandler clientThread = new ClientHandler(client,clients,id);
            clients.put(id,clientThread);
            pool.execute(clientThread);
        }
    }
}

2) ClientHandler

public ClientHandler(Socket clientSocket, HashMap&lt;String, ClientHandler&gt; clients, String id) throws IOException {
        this.id = id;
        this.client = clientSocket;
        this.clients = clients;
        in = new BufferedReader(new InputStreamReader(client.getInputStream()));
        out = new PrintWriter(client.getOutputStream(), true);
    }

    @Override
    public void run() {
        try {
            while (true) {
                String request = in.readLine();
                System.out.println(request);
                if(request.startsWith(&quot;say&quot;)){
                    outToAll(request);
                }
            }
        } catch (Exception e) {
            System.out.println(e);
        } finally {
            try {
                in.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            out.close();
        }
    }
    //this method is used to send message to all the active clients
    public void outToAll(String msg){
            clients.forEach((key,client)-&gt;{
                  c.out.println(msg);
            });
        }

3)Client

public Client(String id) throws IOException {
        this.id = id;
        this.socket = new Socket(IP_ADDRESS, PORT);
        this.serverConnection = new ServerConnection(socket);
        this.keyboard = new BufferedReader(new InputStreamReader(System.in));
        this.out = new PrintWriter(socket.getOutputStream(), true);
    }

    @Override
    public void run() {
        new Thread(serverConnection).start();
        try {
            out.println(id);
            while (true) {
                String command = keyboard.readLine();
                if (command.equals(&quot;quit&quot;)) break;
                out.println(command);
            }
        } catch (Exception e) {
            System.out.println(e);
        }
        out.close();
        try {
            socket.close();
            keyboard.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public void stopSocket() throws IOException {
        socket.close();
    }

4) ServerConnection

//Server connection is connection from client side to the server.which reads message from server and just prints into the console

public ServerConnection(Socket server) throws IOException {
        this.server = server;
        this.in = new BufferedReader(new InputStreamReader(this.server.getInputStream()));
    }
    @Override
    public void run() {
        try{
            while (true){
                String msg = in.readLine();
                if(msg == null) break;
                System.out.println(&quot;Server says &gt; &quot;+ msg);
            }
        }catch (Exception e){
            System.out.println(e);
        }finally {

        }
    }

So these are my socket service related classes.

>As I am using javaFX each Client Runnable class should run on its own thread, thus to achieve this I have create a thread in side my javaFX Controller Named HomeController which is called whenever a client is logged in. HomeController then creates a client Thread like shown below

I hope that this the correct way to do so, as i need another thread so that client will

not block the javafx ui thread

    //as I have access to the `id` and already have created a Client object
    // I hope that this the correct way to do so
    this.client = new Client(id);
    new Thread(client).start();

And whenever a client is logged out, I close the Client Socket as

    //above created client
    client.stopSocket();

Thus in this way i have created separate client thread for each client connected to the server.

Doubt

I start multiple javafx app instance with and login with different user ids.

>So according to the implementation, when I will type something in the client console it should be printed in each and every console of other clients too . But this doesn't happen all the time . some client do get message some don't and sometimes no one gets the message. I guess some clients are get blocked and I can't understand.

>So for testing purpose, I created another project with same 4 classes and rather than defining Client class as runnable i created a main method in it. And launched multiple instances of Client class. Keeping rest of the things same. This implementation works just great without any flaw.

Please help me through this it's been 4 days I am facing this issue.

huangapple
  • 本文由 发表于 2020年4月10日 17:46:47
  • 转载请务必保留本文链接:https://java.coder-hub.com/61137722.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定