Работа 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
Re: Работа WinSock2 и IOCP под WINE
От: Centaur Россия  
Дата: 13.03.11 02:55
Оценка: +2
Здравствуйте, acDev, Вы писали:

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


Сделай патч, отошли мейнтейнеру.
Re[2]: Работа WinSock2 и IOCP под WINE
От: acDev Россия  
Дата: 20.12.11 08:26
Оценка:
Здравствуйте, Centaur, Вы писали:

C>Сделай патч, отошли мейнтейнеру.


Надеялся что сами найдут косяк. Прошло уже 8 месяцев с момента реализации в WINE функции WS2_async_accept.
Недавно зарегился на winehq и добавил баг-реппорт.
Теперь ошибка в функции WS2_async_accept пофикшена.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.