sevencat 发表于 2015-10-2 18:26:20

在win平台如何将select操作变成异步基于iocp的一种办法

这个巧妙的做法是取得socket底层的afd的句柄,然后将select的调用变成NtDeviceIoControlFile调用,就可以跟iocp进行关联起来了。
有人可能说这个没有用,这个的用处在于使用第三方库。
注:在xp,2003平台上无效,具体原因看代码。

int uv_poll_init_socket(uv_loop_t* loop, uv_poll_t* handle,
    uv_os_sock_t socket) {
WSAPROTOCOL_INFOW protocol_info;
int len;
SOCKET peer_socket, base_socket;
DWORD bytes;
DWORD yes = 1;

/* Set the socket to nonblocking mode */
if (ioctlsocket(socket, FIONBIO, &yes) == SOCKET_ERROR)
    return uv_translate_sys_error(WSAGetLastError());

/* Try to obtain a base handle for the socket. This increases this chances */
/* that we find an AFD handle and are able to use the fast poll mechanism. */
/* This will always fail on windows XP/2k3, since they don't support the */
/* SIO_BASE_HANDLE ioctl. */
#ifndef NDEBUG
base_socket = INVALID_SOCKET;
#endif

if (WSAIoctl(socket,
               SIO_BASE_HANDLE,
               NULL,
               0,
               &base_socket,
               sizeof base_socket,
               &bytes,
               NULL,
               NULL) == 0) {
    assert(base_socket != 0 && base_socket != INVALID_SOCKET);
    socket = base_socket;
}

uv__handle_init(loop, (uv_handle_t*) handle, UV_POLL);
handle->socket = socket;
handle->events = 0;

/* Obtain protocol information about the socket. */
len = sizeof protocol_info;
if (getsockopt(socket,
               SOL_SOCKET,
               SO_PROTOCOL_INFOW,
               (char*) &protocol_info,
               &len) != 0) {
    return uv_translate_sys_error(WSAGetLastError());
}

/* Get the peer socket that is needed to enable fast poll. If the returned */
/* value is NULL, the protocol is not implemented by MSAFD and we'll have */
/* to use slow mode. */
peer_socket = uv__fast_poll_get_peer_socket(loop, &protocol_info);

if (peer_socket != INVALID_SOCKET) {
    /* Initialize fast poll specific fields. */
    handle->peer_socket = peer_socket;
} else {
    /* Initialize slow poll specific fields. */
    handle->flags |= UV_HANDLE_POLL_SLOW;
}

/* Initialize 2 poll reqs. */
handle->submitted_events_1 = 0;
uv_req_init(loop, (uv_req_t*) &(handle->poll_req_1));
handle->poll_req_1.type = UV_POLL_REQ;
handle->poll_req_1.data = handle;

handle->submitted_events_2 = 0;
uv_req_init(loop, (uv_req_t*) &(handle->poll_req_2));
handle->poll_req_2.type = UV_POLL_REQ;
handle->poll_req_2.data = handle;

return 0;
}



