2016-12-06 110 views
1

我正在尝试使用sendmmsg。我的程序工作正常,但sendmmsg始终每次发送1个数据包(返回1)。我已经通过gdb进行了检查,vlen被设置为正确的值(128),正如我所说的,它确实发送了数据包。我需要做什么才能在插座上启用sendmmsg?这是一个数据报(UDP)套接字,如果这有所作为。此外,这在Ubuntu 16和RHEL 7.2上都会发生。sendmmsg总是返回1

更新:另外,它的问题的情况下,我在非阻塞模式下运行,所以我并不感到惊讶,它返回小于128,只是它总是返回1

更新:新增MCVE:

#include <sys/socket.h> 
#include <netinet/ip.h> 
#include <arpa/inet.h> 
#include <unistd.h> 
#include <cstring> 
#include <string> 
#include <vector> 
#include <stdexcept> 
#include <iostream> 

const int SND_BUFFER_SIZE = 212992; 
const int PORT = 3333; 
const int NUM_MESSAGES = 128; 
const int BYTES_PER_MESSAGE = 1024; 

int numSendCalls = 0; 
int numPacketsSent = 0; 

void SetSndBufferSize(int socketFd) { 
    int sndBufferSize = SND_BUFFER_SIZE; 
    socklen_t sndBufferSizeLen = sizeof(int); 

    // Set the value 
    if(setsockopt(socketFd, SOL_SOCKET, SO_SNDBUF, (char*) &sndBufferSize, sndBufferSizeLen) != 0) { 
     throw std::runtime_error("Could not set the send buffer size (errno=" + std::string(strerror(errno)) + ")"); 
    } 

    // Confirm the value was set as desired 
    if(getsockopt(socketFd, SOL_SOCKET, SO_SNDBUF, (char*) &sndBufferSize, &sndBufferSizeLen) != 0) { 
     throw std::runtime_error("Could not fetch the send buffer size after setting it (errno=" + std::string(strerror(errno)) + ")"); 
    } 

    if(sndBufferSize != (SND_BUFFER_SIZE * 2)) { 
     throw std::runtime_error("The underlying OS could not set the send buffer size to what we wanted (" + std::to_string(SND_BUFFER_SIZE) + ") and instead it was (" + std::to_string(sndBufferSize/2) + ")"); 
    } 

} 

struct sockaddr_in MakeAddress(std::string destinationAddress, uint16_t destinationPort) { 
    struct sockaddr_in result; 
    result.sin_family = AF_INET; 
    result.sin_port = htons(destinationPort); 
    if(inet_aton(destinationAddress.c_str(), &result.sin_addr) == 0) { 
     throw std::runtime_error("Invalid IPv4 address " + destinationAddress); 
    } 
    return result; 
} 

void Connect(int socketFd) { 
    auto addr = MakeAddress("127.0.0.1", 3333); 
    if(connect(socketFd, (struct sockaddr*)&addr, sizeof(addr)) != 0) { 
     throw std::runtime_error("Failed to connect to address (" + std::string(strerror(errno)) + ")"); 
    } 
} 

std::vector<std::string> MakeDataBuffers() { 
    std::vector<std::string> result; 
    for(int i = 0; i < NUM_MESSAGES; i++) { 
     result.emplace_back(BYTES_PER_MESSAGE, 'W'); 
    } 
    return result; 
} 

std::vector<struct iovec> MakeIovecs(std::vector<std::string> & dataBuffers) { 
    std::vector<struct iovec> result; 
    for(int i = 0; i < NUM_MESSAGES; i++) { 
     result.emplace_back(); 
     result[i].iov_base = (void*) dataBuffers[i].data(); 
     result[i].iov_len = BYTES_PER_MESSAGE; 
    } 
    return result; 
} 

std::vector<struct mmsghdr> MakeHeaders(std::vector<struct iovec> & iovecs) { 
    std::vector<struct mmsghdr> result; 
    for(int i = 0; i < NUM_MESSAGES; i++) { 
     result.emplace_back(); 
     result[i].msg_hdr.msg_name = NULL; 
     result[i].msg_hdr.msg_namelen = 0; 
     result[i].msg_hdr.msg_iov = (struct iovec*) &iovecs[i]; 
     result[i].msg_hdr.msg_iovlen = 1; 
     result[i].msg_hdr.msg_control = NULL; 
     result[i].msg_hdr.msg_controllen = 0; 
     result[i].msg_hdr.msg_flags = 0; 
     result[i].msg_len = 0; 
    } 
    return result; 
} 

void Send(int socketFd, std::vector<struct mmsghdr> & msgvec) { 
    struct mmsghdr * msgvecdata = (struct mmsghdr*) msgvec.data(); 
    unsigned int size = msgvec.size(); 
    int numUpdated = sendmmsg(socketFd, msgvecdata, size, 0); 
    numSendCalls++; 
    if(numUpdated < 0) { 
     throw std::runtime_error("Error sending packets (" + std::string(strerror(errno)) + ")"); 
    } 
    numPacketsSent += numUpdated; 
} 

void Reset(std::vector<struct mmsghdr> & msgvec) { 
    for(auto it = msgvec.begin(); it < msgvec.end(); it++) { 
     it->msg_len = 0; 
    } 
} 

int main() { 
    auto socketFd = socket(AF_INET, SOCK_DGRAM | SOCK_NONBLOCK, 0); 
    SetSndBufferSize(socketFd); 
    Connect(socketFd); 
    auto dataBuffers = MakeDataBuffers(); 
    auto iovecs = MakeIovecs(dataBuffers); 
    auto headers = MakeHeaders(iovecs); 
    for(int i = 0; i < 1000; i++) { 
     Send(socketFd, headers); 
     Reset(headers); 
    } 
    std::cout << "Sent: " << std::to_string(numPacketsSent) << " packets across " << std::to_string(numSendCalls) + " calls" << std::endl; 
    close(socketFd); 
    return 0; 
} 
+0

'sendmmsg()'返回已更新向量的元素数量,而不是发送的数据包数量。每个元素发送的字节数被写入向量本身。检查'sendmmsg()'[这里]的文档(http://man7.org/linux/man-pages/man2/sendmmsg.2.html)。总之,如果你只发送一条消息,那么你会得到1. – alvits

+0

的结果,我发送了128条消息。我检查并写入的字节数等于第一个数据包的全长,其余为0。 – Pace

+0

这是很难调试,你看不到的代码。你已经有足够长的时间来了解演习 - MCVE([MCVE])。 –

回答

0

好的,所以问题是没有人在端口上侦听,并且ICMP不可达响应正在触发sendmmsg提前返回。如果我用nc打开一个收件人,那么至少在Ubuntu上,一切都按预期工作。

我曾经想过这可能是这样的情况,因此在RHEL上压制ICMP,但是我的代码中只有RHEL上出现了一个错误。