WSARecv是socket编程中用到的异步接收数据的函数。
在接收之前程序会调用socket()来创建一个socket,在wine中这是WS_socket(),最终会进入wineserver的函数create_socket(),这里会调用sock_reselect(),将socket的fd号写入一个全局的pollfd数据结构中。
然后看WSARecv(),这个函数会执行一遍recv,如果这个时候还没有数据到来,则向wineserver申请执行register_async,并设置回调函数为WS2_async_recv。
- 代码: 全选
SERVER_START_REQ( register_async )
{
req->type = ASYNC_TYPE_READ;
req->async.handle = wine_server_obj_handle( wsa->hSocket );
req->async.callback = wine_server_client_ptr( WS2_async_recv );
req->async.iosb = wine_server_client_ptr( iosb );
req->async.arg = wine_server_client_ptr( wsa );
req->async.event = wine_server_obj_handle( lpCompletionRoutine ? 0 : lpOverlapped->hEvent );
req->async.cvalue = cvalue;
err = wine_server_call( req );
}
SERVER_END_REQ;
wineserver在执行register_async时,会通过fd_ops寻找每个对象自己的queue_async函数指针,这里对应socket的是sock_queue_async(),然后调用到create_async(),将一个设置好的async数据结构挂入queue队列。然后wineserver返回,WSARecv也因此返回应用程序。
- 代码: 全选
struct async *create_async( struct thread *thread, struct async_queue *queue, const async_data_t *data )
{
......
list_add_tail( &queue->queue, &async->queue_entry );
......
}
然后看真正的接收数据工作在哪儿做。
看wineserver的main_loop函数:
- 代码: 全选
void main_loop(void)
{
int i, ret, timeout;
set_current_time();
server_start_time = current_time;
main_loop_epoll();
/* fall through to normal poll loop */
while (active_users)
{
timeout = get_next_timeout();
if (!active_users) break; /* last user removed by a timeout */
ret = poll( pollfd, nb_users, timeout );
set_current_time();
if (ret > 0)
{
for (i = 0; i < nb_users; i++)
{
if (pollfd[i].revents)
{
fd_poll_event( poll_users[i], pollfd[i].revents );
if (!--ret) break;
}
}
}
}
}
这是wineserver的主循环,只要有windows进程在运行,这个循环会一直轮询下去。在这个循环里会调用poll函数,参数就是创建socket是写入fd的全局pollfd结构,然后进入等待数据的到来。
当客户端程序调用send之类的函数时,poll会因为有I/O请求而返回,然后进入fd_poll_event函数,一直调用到async_wake_up(),然后会遍历刚刚提到的queue队列,找到对应的async数据结构,然后进入async_terminate(),在这里将刚刚设置的回调函数WS2_async_recv()加入apc队列,当wineserver返回时会进入这个apc函数WS2_async_recv()
- 代码: 全选
void async_terminate( struct async *async, unsigned int status )
{
......
memset( &data, 0, sizeof(data) );
data.type = APC_ASYNC_IO;
data.async_io.func = async->data.callback;
data.async_io.user = async->data.arg;
data.async_io.sb = async->data.iosb;
data.async_io.status = status;
thread_queue_apc( async->thread, &async->obj, &data );
......
}
在WS2_async_recv()里才真正调用recv函数来接收数据。
- 代码: 全选
static NTSTATUS WS2_async_recv( void* user, IO_STATUS_BLOCK* iosb, NTSTATUS status, void **apc)
{
ws2_async* wsa = user;
int result = 0, fd;
switch (status)
{
case STATUS_ALERTED:
if ((status = wine_server_handle_to_fd( wsa->hSocket, FILE_READ_DATA, &fd, NULL ) ))
break;
result = WS2_recv( fd, wsa );
......
break;
}
....
return status;
}
这样就实现了一个异步的过程。