static void uv__fast_poll_submit_poll_req(uv_loop_t* loop, uv_poll_t* handle) {
uv_req_t* req;
AFD_POLL_INFO* afd_poll_info;
DWORD result;

/* Find a yet unsubmitted req to submit. */
if (handle->submitted_events_1 == 0) {
    req = &handle->poll_req_1;
    afd_poll_info = &handle->afd_poll_info_1;
    handle->submitted_events_1 = handle->events;
    handle->mask_events_1 = 0;
    handle->mask_events_2 = handle->events;
} else if (handle->submitted_events_2 == 0) {
    req = &handle->poll_req_2;
    afd_poll_info = &handle->afd_poll_info_2;
    handle->submitted_events_2 = handle->events;
    handle->mask_events_1 = handle->events;
    handle->mask_events_2 = 0;
} else {
    assert(0);
    return;
}

/* Setting Exclusive to TRUE makes the other poll request return if there */
/* is any. */
afd_poll_info->Exclusive = TRUE;
afd_poll_info->NumberOfHandles = 1;
afd_poll_info->Timeout.QuadPart = INT64_MAX;
afd_poll_info->Handles.Handle = (HANDLE) handle->socket;
afd_poll_info->Handles.Status = 0;
afd_poll_info->Handles.Events = 0;

if (handle->events & UV_READABLE) {
    afd_poll_info->Handles.Events |= AFD_POLL_RECEIVE |
      AFD_POLL_DISCONNECT | AFD_POLL_ACCEPT | AFD_POLL_ABORT;
}
if (handle->events & UV_WRITABLE) {
    afd_poll_info->Handles.Events |= AFD_POLL_SEND | AFD_POLL_CONNECT_FAIL;
}

memset(&req->u.io.overlapped, 0, sizeof req->u.io.overlapped);

result = uv_msafd_poll((SOCKET) handle->peer_socket,
                         afd_poll_info,
                         afd_poll_info,
                         &req->u.io.overlapped);
if (result != 0 && WSAGetLastError() != WSA_IO_PENDING) {
    /* Queue this req, reporting an error. */
    SET_REQ_ERROR(req, WSAGetLastError());
    uv_insert_pending_req(loop, req);
}
}


int WSAAPI uv_msafd_poll(SOCKET socket, AFD_POLL_INFO* info_in,
    AFD_POLL_INFO* info_out, OVERLAPPED* overlapped) {
IO_STATUS_BLOCK iosb;
IO_STATUS_BLOCK* iosb_ptr;
HANDLE event = NULL;
void* apc_context;
NTSTATUS status;
DWORD error;

if (overlapped != NULL) {
    /* Overlapped operation. */
    iosb_ptr = (IO_STATUS_BLOCK*) &overlapped->Internal;
    event = overlapped->hEvent;

    /* Do not report iocp completion if hEvent is tagged. */
    if ((uintptr_t) event & 1) {
      event = (HANDLE)((uintptr_t) event & ~(uintptr_t) 1);
      apc_context = NULL;
    } else {
      apc_context = overlapped;
    }

} else {
    /* Blocking operation. */
    iosb_ptr = &iosb;
    event = CreateEvent(NULL, FALSE, FALSE, NULL);
    if (event == NULL) {
      return SOCKET_ERROR;
    }
    apc_context = NULL;
}

iosb_ptr->Status = STATUS_PENDING;
status = pNtDeviceIoControlFile((HANDLE) socket,
                                  event,
                                  NULL,
                                  apc_context,
                                  iosb_ptr,
                                  IOCTL_AFD_POLL,
                                  info_in,
                                  sizeof *info_in,
                                  info_out,
                                  sizeof *info_out);

if (overlapped == NULL) {
    /* If this is a blocking operation, wait for the event to become */
    /* signaled, and then grab the real status from the io status block. */
    if (status == STATUS_PENDING) {
      DWORD r = WaitForSingleObject(event, INFINITE);

      if (r == WAIT_FAILED) {
      DWORD saved_error = GetLastError();
      CloseHandle(event);
      WSASetLastError(saved_error);
      return SOCKET_ERROR;
      }

      status = iosb.Status;
    }

    CloseHandle(event);
}

switch (status) {
    case STATUS_SUCCESS:
      error = ERROR_SUCCESS;
      break;

    case STATUS_PENDING:
      error = WSA_IO_PENDING;
      break;

    default:
      error = uv_ntstatus_to_winsock_error(status);
      break;
}

WSASetLastError(error);

if (error == ERROR_SUCCESS) {
    return 0;
} else {
    return SOCKET_ERROR;
}
}


winston 发表于 2015-10-2 21:51:44

没想过这个方案,确实有一套!
页: [1]
查看完整版本: 在win平台如何将select操作变成异步基于iocp的一种办法