Асинхр. сервак с использованием 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.
Этот вызов тоже по настройке сделал.