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").
Related
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;
}
I have a relatively simple web server I have written in C++. It works fine for serving text/html pages, but the way it is written it seems unable to send binary data and I really need to be able to send images.
I have been searching and searching but can't find an answer specific to this question which is written in real C++ (fstream as opposed to using file pointers etc.) and whilst this kind of thing is necessarily low level and may well require handling bytes in a C style array I would like the the code to be as C++ as possible.
I have tried a few methods, this is what I currently have:
int sendFile(const Server* serv, const ssocks::Response& response, int fd)
{
// some other stuff to do with headers etc. ........ then:
// open file
std::ifstream fileHandle;
fileHandle.open(serv->mBase + WWW_D + resource.c_str(), std::ios::binary);
if(!fileHandle.is_open())
{
// error handling code
return -1;
}
// send file
ssize_t buffer_size = 2048;
char buffer[buffer_size];
while(!fileHandle.eof())
{
fileHandle.read(buffer, buffer_size);
status = serv->mSock.doSend(buffer, fd);
if (status == -1)
{
std::cerr << "Error: socket error, sending file\n";
return -1;
}
}
return 0
}
And then elsewhere:
int TcpSocket::doSend(const char* message, int fd) const
{
if (fd == 0)
{
fd = mFiledes;
}
ssize_t bytesSent = send(fd, message, strlen(message), 0);
if (bytesSent < 1)
{
return -1;
}
return 0;
}
As I say, the problem is that when the client requests an image it won't work. I get in std::cerr "Error: socket error sending file"
EDIT : I got it working using the advice in the answer I accepted. For completeness and to help those finding this post I am also posting the final working code.
For sending I decided to use a std::vector rather than a char array. Primarily because I feel it is a more C++ approach and it makes it clear that the data is not a string. This is probably not necessary but a matter of taste. I then counted the bytes read for the stream and passed that over to the send function like this:
// send file
std::vector<char> buffer(SEND_BUFFER);
while(!fileHandle.eof())
{
fileHandle.read(&buffer[0], SEND_BUFFER);
status = serv->mSock.doSend(&buffer[0], fd, fileHandle.gcount());
if (status == -1)
{
std::cerr << "Error: socket error, sending file\n";
return -1;
}
}
Then the actual send function was adapted like this:
int TcpSocket::doSend(const char* message, int fd, size_t size) const
{
if (fd == 0)
{
fd = mFiledes;
}
ssize_t bytesSent = send(fd, message, size, 0);
if (bytesSent < 1)
{
return -1;
}
return 0;
}
The first thing you should change is the while (!fileHandle.eof()) loop, because that will not work as you expect it to, in fact it will iterate once too many because the eof flag isn't set until after you try to read from beyond the end of the file. Instead do e.g. while (fileHandle.read(...)).
The second thing you should do is to check how many bytes was actually read from the file, and only send that amount of bytes.
Lastly, you read binary data, not text, so you can't use strlen on the data you read from the file.
A little explanations of the binary file problem: As you should hopefully know, C-style strings (the ones you use strlen to get the length of) are terminated by a zero character '\0' (in short, a zero byte). Random binary data can contain lots of zero bytes anywhere inside it, and it's a valid byte and doesn't have any special meaning.
When you use strlen to get the length of binary data there are two possible problems:
There's a zero byte in the middle of the data. This will cause strlen to terminate early and return the wrong length.
There's no zero byte in the data. That will cause strlen to go beyond the end of the buffer to look for the zero byte, leading to undefined behavior.
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
I'm currently working on a multiplayer game using sockets and I encountered some problems at the log-in.
Here's the server function - thread that deals with incoming messages from a user:
void Server::ClientThread(SOCKET Connection)
{
char *buffer = new char[256];
while (true)
{
ZeroMemory(buffer,256);
recv(Connection, buffer, 256, 0);
cout << buffer << endl;
if (strcmp(buffer, "StartLogIn"))
{
char* UserName = new char[256];
ZeroMemory(UserName, 256);
recv(Connection, UserName, 256, 0);
char* Password = new char[256];
ZeroMemory(Password, 256);
recv(Connection, Password, 256, 0);
cout << UserName << "-" << Password << " + "<< endl;
if (memcmp(UserName, "taigi100", sizeof(UserName)))
{
cout << "SMB Logged in";
}
else
cout << "Wrong UserName";
}
int error = send(Connection, "0", 1, 0);
// error = WSAGetLastError();
if (error == SOCKET_ERROR)
{
cout << "SMB D/Ced";
ExitThread(0);
}
}
}
And here is the function that sends the data from the client to the server:
if (LogInButton->isPressed())
{
send(Srv->getsConnect(), "StartLogIn", 256, 0);
const wchar_t* Usern = UserName->getText();
const wchar_t* Passn = Password->getText();
stringc aux = "";
aux += Usern;
char* User = (char*)aux.c_str();
stringc aux2 = "";
aux2 += Passn;
char* Pass = (char*)aux2.c_str();
if (strlen(User) > 0 && strlen(Pass) > 0)
{
send(Srv->getsConnect(), User, 256, 0);
send(Srv->getsConnect(), Pass, 256, 0);
}
}
I'm going to try to explain this as easy as possible. The first recv function from the while(true) in the Server-side function receives at first "StartLogIn" but does not enter the if only until the next loop of the while. Because it loops again it changes to "taigi100" ( a username I use ) and then it enters the if even tho it shouldn't.
A way to fix this would be to make a send-recv system in order to not send anything else until it got some feedback.
I want to know if there are any other fast ways of solving this problem and why such weird behaviour happens.
Well it's full of bugs.
Your overuse of new[]. Ok not a bug but you are not deleting any of these, and you could use either local stack buffer space or vector< char >
You need to always check the result of any call to recv as you are not guaranteed to receive the number of bytes you are expecting. The number you specify is the size of the buffer, not the number of bytes you are expecting to get.
strcmp returns 0 if the strings match, non-zero if they do not (actually 1 or -1 depending whether they compare less or greater). But it appears you are using non-zero to mean equal.
Not sure what stringc is. Some kind of conversion from wide string to string? In any case, I think send is const-correct so there is no need to cast the constness away.
3rd parameter of send is the number of bytes you are sending, not the capacity of your buffer. The user name and password are probably not 256 bytes. You need to send them as a "packet" though so the receiver knows what they are getting and will know when they have received a full packet. e.g. send a string like "User=vandamon\0". (And you need to check its return value too)
Because send() and recv() calls may not match up, two very good habits to get into are (1) preceed all variable length data by a fixed size length, and (2) only send the bare minimum needed.
So your initial send() call would be written as follows:
char const * const StartLogin = "StartLogIn";
short const StartLoginLength = static_cast<short>(strlen(StartLogin));
send(Srv->getsConnect(), reinterpret_cast<char *>(&StartLoginLength), sizeof(short), 0);
send(Srv->getsConnect(), StartLogin, StartLoginLength, 0);
The corresponding receive code would then have to read two bytes and guarantee that it got them by checking the return value from recv() and retrying if not enough was received. Then it would loop a second time reading exactly that many bytes into a buffer.
int guaranteedRecv(SOCKET s, char *buffer, int expected)
{
int totalReceived = 0;
int received;
while (totalReceived < expected)
{
received = recv(s, &buffer[totalReceived], expected - totalReceived, 0);
if (received <= 0)
{
// Handle errors
return -1;
}
totalReceived += received;
}
return totalReceived;
}
Note that this assumes a blocking socket. Non-blocking will return zero if no data is available and errno / WSAGetLastError() will say *WOULDBLOCK. If you want to go this route you'll have to handle this case specifically and find some way to block till data is available. Either that or busy-wait waiting for data, by repeatedly calling recv(). UGH.
Anyway, you call this first with the address of a short reinterpret_cast<char *> and expected == sizeof(short). Then you new[] enough space, and call a second time to get the payload. Beware of the lack of trailing NUL characters, unless you explicitly send them, which my code doesn't.
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.