recv() issues with delay - c++

This question has been asked a number of times, I have noted, but none of the solutions seem to be applicable to me. Before I continue I will post a little bit of code for you:
// Await the response and stream it to the buffer, with a physical limit of 1024 ASCII characters
stringstream input;
char buffer[4096*2];
while (recv(sock, buffer, sizeof(buffer) - 1, MSG_WAITALL) > 0)
input << buffer;
input << '\0';
// Close the TCP connection
close(sock);
freehostent(hostInfo);
And here is my request:
string data;
{
stringstream bodyStream;
bodyStream
<< "POST /api/translation/translate HTTP/1.1\n"
<< "Host: elfdict.com\n"
<< "Content-Type: application/x-www-form-urlencoded\n"
<< "Content-Length: " << (5 + m_word.length())
<< "\n\nterm=" << m_word;
data = bodyStream.str();
}
cout << "Sending HTTP request: " << endl << data << endl;
I am very new to this sort of programming (and stack overflow- preferring to slog it out and bang my head against a wall until I solve problems myself but I'm lost here!) and would really appreciate help working out why it takes so long! I've looked into setting it up so that it is non-blocking, but had issues getting that to work as expected. Though maybe people here could point me in the right direction, if the non-bocking route is the way I need to go.
I have seen that a lot of people prefer to use libraries but I want to learn to do this!
I'm also new to programming on the mac and working with sockets. Probably not the best first time project maybe, but I've started now! So I wish to continue :) Any help would be nice!
Thank you in advance!

The reason why it takes a long time to receive is because you tell the system to wait until it has received all data you ask for, i.e. 8k bytes, or there is an error on the connection or it is closed. This is what the flag MSG_WAITALL does.
One solution to this is to make the socket non-blocking, and do a continuous read in a loop until we get an error or the connection is closed.
How to make a socket non-blocking differs depending on platform, on Windows it done with the ioctlsocket function, on Linux or similar systems it is done with the fcntl function:
int flags = fcntl(sock, F_GETFL, 0);
flags |= O_NONBLOCK;
fcntl(sock, F_SETFL, flags);
Then you read from the socket like this:
std::istringstream input;
for (;;)
{
char buffer[256];
ssize_t recvsize;
recvsize = recv(sock, buffer, sizeof(buffer) - 1, 0);
if (recvsize == -1)
{
if (errno != EAGAIN && errno != EWOULDBLOCK)
break; // An error
else
continue; // No more data at the moment
}
else if (recvsize == 0)
break; // Connection closed
// Terminate buffer
buffer[recvsize] = '\0';
// Append to input
input << buffer;
}
The problem with the above loop is that if no data is ever received, it will loop forever.
However, you have a much more serious problem in your code: You receive into a buffer, and then you append it to the stringstream, but you do not terminate the buffer. You do not need to terminate the string in the stream, it's done automatically, but you do need to terminate the buffer.
This can be solved like this:
int rc;
while ((rc = recv(sock, buffer, sizeof(buffer) - 1, MSG_WAITALL)) > 0)
{
buffer[rc] = '\0';
input << buffer;
}

The problem here happens because you are specifying MSG_WAITALL flag. It forces the recv to remain blocked until all the specified bytes are received (sizeof(buffer) - 1 in your case, while the message being sent by the other party is obviously smaller) or an error occurs and it returns -1 with errno variable being set appropriately.
I think, a more preferable option would be to cause recv without any flags in a loop until the socket on the other end is closed (recv returns 0) or some separator is received.
However, you should be careful using input << buffer, because recv might return only a small portion of data (for example, 20 bytes) on each iteration, so you should put exactly this amount of data to string stream. The number of bytes received is returned by recv.

Related

problem with comparing two string/chars in c++

