Can't send message over Java socket without closing it

I am writing a server side of a large Java application to communicate with a client over TCP / IP using Java sockets. A client (written in PHP) connects to the server, sends a request in XML format, and then the server sends a response. The request-response can be repeated several times in one connection.

The server side is pretty straightforward. It must allow multiple client connections in order to listen for a stream and create a session for each accepted connection. A session consists of an object containing two LinkedBlockingQueues to send and receive, two threads to send and receive messages using these queues and a processing thread.

The problem is that any messages are actually sent only after the socket is closed. The response messages get into the message queue and the PrintStream.println () method is no problem, but wireshark only reports transmission when the client closes the connection on its side. Creating a PrintStream with auto-streaming enabled or using flush () doesn't work. Closing the socket on the server side also doesn't work, the server is still running and receiving messages.

Also with the current implementation of the client receiving server side requests works great, the same happens with echo -e "test" | socat - TCP4:192.168.37.1:1337

from the local Linux VM, but when I connect to the server and try to send something, receive anything until I close telnet client, same problem as above.

Relevant server code (the whole application is too big to embed everything, and I work with a lot of other people's code):

package Logic.XMLInterfaceForClient;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.Socket;
import java.util.HashSet;
import java.util.concurrent.LinkedBlockingQueue;

import Data.Config;
import Logic.Log;

public class ClientSession {

    /**
     * @author McMonster
     * 
     */
    public class MessageTransmitter extends Thread {

        private final Socket socket;
        private final ClientSession parent;

        private PrintStream out;

        /**
         * @param socket
         * @param parent
         */
        public MessageTransmitter(Socket socket, ClientSession parent) {
            this.socket = socket;
            this.parent = parent;
        }

        /*
         * (non-Javadoc)
         * 
         * @see java.lang.Runnable#run()
         */
        @Override
        public void run() {
            try {
                out = new PrintStream(socket.getOutputStream(), true);

                while (!socket.isClosed()) {
                    try {
                        String msg = parent.transmit.take();
                        // System.out.println(msg);
                        out.println(msg);
                        out.flush();
                    }
                    catch(InterruptedException e) {
                        // INFO: purposefully left empty to suppress spurious
                        // wakeups
                    }
                }

            }
            catch(IOException e) {
                parent.fail(e);
            }

        }

    }

    /**
     * @author McMonster
     * 
     */
    public class MessageReceiver extends Thread {

        private final Socket socket;
        private final ClientSession parent;

        private BufferedReader in;

        /**
         * @param socket
         * @param parent
         */
        public MessageReceiver(Socket socket, ClientSession parent) {
            this.socket = socket;
            this.parent = parent;
        }

        /*
         * (non-Javadoc)
         * 
         * @see java.lang.Runnable#run()
         */
        @Override
        public void run() {
            try {
                in = new BufferedReader(new InputStreamReader(socket.getInputStream()));

                while (!socket.isClosed()) {
                    String message = "";
                    String line;

                    while ((line = in.readLine()) != null) {
                        message = message + line + "\n";
                    }

                    if(message != "") {
                        parent.receive.offer(message.toString());
                    }
                }
            }
            catch(IOException e) {
                parent.fail(e);
            }

        }
    }

    public final LinkedBlockingQueue<String> transmit = new LinkedBlockingQueue<>();
    public final LinkedBlockingQueue<String> receive = new LinkedBlockingQueue<>();

    private final XMLQueryHandler xqh;
    private final Socket socket;

    private String user = null;
    private HashSet<String> privileges = null;

    /**
     * @param socket
     * @param config
     * @throws IOException
     * @throws IllegalArgumentException
     */
    public ClientSession(Socket socket, Config config)
            throws IOException,
            IllegalArgumentException {
        // to avoid client session without the client
        if(socket == null) throw new IllegalArgumentException("Socket can't be null.");

        this.socket = socket;

        // we do not need to keep track of the two following threads since I/O
        // operations are currently blocking, closing the sockets will cause
        // them to shut down
        new MessageReceiver(socket, this).start();
        new MessageTransmitter(socket, this).start();

        xqh = new XMLQueryHandler(config, this);
        xqh.start();
    }

    public void triggerTopologyRefresh() {
        xqh.setRefresh(true);
    }

    public void closeSession() {
        try {
            xqh.setFinished(true);
            socket.close();
        }
        catch(IOException e) {
            e.printStackTrace();
            Log.write(e.getMessage());
        }
    }

    /**
     * Used for reporting failures in any of the session processing threads.
     * Handles logging of what happened and shuts down all session threads.
     * 
     * @param t
     *            cause of the failure
     */
    synchronized void fail(Throwable t) {
        t.printStackTrace();
        Log.write(t.getMessage());
        closeSession();
    }

    synchronized boolean userLogin(String login, HashSet<String> privileges) {
        boolean success = false;

        if(!privileges.isEmpty()) {
            user = login;
            this.privileges = privileges;
            success = true;
        }

        return success;
    }

    public synchronized boolean isLoggedIn() {
        return user != null;
    }

    /**
     * @return the privileges
     */
    public HashSet<String> getPrivileges() {
        return privileges;
    }
}

      

0


source to share


2 answers


I can understand why messages are not being accepted on the server until the socket is closed - this is similar to what was designed. in.readLine () will only return null when the end of the stream is reached, which with a TCP socket stream means the socket is closed. If you want your readLine () loop to return before then, the code in the loop will need to detect the end of your message using whatever protocol you use over TCP to define the messages.



0


source


It has nothing to do with shipping at all. It would be more correct to say that if you read to the end of the stream, you won't get the end of the stream until the peer finishes the stream by closing the socket.



This is a tautology.

+1


source







All Articles