我如何获得可在 Linux 上的异​​步套接字上读取的字节数?

Posted

技术标签:

【中文标题】我如何获得可在 Linux 上的异​​步套接字上读取的字节数?【英文标题】:how can i get the number of bytes available to read on async socket on linux? 【发布时间】:2013-07-31 16:08:00 【问题描述】:

我有一个在 linux 上用 c++ 编写的简单 tcp/ip 服务器。我正在使用异步套接字和 epoll。当我收到 EPOLLIN 事件时,是否可以找出有多少字节可供读取?

【问题讨论】:

为什么? recv() 会告诉你,也会给你数据。 嗯,有时知道缓冲区的大小会很方便,应该分配给读取。例如,在 kqueue 中,数据字段中返回的字节数。 恕我直言,您不应该为读取分配缓冲区。这只会产生垃圾、堆碎片等。您应该使用本地分配的字节数组。 但是,如果接收到的数据应该在其他线程中处理,我该怎么办?例如,我有一个带有一个线程的服务器,它接受网络连接、读取请求并将它们传输到另一个线程(甚至线程池),进行一些处理? 【参考方案1】:

来自man 7 tcp

int value;
error = ioctl(sock, FIONREAD, &value);

或者SIOCINQ,它是FIONREAD的同义词。

无论如何,我建议在循环中以非阻塞模式使用recv,直到它返回EWOULDBLOCK

更新:

从您下面的 cmets 来看,我认为这不是您的问题的合适解决方案。

假设您的标头是 8 个字节,而您只收到 4 个;那么您的poll/select 将返回EPOLLIN,您将检查FIONREAD,看到标头尚未完成并且需要更多字节。但是这些字节永远不会到达,所以你在每次调用poll/select 时都会得到EPOLLIN,并且你有一个无操作的忙循环。也就是说,poll/select级别触发的。并不是说边缘触发功能也可以解决您的问题。

最后,你最好做一些工作,为每个连接添加一个缓冲区,并将字节排队,直到你有足够的时间。它并不像看起来那么困难,而且效果要好得多。例如,类似的东西:

struct ConnectionData

    int sck;
    std::vector<uint8_t> buffer;
    size_t offset, pending;
;

void OnPollIn(ConnectionData *d)

    int res = recv(d->sck, d->buffer.data() + offset, d->pending);
    if (res < 0) 
        handle_error();
    d->offset += res;
    d->pending -= res;

    if (d->pending == 0)
        DoSomethingUseful(d);

而且无论何时你想获得多个字节:

void PrepareToRecv(ConnectionData *d, size_t size)

    d->buffer.resize(size);
    d->offset = 0;
    d->pending = size;

【讨论】:

感谢您的回答!是的,我可以使用 recv 或像这样读取,但是我有一个二进制协议,它将数据包大小存储在它的标头中,所以我认为代码可以变得更简单,如果我要检查标头是否已经可用于读取或不是。 @PavelDavydov:确实,在这里有一个缓冲区和累积可能(只是)有点复杂。但我认为你可能走得太远了……请看我的更新。 是的,无休止地获取 EPOLLIN 可能是个问题,谢谢。我的服务器的行为方式与您推荐的类似,我只是认为也许可以更轻松、更清洁。

以上是关于我如何获得可在 Linux 上的异​​步套接字上读取的字节数?的主要内容,如果未能解决你的问题,请参考以下文章

我如何获得在 unix 域套接字中排队的数据报数量

如何在所有时间步中获得网络中所有层中所有单元的网络激活?

如何从服务器调用客户端上的方法?

Linux 上的程序如何与 X11 Server 通信?

用于 Windows 和 Linux 的同一台机器上的套接字

任何c#应用程序都可以在linux上运行吗[重复]