Работа WinSock2 и IOCP под WINE
От: acDev Россия  
Дата: 12.03.11 10:01
Оценка: 6 (2)
Асинхр. сервак с использованием IOCP написал 1,5 месяца назад. За это время так и не удалось заставить работать его под вайном.
Два дня назад в VMWare установил Ubuntu 10.4 и wine 1.3.13. И два вечера убил на выяснение причин "глючной" работы (нынешние админчеги не в состоянии что то тестить, лучше уж самому).
Вот и решил отписать тут, может кому и пригодится сей опыт.

Во-первых, под вайном некорректно отрабатывает функция GetAcceptExSockaddrs. А если быть точнее, то её работа некорректна из-за того, что в функции WS2_async_accept (это аналог AcceptEx) с порядком следования local_addr и remote_addr напутали.
Из-за этого в конфиг сервера пришлось добавить настройку, по которой я "правильно" использую полученный из функции GetAcceptExSockaddrs результат (админ выставляет настройку эту только при использовании под WINE).

Вот реализация ф-ии GetAcceptExSockAddrs в wine:
static void WINAPI WS2_GetAcceptExSockaddrs(PVOID buffer, DWORD data_size, DWORD local_size, DWORD remote_size, 
                                     struct WS_sockaddr **local_addr, LPINT local_addr_len, 
                                     struct WS_sockaddr **remote_addr, LPINT remote_addr_len) 
{ 
    char *cbuf = buffer; 
    TRACE("(%p, %d, %d, %d, %p, %p, %p, %p)\n", buffer, data_size, local_size, remote_size, local_addr, 
                                                local_addr_len, remote_addr, remote_addr_len ); 
    cbuf += data_size; 

    *local_addr_len = *(int *) cbuf; 
    *local_addr = (struct WS_sockaddr *)(cbuf + sizeof(int)); 

    cbuf += local_size; 

    *remote_addr_len = *(int *) cbuf; 
    *remote_addr = (struct WS_sockaddr *)(cbuf + sizeof(int)); 
}


Вот реализация callback ф-ии от AcceptEx в wine:
static NTSTATUS WS2_async_accept( void *arg, IO_STATUS_BLOCK *iosb, NTSTATUS status, void **apc ) 
{ 
    struct ws2_accept_async *wsa = arg; 
    int len; 
    char *addr; 
    
    // ..... тут много кода

    /* WS2 Spec says size param is extra 16 bytes long...what do we put in it? */ 
    addr = ((char *)wsa->buf) + wsa->data_len; 
    len = wsa->local_len - sizeof(int); 
    WS_getpeername(HANDLE2SOCKET(wsa->accept_socket), 
                   (struct WS_sockaddr *)(addr + sizeof(int)), &len); 
    *(int *)addr = len; 

    addr += wsa->local_len; 
    len = wsa->remote_len - sizeof(int); 
    WS_getsockname(HANDLE2SOCKET(wsa->accept_socket), 
                   (struct WS_sockaddr *)(addr + sizeof(int)), &len); 
    *(int *)addr = len; 

    // ..... тут много кода
}


Во-вторых, было замечано что под wine функция GetQueuedCompletionStatus нормально отрабатывает только для AcceptEx, но вот после вызовов WSARecv и WSASend эта функция "не срабатывает" (даже если очередь не была заюзана в IOCP).
Что бы заставить нормально работать тамошний "IOCP" с WSARecv и WSASend, нужно перед обработкой результата от AcceptEx (после вызова GetQueuedCompletionStatus) повторно "привязать" клиентский сокет к IOCP (вызовом функции CreateIoCompletionPort).
После этой операции сервер не "виснет" на фукции GetQueuedCompletionStatus.
Этот вызов тоже по настройке сделал.
wine linux iocp wsarecv wsasend acceptex getqueuedcompletionstatus
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.