0

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:

  1. merge content of buffer to stringxxx += buf;
  2. bzero temp buf
  3. 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)
abrahab
  • 2,430
  • 9
  • 39
  • 64

4 Answers4

1

Instead of bzero, you can just use

memset(buf, 0, 1025);
1

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.

xpapad
  • 4,376
  • 1
  • 24
  • 25
  • maybe its the better solution. thanks 1) check after itteration that readed less than 1024 bytes(buffer size), and if so, check for SPECIAL_SYMBOL up to position that ::recv return, but its will not fix the problem with the loop. seems its not infinite loop, because with messages near 2 kb all ok, but 50 kb message and php part that connected to c++ server get time out. – abrahab Apr 11 '12 at 17:24
  • i rewrited my "receive" function as you suggested. many thanks, now its can work without cleaning the buffer on each itteration, but there is still problem with infinite loo[. when I send (1023*8) or less bytes to the socket - all ok, but when sended (1023*9) or more - i get infinite loop on the thread with this socket. maybe its FreeBSD default UNIX socket limit or C++ default socket settings? – abrahab Apr 11 '12 at 19:40
1

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.

Jerry Coffin
  • 476,176
  • 80
  • 629
  • 1,111
  • thanks, the std::fill_n is what I am seaching for my c++ program. about why I clean the buffer every itteration of ::recv, please, check "edit" notes at first message. – abrahab Apr 11 '12 at 17:19
1

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.
modelnine
  • 1,499
  • 8
  • 11
  • php script do his job exactly. there is "if keeper" inside the loop for situation that you described, "if (status_or_nbytes_from_recv <= 0) done = 1;" and it must exists from the loop. i seaching for solution, maybe its something related to SO_SNDBUF and SO_RCVBUF options of socket create function. – abrahab Apr 11 '12 at 20:42
  • i made some additational tests and I am 100% sure that its "freeze" on the last 9th circle of the loop on ::recv command. – abrahab Apr 11 '12 at 20:46
  • SO_SNDBUF and SO_RCVBUF have _nothing_ to do with this. If your C++ process enters an infinite loop trying to receive data, this simply means that the sending side has not sent the amount of data you expect to receive and closed the connection, or that you're missing a break condition in your recv()-loop. – modelnine Apr 11 '12 at 21:25
  • See the attached example on properly using ::recv(), does implementing something similar in your program solve up your infinite loop problem? – modelnine Apr 11 '12 at 21:35
  • Oh, just getting to me, are you using actual UNIX domain sockets, a socketpair(), or are you actually using a pipe() and not a socket? In the last case, it possibly has something to do with buffering (but not with SO_SND/RCVBUF) and would require flushing in the sender, in the former cases, all data sent is available on the receiving side, and there is no need for flushing. – modelnine Apr 11 '12 at 21:52
  • thanks, but something similar was already implemented... std::cout << "L1" << std::endl; status_or_nbytes = ::recv ( current_socket, buf, 1024, 0 ); std::cout << "L2" << std::endl; its never print L2 after 8th itteration with buf[1024] therefore, problem not in the loop. maybe something with socket create and setsockopt. I will attach the example of my socket & setsockopt to the first message. + i create socket with socket() – abrahab Apr 11 '12 at 21:53
  • 1
    Have you made sure that the sender has actually sent 9*1024 bytes? from what I gather, you weren't initially checking return values for the recv() either, so possibly, the send() in the PHP script only queued a part of the buffer you're trying to send in one go (which would make the SO_SNDBUF of the unix-domain-socket be 8192 bytes), and you also have to implement a loop on that side to send all data. – modelnine Apr 11 '12 at 21:56
  • at php side i send all the data at once request (not circle). the c++ side 100% receive first 8*1024 bytes, on every itteration i also std::out << status_or_nbytes; from ::recv and its print 8 times. and freeze on 9th itteration, before any std::cout commands after ::recv :( – abrahab Apr 11 '12 at 22:05
  • 1
    Again, have you made sure that the PHP side actually sends out the 8*1024+x bytes? ::send() _may_ return before having sent out all data via the socket; just as you implement a loop to consecutively receive data, you (might) have to implement a loop to send the buffer on the other side. – modelnine Apr 11 '12 at 22:08
  • got it, thanks, but still does not know how to fix. looks like php side does not send the whole message. and c++ side ::recv waiting to continue. loop at php side does not work. maybe need to open new topic about how to fix php side? – abrahab Apr 11 '12 at 22:46
  • Please make a separate topic for that, yes. And good to know that sockets still work the way I expected them to. ;-) – modelnine Apr 12 '12 at 07:15
  • modelnine, thanks you for your help. at the morning with fresh head I found the bug: socket_write() at the php side was on the non-blocking mode, and, as your wrote, sending the last part of the message failed to the open socket. i fixed my code and now its working fine under blocking mode, but there is new question about why I am trying(tried) to implement non-blocking mode for socket_write() http://stackoverflow.com/questions/10121711/php-socket-write-over-blocking-and-non-blocking-sockets – abrahab Apr 12 '12 at 10:28