0
votes

I am facing another TCP Socket issue. I've read through a huge bunch of questions an answers to similar issues, but my problem is somehow different.

I have a Java Client and C++ Server. Everything goes as expected until I'm using different machines (equal to other issues so far) The messages from the client seem to getting stuck in den TCP Buffer. When I finally close the socket, everything is sent to the server. But these single messages are controlmessages so I need them to be send immediatly. As far as I read this is expected behavior, but how do I send reliable control messages.

Is there a way to force the messages to be sent. (I can leave the socket open for a couple of minutes with nothing is sent.)

Is there something wrong? (see the following code)

Do I have to close the socket each time to perform a REAL flush?

Should I use UDP instead, with an additional amount of protocol work?

Javacode:

mSocketSend = new Socket();
mSocketSend.connect(new InetSocketAddress(mServerIp, mSocketPortSend), mTimeOut);
PrintWriter pw = new PrintWriter(mSocketSend.getOutputStream(), true);
pw.println(data);

C++ Code:

   opening socket...(i leave that)
   char* buffer = new char[1024];
   int rc = recv(mConnectedSocket, buf, 1024, 0);

If you want more of it. Write it. I left almost everything out. ^^ I dont think its relevant. The Communication wents great usually.. No errors at all. So its just this TCPBuffer thingi.

I know there should be some delimiter or message length stuff. But in fact: A message length, which is not sent, does not help. ^^

Thanks for your help.

EDIT #01 The whole bunch of code:

mSocket->createSocketServer(22);
    char* buffer = new char[1024];
 while(true){

        int numberBytes = mSocket->receiveChars(buffer, 1024);

        if (numberBytes > 0){
            uninterestingHandlingFunction(buffer);
        }else{
            mSocket->createSocketServer(22);
        }
    }

bool Socket::createSocketServer(u_short port)
{
    if (mConnectedSocket != INVALID_SOCKET)
    {
        closesocket(mConnectedSocket);
    }

    if (s == INVALID_SOCKET)
    {
        WSADATA wsa;

        if (WSAStartup(MAKEWORD(2,0), &wsa) != 0)
            return 0;

        s = socket(AF_INET, SOCK_STREAM, 0);
        if (s == INVALID_SOCKET)
            return 0;

        SOCKADDR_IN addr;
        memset(&addr, 0, sizeof(SOCKADDR_IN));
        addr.sin_family=AF_INET;
        addr.sin_port=htons(port);
        addr.sin_addr.s_addr=ADDR_ANY;

        if (bind(s, (SOCKADDR*)&addr, sizeof(SOCKADDR_IN)) == SOCKET_ERROR)
        {
            s = INVALID_SOCKET;
        } else if (listen(s, 10) == SOCKET_ERROR)
        {
            s = INVALID_SOCKET;
        }

        if (s == INVALID_SOCKET)
        {
            closesocket(s);
            return 0;
        }
    }
    mConnectedSocket = accept(s, NULL, NULL);

    if (mConnectedSocket == INVALID_SOCKET)
    {
        closesocket(s);
        return 0;
    }
    return 1;
}

int Socket::receiveChars(char* buf, unsigned maxSize)
{
    if (mConnectedSocket == INVALID_SOCKET)
        return -1;

    int rc = recv(mConnectedSocket, buf, maxSize, 0);
    if (rc == SOCKET_ERROR)
    {
        std::cout << "Socket: error " << WSAGetLastError() << std::endl;
    }
    return rc;
}

You wanted it....

EDIT #2 Give it one more try

There are few more things I tried out. At first: This problem does not occure on a device connected over real network everytime. -> Full Reboot Client&Server -> Problem does not occure -> Full Reboot Client&Server -> Problem occures

Sadly, I don't know what to take from this habit.

Another thing I stumbled over is the bind and listen socket (in Code SOCKET s). This socket listens for connections and if the working thread needs a new connection (on startup or if the previous closes) the socket s gives the next queued connection to mConnectedSocket for recv, other connections are backlogged while one is processed. From the Java view: a Socket is connected (Device A). next socket (Device B) tries to connect. -> Connection success (its properly controlled in code if this is happens indeed) -> followed by sending data in natural matter. (The socket is still in the backlog on c++ side)

Well, this is hard to transform to the habit I experienced. I'll try to express my thoughts. Javaside: PrintWriter is created. Feeded with data and is flushed. Because the connection is not fully established (No additional connectedSocket on C++ side). The flush doesn't work. And onClose the socket finally flushes its content.

Please tell me to shut up, if you think so. I dont really know what the "Connection is backlogged" ACTUALLY mean in implementation" ^^

I know, I should open a new thread for each connection, but I can't at the moment. So stick with this server code.

7
Have you tried pw.flush() ? - Mattias Isegran Bergander
PrintWriter(OutputStream, boolean autoflush) should do the work on '\n', but yes.. I tried that, too - xeed
buf = buffer? The server waits for 1024 bytes or a eof? - esej
Oh damn. yeah copied that together. The buffer is allocated somewhere else and it is passed as buf to the receiveFunc. The second question is something i dont really know. My current expectation is that recv return when atleast one byte can be received. - xeed
Yes it does return with the available bytes in C++ as well. - Mattias Isegran Bergander

7 Answers

2
votes

You need to do a flush(), that pushes the data out.

1
votes
PrintWriter.flush();

Or use a writer with automatic flushing.

You should also make sure that the server reads a line (until \n) and not the full 1024 chars, but I don't know what recv() does so I don't know about that.

1
votes

Fixed it. Kind of embarassing... The backlog, i noticed in my edit was indeed the problem. If two at clients at a time connect to the server, the second is backlogged and his messages would be processed, when the first disconnects.

Additionally (here comes the clue)

As mentioned before its an android java client. There is another thread on java side to receive data from the C++ server. This socket connects on another port. BUT i set the ports and ip addresses to connect to in a settings activity and there was a bad port for the other socket as default value (same as for the issuesocket, wrong variable taken) So this socket connects first and the issuesocket connects into the backlog. This default value is only taken if I enter the settings to set another IPAddress (For example, when I connect to a remote host instead of localhost)

Incredible circumstances... i didnt even wrote the settings...

WIRESHARK would have fixed this.

0
votes
PrintWriter pw = new PrintWriter(mSocketSend.getOutputStream(), true);
pw.println(data);
pw.flush();
0
votes

Actually your problem could be on the receiving end, your recv needs to be in a loop (you can google for examples of this). There is no guarantee not much each call to recv will get. If you know you are flushing the data on the Java site, that's probably your problem.

0
votes

As you are using auto flush and even tried using an explicit flush():

Could be because you don't open the inputStream. Try and do a getInputStream() as well.

Otherwise, Have you tried:

  1. any diff you don't use connect but just give the parameters directly in the Socket constructor?

  2. setTcpNoDelayon the socket (shouldn't cause minutes of delay though!!)?

0
votes

Your loop is incorrectly coded. Every new recv() will overwrite the previous one. You should advance the offset parameter.