So i have created a java server and a c++ client.
The java server sends a message with a printwriter to the c++ client to execute a command (the data transfer is correct, no problems with that)
Im using a strcmp() to check if the string that client recieved with recv() is the string i want but when i try to check it, it doesn't work. I've tried to print out the line with the recieved buffer and i dont see any problems.
Here is the code that recieves and checks the buffer(c++, ignore some values becouse this is a small piece of the code)
char buffer[1024];
if (recv(s, buffer, sizeof(buffer), 0) == SOCKET_ERROR) {
cout << "Error CR#001" << endl;
return -1;
}
if (strcmp(buffer, "||clear") == 0) {
system("cls");
return 1;
}
In c++ you can use std::string for the buffer:
const ssize_t MAX_BYTES = 1024;
ssize_t noreceivedbytes;
std::string buffer;
buffer.reserve(MAX_BYTES + 1);
noreceivedbytes = recv(s, buffer.data(), MAX_BYTES, 0)
if (noreceivedbytes <= 0) {
cout << "Error CR#001" << endl;
return -1;
}
buffer.data()[noreceivedbytes] = '\0';
if (buffer == "||clear") {
system("cls");
return 1;
}
Safer c solution for completeness:
#define MAX_BYTES = 1024;
ssize_t noreceivedbytes;
chat buffer[MAX_BYTES];
noreceivedbytes = recv(s, buffer, MAX_BYTES - 1, 0)
if (noreceivedbytes <= 0) {
cout << "Error CR#001" << endl;
return -1;
}
buffer[noreceivedbytes] = '\0';
if (strcmp(buffer, "||clear") == 0) {
system("cls");
return 1;
}
Please note:
This answer brings you only over the top of the iceberg. There are many more things that could go wrong when dealing with sockets (as mentioned by others in comments).
recv() doesn't guarantee that the whole chunk of data sent from the server will be read completely. You could easily end up with partial strings like "||cle" or "||c".
The least thing you'll need to do is to receive the bytes from the socket in a loop, until you have something at hand you can reasonably parse and match.
The simplest way to do so is to define a very primitive protocol, which preceeds payload data sent with it's size (take care of endianess problems when converting the size sent as integer value from the received data).
Having that at hand, you'll know exactly how many bytes you have to read until you have the payload chunk completed, such it can be parsed and compared reasonably.
How to do all that in detail exactly would lead to far to be answered here. There are whole books written about the topic (I'd recommend Stevens, "Unix network programming").

Segmentation fault on the second call of the function?

Edit (solution)
I've followed the advice of debugging with -fsanitize=address & valgrind. I only used -fsanitize (which I never heard of before) and found out what was the problem, there was a left over call for a destructor in another function and the object was being destroyed twice. The memory was completely jeopardised at this point.
Thanks a lot for the help and the other recommendations too.
I'm writing a code in C++ to talk with CouchDB using sockets (CouchDB is a Database by Apache that has an HTTP API). I've created a whole class to deal with it and it's basically a socket client that connects and closes.
One of my functions is to send an HTTP request and then read the response and work with it, it works fine on the first call, but fails when I call it a second time.
But it's inconsistent where it fails, sometimes it's a SEGFAULT inside of it in one of the string functions, other times it's a SIGABORT in the return. I've signalled the lines where it crashed with ->
And the worst part is that it only fails when it runs for the "second" time, which is actually the 10th time. Explanation: When the class is instantiated a socket is created, sendRequest is called 8 times (all work, always), I close the socket. Then I have another class that controls a socket server, which receives commands and creates a remote user object that executes the command, the remote user command then calls the CouchDB class to manipulate the DB. The first time a command is requested works, but the second fails and crashes the program.
Extra info: In the short int httpcode line, gdb trace shows it's a crash on substr, on the SIGABORT crash trace shows a problem on free().
I've already debugged many times, made some changes as to where and how to instantiate the string and the buffer and I'm lost. Anyone knows why it would work fine many times but crash on a subsequent call?
CouchDB::response CouchDB::sendRequest(std::string req_method, std::string req_doc, std::string msg)
{
std::string responseBody;
char buffer[1024];
// zero message buffer
memset(buffer, 0, sizeof(buffer));
std::ostringstream smsg;
smsg << req_method << " /" << req_doc << " HTTP/1.1\r\n"
<< "Host: " << user_agent << "\r\n"
<< "Accept: application/json\r\n"
<< "Content-Length: " << msg.size() << "\r\n"
<< (msg.size() > 0 ? "Content-Type: application/json\r\n" : "")
<< "\r\n"
<< msg;
/*std::cout << "========== Request ==========\n"
<< smsg.str() << std::endl;*/
if (sendData((void*)smsg.str().c_str(), smsg.str().size())) {
perror("#CouchDB::sendRequest, Error writing to socket");
std::cerr << "#CouchDB::sendRequest, Make sure CouchDB is running in " << user_agent << std::endl;
return {-1, "ERROR"};
}
// response
int len = recv(socketfd, buffer, sizeof(buffer), 0);
if (len < 0) {
perror("#CouchDB::sendRequest, Error reading socket");
return {-1, "ERROR"};
}
else if (len == 0) {
std::cerr << "#CouchDB::sendRequest, Connection closed by server\n";
return {-1, "ERROR"};
}
responseBody.assign(buffer);
// HTTP code is the second thing after the protocol name and version
-> short int httpcode = std::stoi(responseBody.substr(responseBody.find(" ") + 1));
bool chunked = responseBody.find("Transfer-Encoding: chunked") != std::string::npos;
/*std::cout << "========= Response =========\n"
<< responseBody << std::endl;*/
// body starts after two CRLF
responseBody = responseBody.substr(responseBody.find("\r\n\r\n") + 4);
// chunked means that the response comes in multiple packets
// we must keep reading the socket until the server tells us it's over, or an error happen
if (chunked) {
std::string chunkBody;
unsigned long size = 1;
while (size > 0) {
while (responseBody.length() > 0) {
// chunked requests start with the size of the chunk in HEX
size = std::stoi(responseBody, 0, 16);
// the chunk is on the next line
size_t chunkStart = responseBody.find("\r\n") + 2;
chunkBody += responseBody.substr(chunkStart, size);
// next chunk might be in this same request, if so, there must have something after the next CRLF
responseBody = responseBody.substr(chunkStart + size + 2);
}
if (size > 0) {
len = recv(socketfd, buffer, sizeof(buffer), 0);
if (len < 0) {
perror("#CouchDB::sendRequest:chunked, Error reading socket");
return {-1, "ERROR"};
}
else if (len == 0) {
std::cerr << "#CouchDB::sendRequest:chunked, Connection closed by server\n";
return {-1, "ERROR"};
}
responseBody.assign(buffer);
}
}
// move created body from chunks to responseBody
-> responseBody = chunkBody;
}
return {httpcode, responseBody};
}
The function that calls the above and that sometimes SIGABORT
bool CouchDB::find(Database::db db_type, std::string keyValue, std::string &value)
{
if (!createSocket()) {
return false;
}
std::ostringstream doc;
std::ostringstream json;
doc << db_name << db_names[db_type] << "/_find";
json << "{\"selector\":{" << keyValue << "},\"limit\":1,\"use_index\":\"index\"}";
-> CouchDB::response status = sendRequest("POST", doc.str(), json.str());
close(socketfd);
if (status.httpcode == 200) {
value = status.body;
return true;
}
return false;
}
Some bits that you might have questions about:
CouchDB::response is a struct {httpcode: int, body: std::string}
CouchDB::db is an enum to choose different databases
sendData only sends anything as bytes until all bytes are sent
Make it int len = recv(socketfd, buffer, sizeof(buffer), 0); might be overwriting the last '\0' in your buffer. One might be tempted to use sizeof(buffer) - 1 but this would be wrong as you might be getting null bytes in your stream. So, do this instead: responseBody.assign(buffer, len);. Only do this of course after you've made sure len >= 0, which you do in your error checks.
You have to do that every place where you call recv. Though, why you're using recv instead of read is beyond me, since you aren't using any of the flags.
Also, your buffer memset is pointless if you do it my way. You should also declare your buffer right before you use it. I had to read through half your function to figure out if you did anything with it. Though, of course, you do end up using it a second time.
Heck, since your error handling is basically identical in both cases, I would just make a function that did it. Don't repeat yourself.
Lastly, you play fast and loose with the result of find. You might not actually find what you're looking for and might get string::npos back instead, and that'd also cause you interesting problems.
Another thing, try -fsanitize=address (or some of the other sanitize options documented there) if you're using gcc or clang. And/or run it under valgrind. Your memory error may be far from the code that's crashing. Those might help you get close to it.
And, a very last note. Your logic is totally messed up. You have to separate out your reading data and your parsing and keep a different state machine for each. There is no guarantee that your first read gets the entire HTTP header, no matter how big that read is. And there is no guarantee that your header is less than a certain size either.
You have to keep reading until you've either read more than you're willing to for the header and consider it an error, or until you get the CR LN CR LN at the end of the header.
Those last bits won't cause your code to crash, but will cause you to get spurious errors, especially in certain traffic scenarios, which means that they will likely not show up in testing.

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;
}

