Question

I am using recvmsg and sendmsg to send data over an asynchronous STREAM socket . The amount of data to transfer is rather big, between 15 MB and 30 MB.

I cannot understand why the data arrives corrupted. The sending messages and the reciving function gets interrupted by the errno EINTR adn EAGAIN.

The functions I ma using are :

int receive_result_with_extra(int fd, struct syscall_result * result, int extra_size, char * buf) { 
    struct iovec io[2];
    struct msghdr msg; 
    int transfered=0, temp=0; 
    const int total =  SIZE_RESULT + extra_size; 

    CLEAN_MSG(&msg);
    memset(io, 0, sizeof(io)); 

    // result header 
    io[1].iov_len=SIZE_RESULT; 
    io[1].iov_base=result;
    // result buffer
    io[0].iov_len = extra_size; 
    io[0].iov_base = buf; 
    // iov struct 
    msg.msg_iov=io;
    msg.msg_iovlen=2;

    do { 
      temp = recvmsg(fd,&msg, 0);       
      if ( temp < 0 && (errno==EAGAIN || errno == EINTR || errno == EWOULDBLOCK)) 
        continue; 
      else if ( temp < 0 )
          die("Error receiveing data recvmsg (receive_result_with_extra)"); 
      transfered += temp; 
    } while(transfered < total); 

    if ( transfered < 0) 
        die("recvmsg (receive_result_with_extra)"); 

    assert(transfered == total);
    return transfered; 
}
int send_result_with_extra(int fd, struct syscall_result * result, int extra_size, char * buf) {

    struct iovec io[2];
    struct msghdr msg; 
    int transfered=0, temp=0; 
    const int total =  SIZE_RESULT + extra_size; 

    CLEAN_MSG(&msg);
    memset(io, 0, sizeof(io)); 

    // result header 
    io[1].iov_len=SIZE_RESULT; 
    io[1].iov_base=result;
    // result buffer
    io[0].iov_len = extra_size; 
    io[0].iov_base = buf; 
    // iov struct 
    msg.msg_iov=io;
    msg.msg_iovlen=2;

    do {
      temp=sendmsg(fd,&msg, 0); 
      if ( temp < 0 && (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK))
          continue; 
      else if ( temp < 0)
          die("Failed sending data ( send_result_with_extra)");  
      transfered += temp; 
    } while( transfered < total);

    if ( transfered < 0) 
        die("recvmsg (fstat handler)"); 

    assert(transfered == total);

    return transfered; 
}
Était-ce utile?

La solution

You don't update the iovecs in your send/recv loops, so you will repeatedly send the same prefix / recv into the beginning of the data buffer if the data is too large to transfer in a single network packet.

I would use a common function for both send and receive to update the iovec:

/**
 * Shrinks the iovec associated with a msg by a given number of bytes.
 *
 * @msg    The message whose iovec needs updating.
 * @bytes  Number of bytes to remove from the beginning of the iovec.
 *
 * @return Returns 0 iff the updated iovec is empty.
 */
int update_buffer(struct msghdr* msg, size_t bytes) {
  while (msg->msg_iovlen > 0) {
    if (bytes < msg->msg_iov[0].iov_len) {
      msg->msg_iov[0].iov_len -= bytes;
      msg->msg_iov[0].iov_base += bytes;
      return 1;
    }
    bytes -= msg->msg_iov[0].iov_len;
    ++msg->msg_iov;
    --msg->msg_iovlen;
  }
  return 0;
}

You can then use the return value of this update function in the condition of your send/recv loops. E.g.:

do {
  do {
    temp = sendmsg(fd, &msg, 0); 
  } while (temp < 0 && (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK));
  if (temp < 0)
    die("Failed sending data (send_result_with_extra)");
} while (update_buffer(&msg, temp));
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top