Why does this function loop forever? - c++

I have this function that takes a SOCKET and a file name to the output file, then it tries to receive and output the data to a file, when compiling and running the code it works fine but the program does not exit and the output file (file.txt) contains some unreadable garbage text
the rest of the code work fine and tested, I am sure the problem is in this file but can't tell what and where
int recv_file(SOCKET soc, const char * fname)
{
FILE * ptr;
char buffer[MAX];
unsigned long long int x = 0;
if (fopen_s(&ptr, fname , "wb") != 0)
{
printf("Failed to receive file \n");
return 1;
}
do
{
c = recv(soc, buffer, MAX, 0);
printf("recv count : %d\n",c);
// here was -> fwrite(buffer, 1, sizeof(buffer), ptr);
fwrite(buffer, 1, c, ptr);
}
while ( c > 0);
printf("Total received : %lld\n", x);
fclose(ptr);
return 0;
}
as an output it prints only one line recv count : 65
I tried to use closesocket(soc);WSACleanup(); but this does not solve the problem
for the server, I tested with netcat and python socketserver and had same behavior for both
also the last printf right after the do-while does not get printed to the screen

Data from network can come in chuncks
recv returns negative value on errors that what break handles (stops loop).
recv returns number of bytes received.
fwrite should write number of bytes equal to those which has been received, so it should be:
fwrite(buffer, 1, c, ptr);
Passing there sizeof(buff) will record trash in a file (content form previous read or memory trash).
Now question is what sender does when there is no data. If nothing recv will block waiting for new data. If sender closes tcp connection then respective error should be reported by recv and loop should be ended.
If you are fetching data form some HTTP server then most probably server assumes that client will close connection. Server just waits for next request.
Note also that depending on SOCKET configuration recv can be blocking or not. So depending on that recv may return 0 on success when in fact there will be new data in future.

Related

I am getting some garbage value while sending file data through socket to server? Why?