Download file, winsock recv() to fstream write, file corrupted

Im trying to download a file from my website using winsock. i faced countless problems and now im able to download the file, but its corrupted.
It doesnt work with any file extension. Text and pictures end up corrupted, audio files too. With binary files i can see this error upon execution "program too big to fit in memory".
First i send() a Head request to the server to know the content-leght (size of file to download), then i send a Get request and i recv into a buffer. After recv is done i write the file.
I tried to write a simple example of code here, i tried various loop approaches, but at the end i still have a corrupted file written to disk. the size is the same (50kb file on the server, 50kb file downloaded and written on disk).
Thank you all.
headrequest = "HEAD " + "/folder/file.asd" + " HTTP/1.1\r\nHost: " + "url.com" + "\r\n\r\n";
getrequest = "GET " + "/folder/file.asd" + " HTTP/1.1\r\nHost: " + "url.com" + "\r\n\r\n";
send(socket, headrequest, sizeof(headrequest), 0);
recv(socket, reply_buf_headrequest, sizeof(reply_buf_headrequest), 0);
//two functions to get the header end and "Content-Lenght" data from header
send(socket, getrequest, sizeof(getrequest), 0);
while(1)
{
recv(socket, recvbuff, sizeof(recvbuff), 0);
if (recv(socket, recvbuff, sizeof(recvbuff), 0) == 0)
break;
}
out.write(recvbuff, content_lenght); // also tried --> out.write(recvbuff + header_end, content_lenght) //same errors.
out.close();
I screw up with the buffer/position to start reading/writing or something like that. I thought using recvbuff + header_end would work, since it would start reading from the end of the header to get the file. This is confusing.
I hope one kind soul could help me figure out how to handle this situation and write correctly the file bytes. :)
Edit:
i dint thought that i was overwriting data like that. damn.
content_length comes from the previous HEAD request, a function reads the recv'ed data and finds the "Content-Length" value, which is the size in bytes of /folder/file.asd.
i couldnt manage to get it in the Get request, so i did it like this.. the filesize it gets is correct.
so,
while(1)
{
if (recv(socket, recvbuff, sizeof(recvbuff), 0) == 0)
break;
}
out.write(recvbuff, content_lenght);
out.close();
out.write should after the loop or inside the while(1) loop?
Thanks for the fast reply. :)
I omitted the error checking part to keep the example code short, sorry.
the head and get request are chars, i tried with strings too and ended up not using sizeof() for that. i cant access the real code until tomorrow, so im trying to fix it at home using a similar snippet..there are some typos probably..
Edit 2:
as test with a small exe that just spawns a messagebox im using a buffer bigger than the file and this:
ofstream out("test.exe", ios::binary);
and using this loop now:
int res; // return code to monitor transfer
do {
res = recv(socket, recvbuff, sizeof(recvbuff), 0); // look at return code
if (res > 0) // if bytes received
out.write(recvbuff, res ); // write them
} while (res>0); // loop as long as we receive something
if (res==SOCKET_ERROR)
cerr << "Error: " << WSAGetLastError() << endl;
still having "program too big to fit in memory" error upon execution..
That's normal ! Your code doesn't really take care of the content you receive !
See my comments:
while(1) // Your original (indented) code commented:
{
recv(socket, recvbuff, sizeof(recvbuff), 0); // You read data in buffer
if (recv(socket, recvbuff, sizeof(recvbuff), 0) == 0) // you read again, overwriting data you've received !!
break;
}
out.write(recvbuff, content_lenght); // You only write the last thing you've received.
// Where does the lengthe come from ? Maybe you have buffer overflow as well.
Rewrite your loop as follows:
int res; // return code to monitor transfer
do {
res = recv(socket, recvbuff, sizeof(recvbuff), 0); // look at return code
if (res > 0) // if bytes received
out.write(recvbuff, res ); // write them
} while (res>0); // loop as long as we receive something
if (res==SOCKET_ERROR)
cerr << "Error: " << WSAGetLastError() << endl;
The advantage is that you don't have to care for overall size, as you write each small chunk that you receive.
Edit:
Following our exchange of comment, here some additional information. As someone pointed out, HTTP protocol is somewhat more complex to manage. See here, in chapter 6 for additional details about the format of a response, and the header you have to skip.
Here some updated proof of concept to skip the header:
ofstream out;
out.open(filename, ios::binary);
bool header_skipped=false; // was header skiped (do it only once !!)
int res; // return code to monitor transfer
do {
res = recv(mysocket, recvbuff, sizeof(recvbuff), 0); // look at return code
if (res > 0) // if bytes received
{
size_t data_offset = 0; // normally take data from begin of butter
if (!header_skipped) { // if header was not skipped, look for its end
char *eoh = "\r\n\r\n";
auto it = search (recvbuff, recvbuff + res, eoh, eoh + 4);
if (it != recvbuff + res) { // if header end found:
data_offset = it - recvbuff + 4; // skip it
header_skipped = true; // and then do not care any longer
} // because data can also containt \r\n\r\n
}
out.write(recvbuff + data_offset, res - data_offset); // write, ignoring before the offset
}
} while (res > 0); // loop as long as we receive something
if (res == SOCKET_ERROR) cerr << "Error: " << WSAGetLastError() << endl;
out.close();
Attention ! As said, it's a proof of concept. It will probably work. However, be aware that you cannot be sure how the data will be regrouped at receiver side. It is perfectly well possibly that the end of header is split between two successive reads (e.g. \r as last byte of one recv() and \n\r\n as first bytes of next recv()). In such a case this simple code won't find it. So it's not yet production quality code. Up to you to improve further

