Java 客户端 - 服务器 - 具有同步线程的客户端

标签 java multithreading sockets

我正在做一个小的客户端/服务器应用程序。目前它运行良好,但我需要添加一个“选项”。

服务器类是这样的:

public class Server {

    public static void main(String[] zero) throws IOException {

        ServerSocket serverSocket  ;
        Socket clientSocket ;

        DataBase DB = new DataBase();
        System.out.println("ouverture du server");
        serverSocket = new ServerSocket(2000);

        DB.printAll(); //just to see the DB.


        while(true) { 
            try {

                clientSocket = serverSocket.accept(); 
                Thread t = new Thread(new Connect(clientSocket, DB));
                t.start();

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

因此很多客户端都能够连接到服务器。我的观点是:我希望连接的客户端(例如 Client1)能够向他选择的另一个连接的客户端(Client2)发送某些内容。

我的问题是:既然所有客户端都通过不同线程中的 clientSocket 连接到服务器,那么 Client1 如何找到/拥有/检索 Client2 的套接字?

我已阅读 this它建议创建一个包含每个连接客户端的输出流的数据输出流列表。这似乎是一个好主意,但我更愿意对套接字执行相同的操作(因为我不确定现在是否需要“数据”输出流或另一个输出流,并且在任何情况下它都会对于我来说,套接字更清楚)。

另外,Client2 正在与服务器做他自己的事情,其中​​包括交换数据,因此他将使用他的输出/输入流。我必须确保这不会混淆。

例如,假设 Client2 向服务器发送一些内容并等待响应。然后Client1向Client2发送一些东西; Client2 接收 Client1 发送的内容,而不是服务器响应;因为他不应该收到这个,这会导致 fatal error 。

我读过一些关于同步线程的内容,这似乎是我所需要的,但我是java新手,这有点难?

我想如果我将Client2 Socket声明为synchronized,那么线程将等到clientSocket空闲后再使用它;并且由于客户端不应该断开连接(如果他断开连接,那么套接字已关闭,因此我们无法向他发送任何内容),(client1 的)线程将无法使用 client2 的套接字。

现在,如果我同步DataOutputStream,有时client2和服务器之间的通信涉及许多不同的输出流,因此DataOutputStream可以自由使用,但Client2仍然会等待来自服务器的输入,而不是来自Client1的输入,所以不会相同的输入。

也许我可以直接同步完整的OutputStream。或者也许我说的话没有任何意义。 帮忙?

最佳答案

好吧,既然我发布了它,我想我也可以问它。

所以,这不是一个完美的解决方案(在我看来),我还没有完全完成它,但到目前为止它似乎有效。

对于第一个问题(如何检索另一个客户端套接字),我在服务器端创建一个静态哈希表,以客户端名称作为键,以套接字作为值。每当客户端连接时,他就会被添加到此表中;当他断开连接时,他被删除了(我没有尝试断开连接部分)

private static Hashtable<String,Socket> connectedClients= new Hashtable<String, Socket>();
public static void setConnectedClient(String username, Socket clientSocket) {
    connectedClients.put(username, clientSocket);
}

public static void disconnectClient(String user) {
    connectedClients.remove(user);
}

因此,无论我在服务器端的程序中,我都可以检索客户端套接字(知道他的用户名)。

对于第二个问题(如何避免与其他客户端与服务器的工作混淆),我正在创建第二个套接字。这意味着,在客户端有两个套接字:一个用于与服务器通信,一个用于与另一个端口上的客户端(来自服务器)通信。这意味着我在服务器端还有另一个服务器套接字用于另一个端口。 因此,当客户端连接到服务器时,会在客户端和服务器端创建第二个套接字。

        Socket TCHATSocket = Server.getServerSocket().accept(); //create a new socket specifically for TCHAT
        Server.setConnectedClient(user, TCHATSocket); //register the client ID & TchatSocket in the list of connected clients.

现在,在客户端,我必须继续监听来自此套接字的任何输入(以防另一个客户端想通过服务器与我通信),但当我与服务器进行操作时则不能。

为此,我在客户端创建另一个线程。当客户端连接时,它启动创建第二个套接字的线程并等待来自服务器的输入。但是,当客户端启动任何涉及与服务器通信的函数时,该线程将暂停,然后在函数结束时取消暂停。这是线程类:

package client;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.Socket;

public class TCHAT extends Thread{
    private InputStream in;
    private OutputStream out;
    private Socket pakeSocket;
    private String message;
    private volatile boolean pause = false;

    public void pause() {
        pause = true;
    }

    public void unPause() {
        pause = false;
        synchronized(this) {
            notify();
        }

    }

    public TCHAT() {
        try {
            tchatSocket = new Socket(InetAddress.getLocalHost(),2004);
            in = tchatSocket.getInputStream();
            out = tchatSocket.getOutputStream();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

    public void run(){
        while(!tchatSocket.isClosed()) {

            if(pause) {
                System.out.println("TCHAT paused");
                synchronized (this) {
                    try {
                        wait();// wait until someone calls notify() (by calling the method unPause)
                    }catch (InterruptedException e) {           
                        e.printStackTrace();            
                    }
                }
            }

            else {
                System.out.println("Waiting for TCHAT demand");
                try {
                    BufferedReader br = new BufferedReader(new InputStreamReader(in));
                    message = br.readLine();
                    System.out.println(message);
                    //Find a way to blok the while after receiving the message:
                    tchatSocket.close();
                } catch (IOException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }


        }
    }

}

正如你所看到的,它并没有真正终止:现在我只是从服务器收到一条消息,然后关闭套接字;但我想你可以在关闭它之前做任何你想做的事情。另外,由于某种原因,当我调用暂停()时,我从未收到“TCHAT已暂停”消息,并且当我取消暂停时,我没有收到另一个“等待TCHAT需求”,但它似乎有效(因为没有暂停其他客户端功能无法工作,但在这里它们工作良好,并且即使在上传或其他操作之后,客户端也可以正确接收来自服务器的消息。)

基本上,当我想使用客户端功能时,我只需这样做:

TCHAT.pause();
upload(file); //or any function involving client/server communication
TCHAT.unpause();

希望它能帮助别人,但仍然很高兴让它发挥作用:)

关于Java 客户端 - 服务器 - 具有同步线程的客户端,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51650024/

相关文章:

java - 连接重置,java

java - Maven 无法识别 pom.xml 中的 'configuration' 标记

java.lang.IllegalStateException : The ad size and ad unit ID must be set before loadAd is called

java - Java中线程的状态

java - 当通过屏障(例如 CyclicBarrier)的线程数量小于屏障限制时,屏障(例如 CyclicBarrier)是否会导致死锁?

c# - TCP 重置后随机 "An existing connection was forcibly closed by the remote host."

java - 背包算法和凸包

适用于 Java 5 的 Java 反编译器(处理枚举等新事物)

multithreading - performSelectorOnMainThread 与 performSegue

c - 将 'abstract sockets' 与 AF_UNIX SOCK_DGRAM 结合使用