简体   繁体   中英

Isn't recv() in C socket programming blocking?

In Receiver, I have

recvfd=accept(sockfd,&other_side,&len);
while(1)
{
   recv(recvfd,buf,MAX_BYTES-1,0);
   buf[MAX_BYTES]='\0';
   printf("\n Number %d contents :%s\n",counter,buf);
   counter++;
}

In Sender , I have

send(sockfd,mesg,(size_t)length,0);
send(sockfd,mesg,(size_t)length,0);
send(sockfd,mesg,(size_t)length,0);

MAX_BYTES is 1024 and length of mesg is 15. Currently, It calls recv only one time. I want recv function to be called three times for each corresponding send. How do I achieve it?

To send discrete messages over a byte stream protocol, you have to encode messages into some kind of framing language. The network can chop up the protocol into arbitrarily sized packets, and so the receives do not correlate with your messages in any way. The receiver has to implement a state machine which recognizes frames.

A simple framing protocol is to have some length field (say two octets: 16 bits, for a maximum frame length of 65535 bytes). The length field is followed by exactly that many bytes.

You must not even assume that the length field itself is received all at once. You might ask for two bytes, but recv could return just one. This won't happen for the very first message received from the socket, because network (or local IPC pipe, for that matter) segments are never just one byte long. But somewhere in the middle of the stream, it is possible that the fist byte of the 16 bit length field could land on the last position of one network frame.

An easy way to deal with this is to use a buffered I/O library instead of raw operating system file handles. In a POSIX environment, you can take an open socket handle, and use the fdopen function to associate it with a FILE * stream. Then you can use functions like getc and fread to simplify the input handling (somewhat).

If in-band framing is not acceptable, then you have to use a protocol which supports framing, namely datagram type sockets. The main disadvantage of this is that the principal datagram-based protocol used over IP is UDP, and UDP is unreliable. This brings in a lot of complexity in your application to deal with out of order and missing frames. The size of the frames is also restricted by the maximum IP datagram size which is about 64 kilobytes, including all the protocol headers.

Large UDP datagrams get fragmented, which, if there is unreliability in the network, adds up to greater unreliability: if any IP fragment is lost, the entire packet is lost. All of it must be retransmitted; there is no way to just get a repetition of the fragment that was lost. The TCP protocol performs "path MTU discovery" to adjust its segment size so that IP fragmentation is avoided, and TCP has selective retransmission to recover missing segments.

In short: yes , it is blocking. But not in the way you think.

recv() blocks until any data is readable. But you don't know the size in advance.

In your scenario, you could do the following:

  1. call select() and put the socket where you want to read from into the READ FD set
  2. when select() returns with a positive number, your socket has data ready to be read
  3. then, check if you could receive length bytes from the socket:
    recv(recvfd, buf, MAX_BYTES-1, MSG_PEEK) , see man recv(2) for the MSG_PEEK param or look at MSDN, they have it as well
  4. now you know how much data is available
  5. if there's less than length available, return and do nothing
  6. if there's at least length available, read length and return (if there's more than length available, we'll continue with step 2 since a new READ event will be signalled by select()

I bet you've created a TCP socket using SOCK_STREAM, which would cause the three messages to be read into your buffer during the first recv call. If you want to read the messages one-by-one, create a UPD socket using SOCK_DGRAM, or develop some type of message format which allows you to parse your messages when they arrive in a stream (assuming your messages will not always be fixed length).

First send the length to be received in a fixed format regarding the size of length in bytes you use to transmit this length , then make recv() loop until length bytes had been received.


Note the fact (as also already mentioned by other answers), that the size and number of chunks received do not necessarly need to be the same as sent. Only the sum of all bytes received shall be the same as the sum of all bytes sent.

Read the man pages for recv and send . Especially read the sections on what those functions RETURN .

recv will block until the entire buffer is filled, or the socket is closed.

If you want to read length bytes and return, then you must only pass to recv a buffer of size length .

You can use select to determine if

  1. there are any bytes waiting to be read,
  2. how many bytes are waiting to be read, then
  3. read only those bytes

This can avoid recv from blocking.

Edit:

After re-reading the docs, the following may be true: your three "messages" may be being read all-at-once since length + length + length < MAX_BYTES - 1 .

Another possibility, if recv is never returning, is that you may need to flush your socket from the sender-side. The data may be waiting in a buffer to actually be sent to the receiver.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM