Reputation: 49
I'm trying to read in an integer which is going to let the server know the message length. I read until I reach sizeof(int)
bytes using a while loop. I'm following the same convention for the message and length using a while loop to call recv multiple times. If all I'm doing is reading a int can I just call recv directly and expect all the bytes?
If not then how should I read in a integer using a while loop.
struct CONN_STAT {
int size; // length function should return the length into this field
int nRecv; // bytes sent of message
int nSent; // bytes received of message
int lRecv; // bytes received of length
int lSent; // bytes received of length
};
server : How I'm reading length
I copied the logic from my message function it reads similar to this but info is replaced by a char array and the (info + pStat->lRecv) works for it
int readLength(int sockfd, int * info, struct CONN_STAT * pStat){
int infoSize = sizeof(int);
// I copied the logic from my message function it reads similar to this
// but info is replaces by a char array and the (info + pStat->lRecv) works for
while(pStat->lRecv < infoSize){
int n = recv(sockfd, info + pStat->lRecv, infoSize - pStat->lRecv, 0);
if (n > 0) {
pStat->lRecv += n;
}
else if (n == 0 || (n < 0 && errno == ECONNRESET)) {
close(sockfd);
return -1;
}else if (n < 0 && (errno == EWOULDBLOCK)) {
//The socket becomes non-readable. Exit now to prevent blocking.
//OS will notify us when we can read
return 0;
}else {
printf("Unexpected recv error.");
}
}
return 0;
}
Calling it like this
readLength(sockfd, (int*)pStat->size, pStat);
error: warning: cast to 'int *' from smaller integer type 'int' [-Wint-to-pointer-cast]
Upvotes: 0
Views: 551
Reputation: 73294
If all I'm doing is reading a int can I just call recv directly and expect all the bytes?
Generally speaking, no. TCP is a byte-streaming protocol, so it doesn't guarantee anything about how many bytes will be delivered by any one call to recv()
. It's entirely possible (and therefore, given enough time, inevitable) that you'll recv()
only the first part of the integer from a given recv()
call, and you'll need to save the bytes you've received into a buffer somewhere and plan to append the rest of the bytes to that buffer later on. You can only actually parse/use the received integer after you've collected the whole set of bytes that were used to represent it.
If not then how should I read in a integer using a while loop.
Pretty much the same way you are (presumably) reading in the data-payload that follows the integer: write any received bytes into an array until the array has the number of bytes in it that are required to parse it. (In this case, you need to have sizeof(int)
bytes in your array before you can read the integer as an integer... and don't forget that sizeof(int)
may be a different value on different machines, and that an int
may be represented in either big-endian or little-endian form. You might want to use int32_t
instead of int
, and htonl()
and ntohl()
to handle any necessary endian-conversion)
Since you're using non-blocking I/O, I suggest putting your collection-buffer into the CONN_STAT
struct, so that a given call to readLength()
can update the array with any received bytes and then a subsequent call can update the array some more, and so on.
The way I think about is to just see it as receiving two data-buffers: The first buffer I can assume the size of -- it will always be sizeof(int)
bytes long. The second buffer I will know the size of as soon as I have received the entire first buffer and can read what it contains. So I can use (almost) the same logic for both of the two buffers, and then repeat as necessary.
Upvotes: 4