tags:

views:

350

answers:

6

Following my previous question (Why do I get weird results when reading an array of integers from a TCP socket?), I have come up with the following code, which seems to work, sort of. The code sample works well with a small number of array elements, but once it becomes large, the data is corrupt toward the end.

This is the code to send the array of int over TCP:

#define ARRAY_LEN 262144

long *sourceArrayPointer = getSourceArray();

long sourceArray[ARRAY_LEN];
for (int i = 0; i < ARRAY_LEN; i++)
{
    sourceArray[i] = sourceArrayPointer[i];
}

int result = send(clientSocketFD, sourceArray, sizeof(long) * ARRAY_LEN);

And this is the code to receive the array of int:

#define ARRAY_LEN 262144

long targetArray[ARRAY_LEN];
int result = read(socketFD, targetArray, sizeof(long) * ARRAY_LEN);

The first few numbers are fine, but further down the array the numbers start going completely different. At the end, when the numbers should look like this:

0
0
0
0
0
0
0
0
0
0

But they actually come out as this?

4310701
0
-12288
32767
-1
-1
10
0
-12288
32767

Is this because I'm using the wrong send/recieve size?

+5  A: 

Is the following ok?

for (int i = 0; sourceArrayPointer < i; i++)

You are comparing apples and oranges (read pointers and integers). This loop doesnot get executed since the pointer to array of longs is > 0 (most always). So, in the receiving end, you are reading off of from an unitialized array which results in those incorrect numbers being passed around).

It'd rather be:

for (int i = 0; i < ARRAY_LEN; i++)
dirkgently
Must have been a typo. How would the first line compile? (or is my C too rusty)
erikkallen
@erikkallen:That is why there is a question in the first line.
dirkgently
On the Intel architectures we're used to, both an "int" and a "long*" (pointer to a long) are 32-bit values. An "int" is 32 bits, and a "pointer to a long" is a memory address which is 32-bits wide. The compiler will let you do the comparison (but it probably throws a warning)
rascher
Also: assuming that sizeof(long*) == sizeof(int) is not portable!
rascher
Sorry, that wasn't the issue - I had re-typed it from the original code and made a typo. The issue still persists. I printed the results after the for loop and the data to be transmitted appeared to be accurate; the issue is in the usage of read and not the for statement.
nbolton
+1  A: 

Use functions from <net/hton.h>

http://en.wikipedia.org/wiki/Endianness#Endianness_in_networking

vartec
I think dirkgently's answer is the problem the OP is seeing, but your point is valid and needs to be addressed. +1
rmeador
A: 

Not related to this question, but you also need to take care of endianness of platforms if you want to use TCP over different platforms.

It is much simpler to use some networking library like curl or ACE, if that is an option (additionally you learn a lot more at higher level like design patterns).

Amit Kumar
A: 

There is nothing to guarantee how TCP will packet up the data you send to a stream - it only guarantees that it will end up in the correct order at the application level. So you need to check the value of result, and keep on reading until you have read the right number of bytes. Otherwise you won't have read the whole of the data. You're making this more difficult for yourself using a long array rather than a byte array - the data may be send in any number of chunks, which may not be aligned to long boundaries.

Pete Kirkham
+6  A: 

The call to read(..., len) doesn't read len bytes from the socket, it reads a maximum of len bytes. Your array is rather big and it will be split over many TCP/IP packets, so your call to read probably returns just a part of the array while the rest is still "in transit". read() returns how many bytes it received, so you should call it again until you received everything you want. You could do something like this:

long targetArray[ARRAY_LEN];

char *buffer = (char*)targetArray;
size_t remaining = sizeof(long) * ARRAY_LEN;
while (remaining) {
  ssize_t recvd = read(socketFD, buffer, remaining);
  // TODO: check for read errors etc here...
  remaining -= recvd;
  buffer += recvd;
}
sth
This sounds like the most logical answer. I will test this out now.
nbolton
Hmm, the result from read(...) doesn't seem to correlate at all with the actual number of valid int elements read. It also seems to change each time I read (i.e sometimes its ~65593 and others its ~49923). Regardless, I will read in chunks as I have done previously.
nbolton
This line doesn't seem to compile: buffer += recvd; I get the error: pointer of type ‘void *’ used in arithmetic
nbolton
Hm, right, maybe you should make it char* instead.
sth
A: 

I see a number of problem's here. First, this is how I would rewrite your send code as I understand it. I assume getSourceArray always returns a valid pointer to a static or malloced buffer of size ARRAY_LEN. I'm also assuming you don't need sourceArrayPointer later in the code.

#define ARRAY_LEN 262144

long *sourceArrayPointer = getSourceArray();

long sourceArray[ARRAY_LEN];
long *sourceArrayIdx = sourceArray;

for (; sourceArrayIdx < sourceArray+ARRAY_LEN ; )
    sourceArrayIdx++ = sourceArrayPointer++;

int result = send(clientSocketFD, sourceArray, sizeof(long) * ARRAY_LEN);
if (result < sizeof(long) * ARRAY_LEN)
    printf("send returned %d\n", result);

Looking at your original code I'm guessing that your for loop was messed up and never executing resulting in you sending whatever random junk happens to be in the memory sourceArray points to. Basically your condition

sourceArrayPointer < i;

is pretty much guaranteed to fail the first time through.

Robert S. Barnes
Again, regarding your last point, that was a typo, this bug does not exist in the original code. Thanks anyway.
nbolton