简体   繁体   中英

Client-Server communication where Server initiates

I would like to have this setup:

  • Server hosting TCP socket server
  • Multiple clients connected over TCP (keeping connection open)

Then I would like to initiate a message from the Server to the client. I can't figure out how to do this, and have multiple client sessions at the same time. Techniques I've read involve the Server listening on a port, and when it receives communicate from a client, it launches a new thread to handle and process that, and then it goes back to listening on the port for the next request of another client.
So, then how would I tap into that and send a message to a client running on one of those threads?

My actual usage scenario if you are interested is below. Final goal is like a remote control for your file system to upload files to the server. - Each client has a java background application running in the system tray that connects to the server - Server hosts connections, and also hosts a RESTFul webservice to initiate communication - Mobile device connects to Server over RESTFul webservices to request informatino about the client's filesystem. So it can drill down and find a file, then click and have the file uploaded to the server. The idea here is mobile users needing to upload files from their desktop to the server while away from their office on a mobile device. (and this is for custom product, so can't use a third-party app_

PS: I've been looking at the simple Client-Server chat program here: http://way2java.com/networking/chat-program-two-way-communication/

You want to have a server listening at all times on a specified port. Once the server notices an incoming connection on that port you should create a new Thread to handle the communication between that client and the server, while the main thread keeps on listening for other incoming connections. This way you can have multiple clients connected to one server. Like so:

private void listen() throws IOException {
    serverSocket = new ServerSocket(port)
    while (GlobalFlags.listening) {
        new ServerThread(serverSocket.accept();
        if (GlobalFlags.exit) {
            serverSocket.close();
            break;
        }
    }
}

Where the GlobalFlags are variables to control the listening process and are not really necessary. You could do a while True and just keep listening for ever and ever.

In my project I have a main server controller which had listeners running in Threads. The controller controlled the GlobalFlags. I'm sure instead of using global flags there is a better way to do inter thread communication but for me this was the simplest at the time.

The ServerThread should be looping all the time switching between sending output to the client and receiving input from the client. Like so:

ServerThread(Socket socket) {
    super("GameServerThread");
    this.socket = socket;
    try {
        this.socket.setTcpNoDelay(true);
    } catch (SocketException e) {
        // Error handling
    }
    this.terminate = false;
}

@Override
public void run() {        
    try {
        out = new PrintWriter(socket.getOutputStream(), true);
        in = new BufferedReader(
             new InputStreamReader(
             socket.getInputStream()));

        String inputLine, outputLine;

        while ((inputLine = in.readLine()) != null) {
                outputLine = processInput(inputLine);
                out.println(outputLine);
                if (terminate) {
                    break;
                }
            }
        }
        out.close();
        in.close();
        socket.close();

    } catch (Exception e) {
        // Error handling, should not use Exception but handle all exceptions by themselves.
}

On the client side you have a thread running through a similar loop, receiving input from the server and then sending output to the server.

In this example processInput is the function used to process the client's input. If you want the server to initiate contact you can make the server send something to the outputstream before listening for input and make the client listen first.

I have extracted this example from one of my own projects and the this.socket.setTcpNoDelay(true) is supposed to make the process faster. Reference here: http://www.rgagnon.com/javadetails/java-0294.html

"java.net.Socket.setTcpNoDelay() is used to enable/disable TCP_NODELAY which disable/enable Nagle's algorithm. Nagle's algorithm try to conserve bandwidth by minimizing the number of segments that are sent. When applications wish to decrease network latency and increase performance, they can disable Nagle's algorithm (that is enable TCP_NODELAY). Data will be sent earlier, at the cost of an increase in bandwidth consumption. The Nagle's algorithm is described in RFC 896.

You get the current "TCP_NODELAY" setting with java.net.Socket.getTcpNoDelay()"


So to send a message to a specific client you could put all the threads upon creation in an ArrayList so you can keep track of all the currently connected clients. You can have the processInput method halt and polling a queue/variable until another class puts the message to be send in the queue/variable. So how to gain a handle on the class depends on your implementation of processInput. You could give every thread an ID (which is what I did in my project) and maybe have the processInput method poll an ArrayList at index=ID. Then to send output to the client you would have to set the variable at index=ID.

This method seems kind of clunky to me personally but I'm not really sure how else I would do it. You would probably use Queues and have processInput write the input to its Queue and then wait for another class to read it and put its response in the Queue. But I have personally never worked with Queues in java so you should read up on that yourself.

In my knowledge 1) Server hosting TCP socket server -- Possible 2) Multiple clients connected over TCP -- Possible 3) Then I would like to initiate a message from the Server to the client -- Not Possible. The Client has to initiate a connection creation, then the server might be able to send data packets to You. Example: You need to open Facebook website on your browser, Facebook server cannot decide to send its page to your PC on its own because your PC will not have a static IP address, and also if Facebook hypothetically writes code to initiate connection to Your PC, then it is as good as Your PC is the server and Facebook website/server acts as client.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM