C - 如何同时使用aio_read()和aio_write()

Sla*_*lav 5 c sockets networking multithreading aio

我实现了游戏服务器,我需要读写.所以我接受传入连接并使用aio_read()从它开始读取,但是当我需要发送一些东西时,我停止使用aio_cancel()读取然后使用aio_write().在写回调内,我恢复阅读.所以,我一直在读,但是当我需要发送一些东西时 - 我暂停阅读.

它适用于〜的20%的时间-在其他情况下调用调用aio_cancel()失败,"正在进行操作" -我无法取消它(即使在永久周期).所以,我添加的写操作永远不会发生.

如何很好地使用这些功能?我错过了什么?

编辑:在Linux 2.6.35下使用.Ubuntu 10 - 32位.

示例代码:

void handle_read(union sigval sigev_value) { /* handle data or disconnection */ }
void handle_write(union sigval sigev_value) { /* free writing buffer memory */ }
void start()
{
    const int acceptorSocket = socket(AF_INET, SOCK_STREAM, 0);
    struct sockaddr_in addr;
memset(&addr, 0, sizeof(struct sockaddr_in));
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = INADDR_ANY;
addr.sin_port = htons(port);
    bind(acceptorSocket, (struct sockaddr*)&addr, sizeof(struct sockaddr_in));

    listen(acceptorSocket, SOMAXCONN);

    struct sockaddr_in address;
socklen_t addressLen = sizeof(struct sockaddr_in);

    for(;;)
    {
         const int incomingSocket = accept(acceptorSocket, (struct sockaddr*)&address, &addressLen);
         if(incomingSocket == -1)
         { /* handle error ... */}
         else
         {
              //say socket to append outcoming messages at writing:
              const int currentFlags = fcntl(incomingSocket, F_GETFL, 0);
              if(currentFlags < 0) { /* handle error ... */ }
              if(fcntl(incomingSocket, F_SETFL, currentFlags | O_APPEND) == -1) { /* handle another error ... */ }

              //start reading:
              struct aiocb* readingAiocb = new struct aiocb;
              memset(readingAiocb, 0, sizeof(struct aiocb));
              readingAiocb->aio_nbytes = MY_SOME_BUFFER_SIZE;
              readingAiocb->aio_fildes = socketDesc;
              readingAiocb->aio_buf = mySomeReadBuffer;
              readingAiocb->aio_sigevent.sigev_notify = SIGEV_THREAD;
              readingAiocb->aio_sigevent.sigev_value.sival_ptr = (void*)mySomeData;
              readingAiocb->aio_sigevent.sigev_notify_function = handle_read;
              if(aio_read(readingAiocb) != 0) { /* handle error ... */ }
          }
    }
}

//called at any time from server side:
send(void* data, const size_t dataLength)
{
    //... some thread-safety precautions not needed here ...

    const int cancellingResult = aio_cancel(socketDesc, readingAiocb);
    if(cancellingResult != AIO_CANCELED)
    {
        //this one happens ~80% of the time - embracing previous call to permanent while cycle does not help:
        if(cancellingResult == AIO_NOTCANCELED)
        {
            puts(strerror(aio_return(readingAiocb))); // "Operation now in progress"
            /* don't know what to do... */
        }
    }
    //otherwise it's okay to send:
    else
    {
        aio_write(...);
    }
}
Run Code Online (Sandbox Code Playgroud)

cni*_*tar 6

首先,考虑转储aio.还有很多其他方法可以做异步I/O而不是脑死亡(是的,aio是breaindead).很多替代品; 如果你在Linux上,你可以使用libaio(io_submit和朋友).aio(7)提到了这一点.

回到你的问题.
我很久没有用了aio,但这就是我记得的.aio_read并且aio_write都将requests(aiocb)放在某个队列上.即使请求将在一段时间后完成,它们也会立即返回.完全有可能排除多个请求而不关心先前发生的事情.因此,简而言之:停止取消读取请求并继续添加它们.

/* populate read_aiocb */
rc = aio_read(&read_aiocb);

/* time passes ... */
/* populate write_aiocb */
rc = aio_write(&write_aiocb)
Run Code Online (Sandbox Code Playgroud)

稍后您可以等待使用aio_suspend,轮询使用aio_error,等待信号等.

我看到你epoll在评论中提到了.你一定要去libaio.


caf*_*caf 5

如果希望为读取和写入使用单独的AIO队列,以便稍后发出的写入可以在较早发出的读取之前执行,则可以dup()用来创建套接字的副本,并使用一个来发出读取,而另一个来发出读取写道。

但是,我仅次于建议,以完全避免AIO,而只需在epoll()非阻塞套接字中使用-Drived事件循环。该技术已显示可扩展到大量客户端-如果您正在获得高CPU使用率,请对其进行概要分析并找出发生的原因,因为可能不是事件循环才是罪魁祸首。