函数setsockopt似乎不起作用

时间:2016-08-10 17:32:20

标签: c++ sockets visual-c++ winsock

我正在使用WinSock2,我希望立即收到所有包(它们是XML文件)。 现在我正在制作如下所示,因为setsockopt似乎忽略了我的“顺序”来设置接收缓冲区的大小,但它始终从执行中返回OK:

#define IP_BUF_SZ 2000000
//...
std::string sBuffer;
long chrs_read = 0;
int iBufSize = IP_BUF_SZ;
//...
if (setsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, (char*)&iBufSize, sizeof(int)) == SOCKET_ERROR)
    MessageBox("Unable to increase"); //never reachs here

do
{
    char* buf = (char*)MALLOCZ(IP_BUF_SZ + 1000);
    chrs_read = recv(sockfd, buf, IP_BUF_SZ, 0);

    if (chrs_read > 0)
        sBuffer += std::string(buf);

    FREE(buf);
    buf = NULL;
}
while (chrs_read > 0);

但我想做那样的事(一下子):

setsockopt(IP_STATUS[CHAN_EPIC].sockfd, SOL_SOCKET, SO_RCVBUF, (char*)&iBufSize, sizeof(int);
char* buf = (char*)MALLOCZ(IP_BUF_SZ + 1000);
chrs_read = recv(IP_STATUS[CHAN_EPIC].sockfd, buf, IP_BUF_SZ, 0); 
sBuffer += std::string(buf);

备注:我的文件大小小于IP_BUF_SZ。 提前谢谢。

2 个答案:

答案 0 :(得分:2)

setsockopt(SO_RCVBUF)并不保证您可以在一次recv()来电中收到完整的讯息。它甚至不保证您将获得您请求的缓冲区大小。您必须使用getsockopt(SO_RCVBUF)来检索实际的缓冲区大小。

SO_RCVBUF只是在发送方开始阻塞等待您读取字节之前,控制套接字在其内部缓冲区中可以容纳的字节数。但recv()并不保证它会返回您请求的所有字节(除非您使用MSG_WAITALL标志调用它,在此情况下我不建议这样做,因为您不知道消息长度预先)。如果你要求recv()读取X字节,并且当前只有Y字节可用,其中Y < X,recv()将返回Y字节,而不是等待返回X字节。唯一的保证是recv()将返回至少 1个字节且不再而不是X个字节。因此,无论您将内部缓冲区大小设置为什么,仍然需要读取循环。

SO_RCVBUF仅仅是用于优化网络I / O的暗示性选项。实际上并不依赖于代码的读取逻辑。

也就是说,您展示的代码存在其他问题。

您正在循环的每次迭代中分配和释放接收缓冲区。不要那样做。分配一次,然后循环读取,然后在完成时释放缓冲区。

除了recv()实际要求的内容之外,您无需过度分配接收缓冲区。读取时也不需要将缓冲器置零。这些步骤只是在开销上浪费了。

将接收缓冲区附加到recv()时,您也没有考虑std::string的返回值。您正在使用期望缓冲区为空终止的std::string构造函数。您依靠缓冲区归零来提供空终止符,但如果数据包含其自己的任何嵌入的nul字符(取决于XML的编码),那么将截断您附加到std::string的内容。 recv()返回它读取的字节数。您需要将这么多字节追加到std::string,而不是更少。

尝试更像这样的事情:

#define IP_BUF_SZ 2000000

//...

std::string sBuffer;
long chrs_read;

//...

int iBufSize = IP_BUF_SZ;
int iBufVarSize = sizeof(iBufSize);

if (setsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, (char*)&iBufSize, iBufVarSize) == SOCKET_ERROR)
    MessageBox("Unable to set buffer size");
else if (getsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, (char*)&iBufSize, &iBufVarSize) == SOCKET_ERROR)
{
    MessageBox("Unable to get buffer size");
    iBufSize = IP_BUF_SZ;
}

char* buf = (char*) malloc(iBufSize);
if (!buf)
    MessageBox("Unable to allocate buffer");
else
{
    do
    {
        chrs_read = recv(sockfd, buf, iBufSize, 0);
        if (chrs_read <= 0)
        {
            if (chrs_read == SOCKET_ERROR)
                MessageBox("Unable to read message");
            break;
        }
        sBuffer.append(buf, chrs_read);
    }
    while (true);

    free(buf);
    buf = NULL;
}

请注意,此逻辑正在读取,直到套接字在读取时断开连接或遇到错误。如果每个套接字连接只有1个消息,那就没问题。但是,如果您打算在单个连接上发送多个XML消息,那将不再起作用。您需要在消息之间放置分隔符,以便知道一条消息的结束位置和下一条消息的开始位置。这可以是指定消息长度的前导头,也可以是消息末尾的唯一终止符序列。

答案 1 :(得分:0)

您可以使缓冲区与系统允许的一样大,但您无法做任何事情将允许您从TCP一次接收所有数据。你必须循环,直到你收到你期望的所有数据。

你的问题基于错误的假设。