0

I'm trying to send commands from my client side, implemented with Java, to the server side, implemented using C++, through TCP connection. The server-side code is given and tested, so I'm fairly certain that the parsing of the command is not the problem. But when I try to send String commands from the Java side, the server side receives multiple packages sometimes so it does not get parsed correctly. Since the server only executes a command when it ends with a new line symbol, the first part of the command gets discarded and an unknown message error is thrown. I'm using the DataOutputStream for the Java side, and using DataOutputStream.writeBytes(String s) to write my command, and using a BufferedReader to read the message returned by the server.

I'm able to test the server by using telnet to connect to the server without launching Java side code. From telnet, I can successfully send multiple commands and they get parsed correctly. However, when I try to send commands from Java, they get broken up into pieces. The first command is always fine, which is always "add_server 3\n", and the second command, which is "dec_dimmer\n" gets chopped up into "d" and "ec_dimmer\n" and all the subsequent commands in a similar fashion.

Here's how the send command part is implemented:

DataOutputStream outToServer = null;
        try {
            outToServer = new DataOutputStream(mClientSocket.getOutputStream());
        } catch(IOException e) {
            e.printStackTrace();
        }

        try {
            String cmd = command + '\n';
            if(mDebug) {
                System.out.println("Sending Command: " + cmd);
            }
            outToServer.writeBytes(cmd);
        } catch (IOException e) {
            e.printStackTrace();
        }
BufferedReader inFromServer = null;
        try {
            inFromServer = new BufferedReader(new InputStreamReader(mClientSocket.getInputStream()));
        } catch(IOException e) {
            e.printStackTrace();
        }


        String jsonOutput = null;
        try {
            jsonOutput = inFromServer.readLine();
        } catch(IOException e) {
            e.printStackTrace();
        }

        if(mDebug) {
            System.out.println("FROM SERVER: " + jsonOutput);
        }

        return jsonOutput;

In addition, the exact same client has been implemented in c++ before and is working perfectly fine. The following function is the send command function from the c++ version of the client:

std::string SwimClient::sendCommand(const char* command) {
    if (!socket.is_open()) {
        throw std::runtime_error("socket is closed");
    }

    boost::system::error_code ignored_error;
    cout << "sending command = " << command << endl;
    boost::asio::write(socket, boost::asio::buffer(command, strlen(command)),
              boost::asio::transfer_all(), ignored_error);


    vector<char> buffer(128);
    boost::system::error_code error;

    size_t len = socket.read_some(boost::asio::buffer(buffer), error);

    if (error)
        throw boost::system::system_error(error);

    string reply(buffer.data(), len);
    if (reply.compare(0, 6, "error:") == 0) {
        // trim strings
        size_t last = reply.length() - 1;
        while (isspace(reply.at(last))) {
            reply.erase(last--);
        }
        string cmd(command);
        last = cmd.length() - 1;
        while (isspace(cmd.at(last))) {
            cmd.erase(last--);
        }

        throw std::logic_error(reply + "(cmd " + cmd + ')');
    }

    return reply;
}
  • 1
    That's how stream sockets work, yes. There's no correlation between the number of reads on one end and writes on the other end. Your code needs to be able to handle receiving partial records - usually by adding to a buffer until a complete one has eventually been read. – Shawn May 21 '19 at 21:55
  • 1
    Remember that TCP is a stream protocol. It doesn't have the slightest idea what your messages are or what they look like. If you call `recv` for 42 bytes, you'll have to do something to make absolutely certain you got 42 bytes (looping calls to `recv`, `MSG_WAITALL`, whatever). Your messages WILL bill chopped up. There is nothing you can do to prevent this, so you'll have to put them back together again. – user4581301 May 21 '19 at 21:55
  • I guess then my question is that why I can do everything perfectly fine using telnet from terminal. When I connect to my port using telnet, no commands get chopped up and everything is received perfectly. Is there anything else that I'm missing or doing wrong? – Frank Zhang May 21 '19 at 22:01
  • How are you supposed to tell visually if telnet receives `"a"` and `"b\n"` separately vs `"ab\n"` in a single go? – Shawn May 21 '19 at 22:28
  • You were just unlucky in that broken code appeared to work when you tested it. But TCP is a byte-stream protocol; there are no end-to-end message boundaries. Since it looks like your application protocol is line-oriented, you could add an intermediate buffering layer that returns complete lines. –  May 21 '19 at 22:31
  • @shawn - he means he's using telnet as a client into his own server, I think. –  May 21 '19 at 22:32
  • @Shawn I'm using telnet to simply send command to my c++ server, which prints out the commands received. telnet does receive command from server however but that's not the problem here. The commands that telnet send are always perfectly fine together and interpreted as one, while the ones using java DataOutputStream is not. Maybe I'm missing something here but I'm really not sure why this is happening. – Frank Zhang May 21 '19 at 22:34
  • I would suppose the bug to be in the server; unless there is line reassembly code there. it cannot reliably process "lines". –  May 21 '19 at 22:47
  • Typing stuff in in telnet is very different from programmatically sending the same messages. In telnet [Nagle's Algorithm](https://en.wikipedia.org/wiki/Nagle%27s_algorithm) or some similar buffering will be in full force, so ff the messages are short enough to fit in a single packet and the gap before typing the next message is long enough a packet containing one and only one message is very likely. A program firing data into a socket could get 14.5 messages in a TCP packet before the packet fills up and is sent. – user4581301 May 22 '19 at 21:23

0 Answers0