3

I am writing a Java TCP client which sends chunks of data to a C server. The client-server worked very well on my development PC. This code upon deployment on a hardware board showed packet loss. I only have the logs with me and I know that the server did not receive all packets. I do not have the hardware to test. Therefore, at the first level, I want to be very sure client code does send all the required data.

Here is my code(the client part in Java). How do I make sure this is done? Is there some resend commands with timings etc?

        Socket mySocket = new Socket("10.0.0.2",2800);
        OutputStream os = mySocket.getOutputStream();         

        System.out.println(" Sending 8 byte Header Msg with length of following data to Server");
        os.write(hdr, 0, 8);  
        os.flush();

        System.out.println(" Sending Data ");
        start = 0;
        for(int index=0; index < ((rbuffer.length/chucksize)+1); index++){             
            if(start + chucksize > rbuffer.length) {
                System.arraycopy(rbuffer, start, val, 0, rbuffer.length - start);
            } else {
                System.arraycopy(rbuffer, start, val, 0, chucksize);
            }
            start += chucksize ;

            os.write(val,0,chucksize);  
            os.flush();
        }

Here is the C snippet which receives this data:

while ((bytes_received = recv(connected, rMsg, sizeof(rMsg),0)) > 0){

 if (bytes_received  > 0)      // zero indicates end of transmission */
    {
    /* get length of message (2 bytes) */
    tmpVal = 0;
    tmpVal |= rMsg[idx++];
    tmpVal = tmpVal << 8;
    tmpVal |= rMsg[idx++];
    msg_len = tmpVal;

    len = msg_len;
    //printf("msg_len = %d\n", len);
    printf("length of following message from header message : %d\n", len);
    char echoBuffer[RCVBUFSIZE] ;        
    memset(echoBuffer, 0, RCVBUFSIZE);
    int recvMsgsize = 0;

    plain=(char *)malloc(len+1);
    if (!plain)
    {
     fprintf(stderr, "Memory error!");
    }
    for( i = RCVBUFSIZE; i < (len+RCVBUFSIZE); i=i+RCVBUFSIZE){
     if(i>=len){
         recvMsgSize = recv(connected, echoBuffer, (len - (i-RCVBUFSIZE)), 0);
         memcpy(&plain[k], echoBuffer, recvMsgSize);
         k = k+recvMsgSize;
     }
     else{
         recvMsgSize = recv(connected, echoBuffer, RCVBUFSIZE, 0);
         memcpy(&plain[k], echoBuffer, recvMsgSize);
         k = k+recvMsgSize;
      }
    }
 }//closing if
}//closing while
user489152
  • 907
  • 1
  • 23
  • 42
  • How do you know packets were lost? TCP is a guaranteed delivery protocol. That means that if either the write or read fails then there will be and error. Can this be an error in application? – Miserable Variable Sep 15 '11 at 13:11
  • That is what beats me. TCP is a reliable protocol. I shall post the snippet of my C side server which receives the chunks. It could be a hardware issue too. I am a noob in Java and just want to be sure if there are any ways to resend the packets. Of course the kernel must be doing this for protocol reliability...how do you see it? – user489152 Sep 15 '11 at 13:20
  • Packets may be dropped at i/p layer, but TCP would resend them etc and deliver to reciever in the order they were sent. I think some bug in your server or client. – Miserable Variable Sep 15 '11 at 13:24
  • How do you read data into the `len` variable in the C code ? – nos Sep 26 '11 at 15:51
  • @nos: I first send an 8 byte header of which 2 bytes denote the length of the data that would be followed. See updated code on top – user489152 Sep 27 '11 at 09:23
  • @user489152 Well, that code is wrong, how big is rMsg ? If it's 2 bytes, you don't handle the case where recv returns just 1 byte. Which it can, and someday will. If it's larger than 2 bytes, you're throwing away all the other bytes it might receive. – nos Oct 01 '11 at 03:41

2 Answers2

5

First of all there is no such thing as packet loss in TCP/IP. This protocol was designed to reliably send a stream of bytes in correct order. So the problem must be with your application or the other side.

I am not really in a mood to analyze this whole arraycopy() madness (C anyone?), but why aren't you just sending the whole rbuffer in one go through BufferedOutputStream?

OutputStream os = new BufferedOutputStream(mySocket.getOutputStream());

and then:

os.write(rbuffer);

Believe me, BufferedOutputStream is doing the exact same thing (collecting bytes into chunks and sending them in one go). Or maybe I am missing something?

Tomasz Nurkiewicz
  • 334,321
  • 69
  • 703
  • 674
  • @user489152: So the data changes while you're in the for loop? – home Sep 15 '11 at 13:35
  • No the data doesnt change. I had set a buffer cos I was afraid I have to receive huge chunks that may clog the network and perhaps be beyond the TCP window that's all. – user489152 Sep 15 '11 at 13:46
  • @Tomasz: Thanks for the input. I was not aware of the BufferedOutputStream. Should there be any specific measures taken care of at the C side when uing this? – user489152 Sep 15 '11 at 13:48
  • 2
    @user489152, one thing to bear in mind is that Java is big-endian and C has the endianness of your processor, so if you're sending a byte stream built from Java primitives, you may need to reverse the byte order on the C side. – Gnat Sep 15 '11 at 13:57
  • @Gnat: Thanks. but as such my code works on my development system. I receive the exact data that I sent. However there seems to be some packet loss when run on hardware – user489152 Sep 15 '11 at 14:34
  • Is there anyway of making sure the packets are sent? Like a "write fully" similar to readFully? Or is the flush enough?? – user489152 Sep 16 '11 at 07:45
  • Can I have a while loop(like in C) checking by reading the stream and writing until it is negative?? Please help – user489152 Sep 16 '11 at 07:53
0

I changed the C side program in the following way and it now works:

                    printf("length of following message from header message : %d\n", len);

                    plain=(char *)malloc(len+1);
                    if (!plain)
                    {
                        fprintf(stderr, "Memory error!");

                    }
                    memset(plain, 0, len+1);
                    int remain = len;
                    k= 0;
                    while (remain){
                        int toGet = remain > RCVBUFSIZE ? RCVBUFSIZE : remain;
                        remain -= toGet;
                        int recvd = 0;
                        while(recvd < toGet) {
                            if((recvMsgSize = recv(connected, echoBuffer, toGet-recvd, 0)) < 0){
                                printf("error receiving data\n");
                            }
                            memcpy(&plain[k], echoBuffer, recvMsgSize);
                            k += recvMsgSize;
                            printf("Total data accumulated after recv input %d\n", k);
                            recvd += recvMsgSize;
                        }
                    }
user489152
  • 907
  • 1
  • 23
  • 42
  • 1
    Why are you subtracting `toGet` from `remain`? If your `recv` (for some reasons) reads less than `toGet` bytes, then you will ignore the end of the stream. To be save you should subtract `recvMsgSize` instead. – Joachim Sauer Sep 26 '11 at 12:52
  • My recv function gets toGet-recvd bytes where recvd += recvMsgSize – user489152 Sep 27 '11 at 08:12
  • what do you think of it? Is this a good way? Check out the problem I have related to this code : http://stackoverflow.com/questions/7555242/corrupted-tcp-packets-during-flooding-attack-c-server-java-client – user489152 Sep 27 '11 at 08:49