The output when I send hello.
I'm coding a C++ TCP server and I'm using a while loop to continuously get data but I think it's accepting the same thing and prints a bit of the thing that it's meant to output.
while (true)
{
char buffer[5];
if (recv(clisoc, buffer, sizeof(buffer), 0)) {
string abc = (string)buffer;
cout << abc.substr(0, sizeof(buffer));
}
}
Any help would be appreciated thanks!
You only tested the return value of recv to see if it didn't detect a normal close, so none of your subsequent code has any idea how much data you received. Likely, you are receiving data once but printing it twice. You cannot cast arbitrary data to a string, that is legal only for a pointer to an actual string.
Related
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.
//server side
void* s2(void *arg){
info *s = (info*)arg;
char buffer[MAXS];
int k;
sockaddr_in addr;
socklen_t aSize = sizeof(sockaddr_in);
int sfd = accept(s->fd,(sockaddr*)&addr,(socklen_t*)&aSize);
if(sfd<0){
s->current--;
pthread_exit(0);
}
while(1){
k = recv(sfd,buffer,MAXS,0);
cout<<buffer<<"\n";
//1. k ==0 socket has been closed by client
//2. k==-1 error in recv
//3. recv quit
if((k==-1)||(!strncmp(buffer,"quit",4))||(k==0))break;
sprintf(buffer,"%d\n",(int)strlen(buffer)); //Convert length to string using sprintf()
send(sfd,buffer,strlen(buffer),0); //send buffer to client
}
close(sfd);
if(s->limit==s->current)
FD_SET(s->fd,&sfds);
s->current--; //decreament the client number
pthread_exit(0);
}
//client side
1. send(sockfd,"sadhdag",8,0);
2. send(sockfd,"ss",3,0);
Server recv sadhdag in first call to recv .
In second call to recv server recv
ss
dag
server side:
function s2 is run by a thread and argument is passed with socket information connection is accepted there and send and recv is called onn newly accepted client.
Why this is happening ? or how to avoid this ?
You're ignoring the count returned by recv(). Contrary to suggestions in comments, it isn't necessary to memset() the buffer to zero before recv(), but it is necessary to only use that buffer afterwards up to that count. For example:
printf("%s", buffer);
is wrong, and
printf("%.*s", count, buffer);
is right.
NB
if((k==-1)||(!strncmp(buffer,"quit",4))||(k==0))break;
isn't correct either. It should be
if((k==-1)||(k==0)||(k >= 4 && !strncmp(buffer,"quit",4))) break;
It isn't valid to look into the buffer at all unless k is positive, and it isn't valid to compare four characters in it unless there are four characters in it.
You forgot to actually implement any kind of protocol or application-level logic of any kind. This sums it up:
k = recv(sfd,buffer,MAXS,0);
cout<<buffer<<"\n";
This code suggests you're terminating your messages with a newline:
sprintf(buffer,"%d\n",(int)strlen(buffer)); //Convert length to string using sprintf()
send(sfd,buffer,strlen(buffer),0); //send buffer to client
If so, where's the code to parse that on the other end? You throw the returned length away, so you wouldn't even know which characters to check for a newline.
TCP is a byte-stream protocol that does not preserve application-level message boundaries. If you want to use it to send strings or other application-level messages, you have to specify and implement an application-level protocol to do it.
You call recv to get the raw TCP data, then you output it as if it was a string. If you're going to define and implement a protocol to send and receive strings over TCP, you have to actually do it. You can't just imagine it happens by magic.
If you want to receive newline-terminated messages into C-style strings, you can certainly do it. But you have to actually write the code to do it.
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.
I am using read function to read data from a socket, but when the data is more than 4k, read function just read part of the data, for example, less than 4k. Here is the key code:
mSockFD = socket(AF_INET, SOCK_STREAM, 0);
if (connect(mSockFD, (const sockaddr*)(&mSockAdd), sizeof(mSockAdd)) < 0)
{
cerr << "Error connecting in Crawl" << endl;
perror("");
return false;
}
n = write(mSockFD, httpReq.c_str(), httpReq.length());
bzero(mBuffer, BUFSIZE);
n = read(mSockFD, mBuffer, BUFSIZE);
Note than BUFSIZE is much larger than 4k.
When data is just a few hundred bytes, read function works as expected.
This is by design and to be expected.
The short answer to your question is you should continue calling "read" until you get all the data you expect. That is:
int total_bytes = 0;
int expected = BUFSIZE;
int bytes_read;
char *buffer = malloc(BUFSIZE+1); // +1 for null at the end
while (total_bytes < expected)
{
int bytes_read = read(mSockFD, buffer+total_bytes, BUFSIZE-total_bytes);
if (bytes_read <= 0)
break;
total_bytes += bytes_read;
}
buffer[total_bytes] = 0; // null terminate - good for debugging as a string
From my experience, one of the biggest misconceptions (resulting in bugs) that you'll receive as much data as you ask for. I've seen shipping code in real products written with the expectation that sockets work this way (and no one certain as to why it doesn't work reliably).
When the other side sends N bytes, you might get lucky and receive it all at once. But you should plan for receiving N bytes spread out across multiple recv calls. With the exception of a real network error, you'll eventually get all N bytes. Segmentation, fragmentation, TCP window size, MTU, and the socket layer's data chunking scheme are the reasons for all of this. When partial data is received, the TCP layer doesn't know about how much more is yet to come. It just passes what it has up to the app. It's up to the app to decide if it got enough.
Likewise, "send" calls can get conglomerated into the same packet together.
There may be ioctls and such that will make a socket block until all the expected data is received. But I don't know of any off hand.
Also, don't use read and write for sockets. Use recv and send.
Read this book. It will change your life with regards to sockets and TCP:
SOCKET lhSocket;
int iResult;
lhSocket = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
char *sendbuf = "this is a test";
iResult = send(lhSocket, sendbuf, (int)strlen(sendbuf), 0 );
printf("Bytes Sent: %ld\n", iResult);
I have client and Server program using sockets in C++ now i send a buffer it is received by server now when server acknowledge me back saying i got your packet i should get that in string format not bytes received : something. how to achieve that ?
My iresult returns me an integer value, I am sending a message over socket to server , i dont want to print it as Bytes sent : 14.
I want to print the message sent as string to server.
I am dealing with Sockets.
How i can achieve this in C++
stringstream buf;
buf << 12345;
buf.str(); // string("12345")
buf.str().c_str(); // char* "12345"
sendbuf is the string which you are sending. Print sendbuf instead:
printf("Bytes Sent: %s\n", sendbuf);
Another opportunity is boost::lexical_cast<>
const int myIntValue = 12345;
const std::string myStringValue = boost::lexical_cast(myIntValue);
You're asking different things in the title and your post.
Converting int to string in C++ is done with
#include <sstream>
std::ostringstream oss;
oss << some_int;
// do whatever with oss.str()...
as Tomasz illustrated.
To receive data from a socket, you need to make a further call to either recv() or read(). Your "send" call does not itself wait for the reply. recv() or read() accept character-array buffers to read the response into, but you will need to loop reading however much the calls return until you have enough of a response to process, as TCP is what's called a "byte stream" protocol, which means you are not guaranteed to get a complete packet, line, message or anything other than a byte per call.
Given the level of understanding your question implies, I strongly suggest you have a look at the GNU libC examples of sockets programming - there are some server and client examples - easily found via Google.
if you use visual C++ 2008 or 2010 i think there is a function inbuilt to do your job.
something like itoa(int); will convert the given int and return a char *
pretty simple
its in stdlib.hlbtw
** make sure this is not the same in all compilers or distrubutions
http://www.cplusplus.com/reference/clibrary/cstdlib/itoa/
here is a link for reference
Take a look at itoa which converts an integer to a string.