gpt4 book ai didi

c - select()-带有非阻塞TCP套接字的writefds和exceptfds的实际使用?

转载 作者:可可西里 更新时间:2023-11-01 02:40:36 29 4
gpt4 key购买 nike

根据Linux man pagesselect支持三种唤醒事件:

将监视

  • readfds,以查看是否有字符可用于读取
  • writefds将被监视以查看是否有空间可用于写入
  • exceptfds将被监视是否存在异常

  • 在在线和网络书籍中寻找带有TCP套接字的实际使用示例时,即使代码稍后尝试写入套接字,我也大多只看到 readfds被使用。

    但是套接字可能还没有准备好编写,因为我们可能只在 readfs集中收到了,而在 writefds集中没有收到。为了避免阻塞写操作,我通常将套接字的fd设置为非阻塞模式。然后,如果 send失败,我可以将数据排队到某个内部缓冲区中,然后将其发送出去(这意味着-下次当带 select()readfs唤醒时)。但这似乎很危险-如果下一次 readfs唤醒要晚得多,并且要写入的数据只是放在我们的缓冲区中,从理论上讲永远等待怎么办?

    Apple文档还建议使用 writefds: Using Sockets and Socket Streams,请参阅“使用纯POSIX代码处理事件”一节,并引述:

    Call select in a loop, passing two separate copies of that file descriptor set (created by calling FD_COPY) for the read and write descriptor sets.



    因此,问题是:
  • 苹果是否建议仅使用writefds就是因为它是“正确的官方方式”,或者还有其他方法可以不用writefds来处理套接字写操作?苹果的建议对我来说似乎很可疑。如果我们从一开始就将套接字放入writefds中,然后一段时间不对其进行写入,那么select()不会仅因为套接字可写而立即唤醒(那是因为我们尚未对其进行写入)吗?
  • 关于exceptfds-我尚未看到将其与TCP套接字一起使用的任何示例。我已经读过它用于带外数据。这是否意味着如果我仅处理主流的Internet通信(例如HTTP,音频/视频流,游戏服务器等),就可以忽略TCP套接字的exceptfds
  • 最佳答案

    Is Apple recommending to use writefds just because it's "the right official way" or maybe there are other approaches how to deal with socket writes without writefds?



    另一种方法(您在所看过的教程中看到的)是假定写缓冲区始终足够大,可以立即保存要发送给它的任何数据,并在需要时盲目调用send()。到。

    它简化了代码,但这不是一个很好的方法-也许对于玩具/示例程序来说已经足够了,但是我不想在生产质量的代码中做出这样的假设,因为这意味着如果/当程序一次生成足够的数据以填充套接字的输出缓冲区时。根据您(错误)处理send()的方式,您的程序将进入自旋循环(调用send()并一遍又一遍地获取EWOULDBLOCK,直到最终有足够的空间放置所有数据) ,或者出错(如果您将EWOULDBLOCK/short-send()视为致命错误条件),或者丢弃某些传出数据字节(如果您只是完全忽略send()的返回值)。这些都不是处理全输出缓冲区情况的一种优雅方式。

    If we put the socket into writefds from the very start and then don't write to it for some time, won't select() wake up immediately just because the socket is writable (and that's because we haven't written to it yet)?



    是的,绝对是-这就是为什么如果您当前有一些要写入套接字的数据,则仅将套接字放入writefds集中的原因。如果您当前没有要写入套接字的数据,则可以将该套接字保留在writefds之外,这样select()不会立即返回。

    About exceptfds -I haven't yet seen any examples using it with TCP sockets. I have read that it is used for out-of-band data.



    通常,exceptfds很少使用(TCP的带外数据功能AFAIK也不使用)。我唯一看到的其他一次是在Windows下进行异步/非阻塞TCP连接时-异步/非阻塞TCP连接尝试失败时Windows使用exceptionfds唤醒select()。

    Then if send fails, I could just queue the data into some internal buffer and send it out later (which means - next time when select() with readfs wakes up). But this seems dangerous - what if next readfs wakeup comes much later and the data to be written just sits in our buffer waiting, theoretically, forever?



    由于TCP会自动降低发送方的发送速度,使其以接收方以大约接收方的速率传输,因此接收程序肯定有可能仅停止调用recv(),最终将发送方的传输速率降低为零。或者,即使发送方和接收方像预期的那样调用recv(),发送方和接收方之间的网络也可能开始丢弃如此多的数据包,以致传输速率实际上变为零。在这两种情况下,这都意味着您排队的数据很可能会长时间放置在传出数据缓冲区中-在后一种情况下可能不会永远存在,因为完全阻塞的TCP连接最终将出错。在前一种情况下,您需要对接收方进行调试,而不是对发送方进行调试。

    真正的问题出在当发送方生成数据的速度快于接收方接收数据的速度(或者换句话说,其速度比网络传输数据的速度快)时,在这种情况下,如果您正在排队“多余”数据放入发送方的FIFO中,该FIFO可以无限制地增长,直到最终由于内存耗尽而导致发送进程崩溃-绝对不是理想的行为。

    有几种方法可以解决这个问题。一种方法是简单地监视当前保存在FIFO中的字节数,当达到某个阈值(例如1兆字节或某兆;构成“合理”阈值的阈值取决于您的应用程序在做什么)时,服务器可以决定客户端根本不能表现良好,并以自卫方式关闭发送套接字(当然可以释放关联的FIFO队列)。在很多情况下,这种方法都可以很好地发挥作用,尽管如果您的服务器即时生成/排队的数据量超过该数量,它可能会遭受误报,并最终导致不恰当地断开实际上运行良好的客户端。

    另一种方法(如果可能的话,我更喜欢这样)是设计服务器,以便仅在当前没有针对该套接字的输出数据排队时才为该套接字生成更多的输出数据。即,当套接字选择为可写状态时,将尽可能多的现有数据从FIFO队列中排入套接字。当FIFO队列为空并且您有要从中生成传出字节的数据并且套接字已准备好写入时,这是唯一一次生成更多输出数据字节并将其放入FIFO队列的时间。永远重复一次,无论客户端有多慢,FIFO队列的大小都不会大于您在“生成更多数据字节”步骤的一次迭代中生成的数据量。

    关于c - select()-带有非阻塞TCP套接字的writefds和exceptfds的实际使用?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41985774/

    29 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com