在调用 recv() 之前检查错误

Checking for errors before recv() called

本文关键字:检查 错误 调用 recv      更新时间:2023-10-16

In遇到了以下问题:

我创建了一个能够使用 select() 处理多个连接的服务器。但是 select 也会返回一个客户端(索引为 FD_SET),如果套接字刚刚收到"客户端断开连接"之类的错误。

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

示例代码:

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

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

那么在不调用 recv() 的情况下检查套接字是不可能的吗?

我不确定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似乎也支持它。

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

调用 recv 和类似函数不能直接与网络设备或类似设备一起使用。当您发送或接收数据时,您要做的就是询问操作系统的可用数据,或者将数据放入队列中以供发送。然后,当您的代码已经更进一步时,操作系统将完成其他工作。这就是为什么您在下次调用将"接触"操作系统网络层的套接字函数后收到错误的原因。

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

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

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

经过几分钟的工作和您的帮助,我只收到 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

我也尝试为 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));
...