2

遇到以下问题:

我制作了一个能够使用select () 处理多个连接的服务器。但是,如果套接字刚刚收到诸如“客户端断开连接”或其他错误之类的错误,则 select 也会返回一个客户端(FD_SET的索引)。

是否可以在不调用recv () 的情况下检查套接字。因为要接收,我需要从我的“ BufferPool ”中获取一个缓冲区

示例代码:

int ret = recv(client, buffer_pool->get(), BUFFER_SIZE, 0);
if(ret == -1) ... // something went wrong

那么我必须再次释放缓冲区,这几乎浪费了我池中的一个缓冲区。(很短的时间)

所以不调用recv () 就不能检查socket吗

4

5 回答 5

4

我不确定 Windows,但getsockopt()在符合 POSIX 的系统上使用就像一个魅力。尽管在使用它之前 - 请确保从池中获取缓冲区比进行额外的系统调用更昂贵。这是一个代码片段:

int my_get_socket_error(int fd)
{
        int err_code;
        socklen_t len = sizeof(err_code);

        if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &err_code, &len) != 0)
                err_code = errno;
        else
                errno = err_code;
        return err_code;
}

更新:

根据此文档,Windows 似乎也支持它。

于 2012-04-20T15:44:21.757 回答
2

不,没有办法避免recv()来电。如果select()报告套接字是可读的,那么您必须从套接字读取以确定它的新状态。如果客户端正常断开连接,recv()将返回 0,而不是 -1。如果您不想浪费池化缓冲区,则必须先读入临时本地缓冲区,然后如果recv()返回任何数据,则可以检索池化缓冲区并将读取的数据复制到其中。

于 2012-04-20T15:27:46.990 回答
1

调用 recv 和类似的功能不能直接与网络设备或类似的东西一起使用。当您发送或接收数据时,您所做的只是向操作系统询问可用数据,或者将数据放入队列中以进行发送。然后,当您的代码已经走得更远时,操作系统将完成另一项工作。这就是为什么您在下次调用将“联系”操作系统网络层的套接字函数后收到错误的原因。

在这一点上出现错误是正常的,你必须处理它们。

但是为了防止阻塞套接字和浪费缓冲区,请查看实现或准备好的库的在线技术,它们为您提供了使用套接字的异步方式,这样您就不需要在套接字触发接收回调函数之前定义任何东西,您必须这样做实际接收。

同样,一次性接收大量数据也不是什么好技术,因为您将面临通过 TCP 层合并或拆分数据的问题,因为它是基于流的层。建议在您的数据包中包含标头(几个字节)并接收它们,这样您就不需要为标头拉取,但只有在标头之后您才想根据标头中提供的长度读取消息的其余部分。这只是可能的例子。

于 2012-04-20T15:38:47.130 回答
0

经过几分钟的工作和您的帮助,我在收到全部金额之前只收到了 1 个字节:

SOCKET client = ...;

char temp = 0x00;
int len = recv(client, &temp, 1, 0);
if(len == 0)
{
    // .. client error handling
    return;
}

char* buffer = m_memory_pool->Get();
len = recv(client, buffer + 1, m_memory_pool->buffer_size() - 1, 0);
buffer[0] = temp;

// data handling
于 2012-04-21T11:44:23.983 回答
0

我还尝试为 recv() 设置超时,但似乎在 Windows 下它不起作用,这是我的代码:

...
long timeout_ms = 10;
struct timeval interval = {timeout_ms / 1000, (timeout_ms % 1000) * 1000};
if (interval.tv_sec < 0 || (interval.tv_sec == 0 && interval.tv_usec <= 0))
{
    interval.tv_sec = 0;
    interval.tv_usec = 10000;
}
setsockopt(s_sktIx, SOL_SOCKET, SO_RCVTIMEO, (char *)&interval, sizeof(struct timeval));
...
于 2019-03-27T19:12:12.623 回答