I am setting up a client server connection through sockets in C++. I am successfully connecting them but while sending filesize and file data i am receiving some garbage values also in my server.
I am firstly sending File Size through send system call from client and then sending file Buffer to server.
I have recv system call in server which is successfully receiving Filesize, but while getting File data after some bytes i am getting garbage.
Client Code
File = fopen("index.jpg", "rb");
if (!File)
{
MessageBox(L"Error while readaing the file");
}
fseek(File, 0, SEEK_END);
Size = ftell(File);
fseek(File, 0, SEEK_SET);
char* Buffer = new char[Size];
fread(Buffer, Size, 1, File);
char cSize[MAX_PATH];
sprintf(cSize, "%lu", Size);
send(Socket, cSize, MAX_PATH, 0); // File size
send(Socket, Buffer, Size, 0); // File Binary
Server Code
unsigned long Size;
char *Filesize = new char[1024];
if (recv(Sub, Filesize, 1024, 0)) // File size
{
Size = strtoul(Filesize, NULL, 0); //getting filesize
}
Buffer = new char[Size];
int reader = recv(Sub, Buffer, Size, 0);
Buffer[Size] = '\0';
if (reader == -1) // File Binary
{
MessageBox(L"Perror Recv");
}
else if (reader == 0)
{
MessageBox(L"Connection is Closed");
}
else
{
FILE *File;
File = fopen("test.jpg", "wb");
fwrite((const char*)Buffer, 1, Size, File);
MessageBox(L"DATA Received");
fclose(File);
}
One problem is that you aren't handling the return values from recv() correctly. For example:
if (recv(Sub, Filesize, 1024, 0)) // File size
... when the function quoted above returns, it has written some number of bytes (more than 0, less than 1025) into Filesize. How many? Your program doesn't know, because you didn't store the return value into a variable to find out (rather you only checked it to see if it was non-zero, or not, then discarded the value). Therefore, it's entirely likely that Filesize contains not only your file size value, but the some portion of your file's data as well... which is why that portion of your file's data won't get written to disk later on in your program.
A similar problem is here:
int reader = recv(Sub, Buffer, Size, 0);
You check reader to see if it is -1 or 0 (which is good), but in your final case you just fwrite() out Size bytes from the array, when Buffer contains reader bytes, not Size bytes (and reader could have any value between 1 and Size, depending on how many bytes the TCP stack decided to deliver to you in that particular recv() call.
One more problem is that you send MAX_PATH bytes for the file size, but you receive (up to) 1024 bytes for file size. Is MAX_PATH equal to 1024? If not, then even if recv() did fill out all 1024 bytes, your sender and receiver would still be out of sync with each other, since the excess bytes would show up in future recv() calls, or (alternatively) you'd get bytes from subsequent send() calls placed into your FileSize buffer.
So that's the direct problem -- I think the underlying problem is that you are making some assumptions about how TCP network works that are not true. In particular:
There is no guarantee of a one-to-one correspondence between send() and recv() calls. (TCP is a byte-stream protocol and doesn't do any data-framing)
You cannot rely on N bytes of data from a single call to send() being delivered via a single call to recv(). The bytes you send() will be delivered in order, but there are no guarantees about how many calls to recv() it will require to receive them all, nor about how many bytes any given call to recv() will write into your receive-buffer.
You cannot rely on recv() to fill up the entire buffer you passed to it. recv() will write as few or as many bytes as it wants to, and it's up to your code to handle it correctly regardless of how many bytes it gets per recv() call.
In practice, this means you'll need to call recv() in a loop, and keep careful track of the return value from each recv() call, so you always know exactly how many bytes you've received so far and therefore where inside your buffer the next recv() call should start writing at.
You are not handling the responses of send and recv function make sure you collect that as there are number of bytes send and receive and further use them were ever required.

C++ Windows recv() doesn't return even if data are available

I'm writing a C++ program. I need to receive a file and I'm using recv() function over a TCP socket to do that.
download_file() {
while (left_bytes != 0 && !connection_closed) {
if (left_bytes >= buffer_max_size)
bytes_to_download = buffer_max_size;
else
bytes_to_download = left_bytes;
if (request.conn->read_data(buffer, bytes_to_download))
{
left_bytes -= buffer->get_size();
temporary_file.write_data(buffer);
} else connection_closed = true;
}
}
read_data() {
while (bytes_received < size && alive_) {
bytes_read = recv(sock_, read_buffer, size, 0);
if (bytes_read == SOCKET_ERROR) {
delete[] local_buffer;
throw SocketException(WSAGetLastError());
}
// the connection is closed
if (bytes_read == 0) alive_ = false;
else {
bytes_received += bytes_read;
buffer->add(local_buffer, bytes_read);
}
}
}
The problem is that the recv never returns. It receives the whole file except for few KB and it freeze on the recv(). The buffer size is 1460.
I receive the file only if I print something to the console with cout every time the recv is called. Only in this case I receive the whole file.
Otherwise if I set as socket option the WAITALL and the client closes the connection after the file is sent, I receive the whole file.
Here's the code for the Client side that sends the file:
TransmitFile(file_request->connection_->get_handle_socket(), file_handler.get_file_handle(), file_request->file_size_, 65535, nullptr, nullptr, TF_USE_SYSTEM_THREAD)
EDIT
Here's how I send and read the file size between the Client and Server.
std::stringstream stream_;
stream_.str(std::string());
// append the file size
const __int64 file_size = htonll(GetFileSize(file_handle_, nullptr););
stream_ << ' ' << file_size << ' ';
Then I use the send to send this string
Here's how I read the file size
// Within stream_ there is all the content of the received packet
std::string message;
std::getline(stream_, message, ' ');
this->request_body_.file_size_ = ntohll(strtoll(message.c_str(), nullptr, 0));
EDIT
I cleaned up the code and I found out that read_data() is obviously called once and I was updating the buffer variable wrongly. Hence I was tracking the size of the content within the buffer in a wrong way which make me call the recv() once more.
First thing: recv() will block if there are no bytes left to read but the connection is still open. So whatever you might say about what your code is doing, that must be what is happening here.
That could be for any of the following reasons:
the sender lied about the size of the file, or did not send the promised number of bytes
the file size was not interpreted correctly at the receiving end for whatever reason
the logic that 'counts down' the number of bytes left in the receiver is somehow flawed
Trouble is, looking at the code samples you have posted, it's hard to say which because the code is a bit muddled and, in my eyes, more complicated than it needs to be. I'm going to recommend you sort that out.
Sending the size of the file.
Don't mess about sending this as a string. Send it instead in binary, using (say) htonll() at the sending end and ntohll() at the receiving end. Then, the receiver knows to read exactly 8 bytes to figure out what's coming next. It's hard to get that wrong.
Sending the file itself.
TransmitFile() looks to be a good choice here. Stick with it.
Receiving the file and counting down how many bytes are left.
Take a closer look at that code and consider rewriting it. It's a bit of a mess.
What to do if it still doesn't work.
Check with WireShark that the expected data is being sent and then walk through the code in the receiver in the debugger. There is absolutely no excuse for not doing this unless you don't have a debugger for some reason, in which case please say so and somebody will try to help you. The fact that logging to cout fixes your problems is a red-herring. That just changes the timing and then it just happens to work right.
That's all. Best of luck.

How can I stop C++ recv() when string read is finished?

I am reading an Image URL sent from a Java client to a C++ server from Sockets. The server stops reading through recv() when it detects there is a null character in the char buffer[] as I do below in the following code:
void * SocketServer::clientController(void *obj)
{
// Retrieve client connection information
dataSocket *data = (dataSocket*) obj;
// Receive data from a client step by step and append data in String message
string message;
int bytes = 0;
do
{
char buffer[12] = {0};
bytes = recv(data->descriptor, buffer, 12, 0);
if (bytes > 0) // Build message
{
message.append(buffer, bytes);
cout << "Message: " << message << endl;
}
else // Error when receiving it
cout << "Error receiving image URL" << endl;
// Check if we are finished reading the image link
unsigned int i = 0;
bool finished = false;
while (i < sizeof(buffer) / sizeof(buffer[0]) && !finished)
{
finished = buffer[i] == '\0';
i++;
}
if (finished)
break;
}
while (bytes > 0);
cout << message << endl;
close(data->descriptor);
pthread_exit(NULL);
}
Is there a better and more elegant way to make this?
I read about sending first the size of the URL, but I do not know exactly how to stop recv() with it. I guess it is done by counting the bytes received until the size of the URL is reached. At that moment, we should be finished reading.
Another approach could be closing the Java socket so that recv() will return -1 and the loop will be finished. However, considering my Java client waits for a response from C++ server, closing the socket and then reopen it does not seem a suitable option.
Thank you,
Héctor
Apart from that your buffer has an unusual size (one typically chooses a power of 2, so 8, 16, 32, ...) and it looks a little small for your intent, your approach seems fine to me:
I assume that your java client will send a null terminated string and then wait anyway, i. e. especially it does not send any further data. So after you received the 0 character, there won't be any data to receive any more anyway, so there is not need to bother for something explicitly that recv does implicitly (recv normally returns only the data available, even if less than the buffer could consume).
Be aware that you initialized buffer with 0, so if you check the entire buffer (instead of the range [buffer, buffer + bytes), you might detect a false positive (if you receive less than 12 characters in the first iteration)! Detection of the 0 character can be done more elegantly, though, anyway:
if(std::find(buffer, buffer + bytes, 0) < buffer + bytes)
{
// found the 0 character!
break;
}

receive from unix local socket and buffer size

I'm having a problem with unix local sockets. While reading a message that's longer than my temp buffer size, the request takes too long (maybe indefinitely).
Added after some tests:
there is still problem with freeze at ::recv. when I send (1023*8) bytes or less to the UNIX socket - all ok, but when sended more than (1023*9) - i get freeze on recv command.
maybe its FreeBSD default UNIX socket limit or C++ default socket settings? Who know?
i made some additational tests and I am 100% sure that its "freeze" on the last 9th itteration when executing ::recv command, when trying to read message >= (1023*9) bytes long. (first 8th itterationg going well.)
What I'm doing:
The idea is to read in a do/while loop from a socket with
::recv (current_socket, buf, 1024, 0);
and check buf for a SPECIAL SYMBOL. If not found:
merge content of buffer to stringxxx += buf;
bzero temp buf
continue the ::recv loop
How do I fix the issue with the request taking too long in the while loop?
Is there a better way to clear the buffer? Currently, it's:
char buf [1025];
bzero(buf, 1025);
But I know bzero is deprecated in the new c++ standard.
EDIT:
*"Why need to clean the buffer*
I see questions at comments with this question. Without buffer cleanup on the next(last) itteration of reading to the buffer, it will contain the "tail" of first part of the message.
Example:
// message at the socket is "AAAAAACDE"
char buf [6];
::recv (current_socket, buf, 6, 0); // read 6 symbols, buf = "AAAAAA"
// no cleanup, read the last part of the message with recv
::recv (current_socket, buf, 6, 0);
// read 6 symbols, but buffer contain only 3 not readed before symbols, therefore
// buf now contain "CDEAAA" (not correct, we waiting for CDE only)
When your recv() enters an infinite loop, this probably means that it's not making any progress whatsoever on the iterations (i.e., you're always getting a short read of zero size immediately, so your loop never exits, because you're not getting any data). For stream sockets, a recv() of zero size means that the remote end has disconnected (it's something like read()ing from a file when the input is positioned at EOF also gets you zero bytes), or at least that it has shut down the sending channel (that's for TCP specifically).
Check whether your PHP script is actually sending the amount of data you claim it sends.
To add a small (non-sensical) example for properly using recv() in a loop:
char buf[1024];
std::string data;
while( data.size() < 10000 ) { // what you wish to receive
::ssize_t rcvd = ::recv(fd, buf, sizeof(buf), 0);
if( rcvd < 0 ) {
std::cout << "Failed to receive\n"; // Receive failed - something broke, see errno.
std::abort();
} else if( !rcvd ) {
break; // No data to receive, remote end closed connection, so quit.
} else {
data.append(buf, rcvd); // Received into buffer, attach to data buffer.
}
}
if( data.size() < 10000 ) {
std::cout << "Short receive, sender broken\n";
std::abort();
}
// Do something with the buffer data.
Instead of bzero, you can just use
memset(buf, 0, 1025);
These are 2 separate issues. The long time is probably some infinite loop due to a bug in your code and has nothing to do with the way you clear your buffer. As a matter of fact you shouldn't need to clear the buffer; receive returns the number of bytes read, so you can scan the buffer for your SPECIAL_SYMBOL up to that point.
If you paste the code maybe I can help. more.
Just to clarify: bzero is not deprecated in C++ 11. Rather, it's never been part of any C or C++ standard. C started out with memset 20+ years ago. For C++, you might consider using std::fill_n instead (or just using std::vector, which can zero-fill automatically). Then again, I'm not sure there's a good reason to zero-fill the buffer in this case at all.

Select on socket messes up with data

I'm sending some data trough the socket, but I need to set the timeout.
I'm using something like:
fd_set rfds;
struct timeval tv;
int retval;
/* Watch stdin (fd 0) to see when it has input. */
FD_ZERO(&rfds);
FD_SET(sockDesc, &rfds);
/* Wait up to five seconds. */
tv.tv_sec = 5;
tv.tv_usec = 0;
retval = select(sockDesc + 1, &rfds, NULL, NULL, &tv);
if(rtn = ::recv(sockDesc, (raw_type*) buffer, bufferLen, 0)) < 0){
throw SocketException("error", true);
}
return rtn;
So.
Depending the type of data I return I need to implement timeout or not.
If I just send text data I don't need, if I send one file I need...
To explain more or less I'm sending some data through a socket and processing on the other side.
So, if I send one tar:
while(readtar){
senddata
get processed data
}
but sometimes the data sent is just the header, so when the other side process data it doesn't needs to return data and the socket stops on read.
To illustrate:
cat file.tar | myprogram -c "tar -zvt"
So it don't return nothing until receive enough data to return the name of file.
If i just send one file and returns the "cat" i dont have this problem
echo "asjdoiajdlaijdkasdjlkas" | myprogram -c "cat"
or
cat HUGEFILE.tar | myprogram -c "cat" | tar -zvt
In this case it does the same thing, but is not on the server side... so it dont work for me.
Now.... If I just use the recv without the select when I return the data using the cat it works... no problems with that.
BUT if I implement the select the data comes messed up.
WITHOUT SELECT
send "command line text temp test"
recv "command line text temp test"
WITH
send "command line text temp test"
recv "commmand lin/ˆ
1k5d99ck"
it's just to illustrate what is happening
Client loop:
while(size = read(fileno(stdin), thebuffer, 10000)){
sock->send(thebuffer, size); // if this data is not enough the other side never sends the data back
sock->recv //receive data
}
On the other side I do
if(pid == 0){
//stuffs closes, dup2
execlp("bash", "bash", "-c", "run.c_str(), NULL); // if i use one tar -zvt i need a bunch of data to generate the return
}
else
while(size = read(fout[0], buffer, 10000) > 0)){
sock->send(buffer, size);
}
So.. if the data sent is not enough to generate on
If I could check if read have anything or on the stdin side if the execlp send a terminator I could solve the problem
It sounds like you are expecting your TCP recv() to return data to you in chunks of a particular size... however, TCP recv() does not work that way. TCP is stream-based, so the number of bytes returned by recv() may vary anywhere between 1 (or 0 if you are using non-blocking I/O) and the size of the buffer you passed in. It's then up to your receiving code to loop as necessary to re-concatenate the received data again.
Also, it looks like you are trying to print out un-terminated ASCII strings -- that would explain the garbage characters at the end of your second recv() example. (i.e. if you want to print out the received data bytes as a string, be sure to place a NUL/0 byte after the last received byte; recv() won't do that for you)