read handle problem

I am working on network programming using epoll and I have this code...
int read = read(socket, buf, bufsize);
I have a huge buffer size and I assumed it will receive everything clients sent.
However, I started facing problems like packet segmentation.
One example is that if a client sent 500 bytes but it somehow got into two 250 bytes packets then there is no way to handle this situation.
I looked up online and found this code
int handle_read(client *cli, struct epoll_event *ev) {
size_t len = 4096;
char *p;
ssize_t received;
cli->state = 1;
if (cli->buffer != NULL) {
//free(cli->buffer);
//printf("Buff not null %s\n", cli->buffer);
}
//allocate space for data
cli->buffer = (char*)malloc( (size_t)(sizeof(char) * 4096) );
p = cli->buffer;
do { //read until loop conditions
received = recv(ev->data.fd, p, len, 0);
if (received < 0 && errno != EAGAIN && errno != EWOULDBLOCK) {
//if error, remove from epoll and close socket
printf("Handle error!!!\nClient disconnected!\n");
epoll_ctl(epollfd, EPOLL_CTL_DEL, ev->data.fd, ev);
close(ev->data.fd);
}
p = &cli->buffer[received];
} while (received >= len && errno != EAGAIN && errno != EWOULDBLOCK);
return received;
}
Do you guys think it handles all the exceptions might happen while receiving? Also could you please provide me tutorials or examples that handles socket exceptions? Sample codes online don't cover details.. Thanks in advance
recv can return any of three things, and your code needs to handle each one correctly:
1) Positive number. This means it read some bytes.
2) Negative number. This means an "error" occurred.
3) Zero. This means the other end of the connection performed a successful shutdown() (or close()) on the socket. (In general, a return of 0 from read() or recv() means EOF.)
The "error" case further breaks down into "EAGAIN or EWOULDBLOCK" and "everything else". The first two just means it is a non-blocking socket and there was no data to give you at this time. You probably want to go back and call poll() (or select() or epoll()) again to avoid busy waiting...
"Everything else" means a real error. You need to handle those too; see the POSIX spec for recv() for a complete list.
Given all this, I would say your sample code is bad for several reasons. It does not handle 0 (closed connection) properly. It does not handle any errors. It does a busy-loop when the recv() returns EAGAIN/EWOULDBLOCK.
Oh, and it uses sizeof(char), which is a sure sign it was written by somebody who is not familiar with the C or C++ programming languages.
You can't know "How many datas client sent" in normaly. you should use scalable data format(that have data length in the header) or separator for data tokens. For example, you may add \xff between data and next data. Or, you should use fixed data format.