tb_void_t tb_aiop_exit(tb_aiop_ref_t aiop) { // check tb_aiop_impl_t* impl = (tb_aiop_impl_t*)aiop; tb_assert_and_check_return(impl); // exit reactor if (impl->rtor && impl->rtor->exit) impl->rtor->exit(impl->rtor); // exit spak if (impl->spak[0]) tb_socket_exit(impl->spak[0]); if (impl->spak[1]) tb_socket_exit(impl->spak[1]); impl->spak[0] = tb_null; impl->spak[1] = tb_null; // exit pool tb_spinlock_enter(&impl->lock); if (impl->pool) tb_fixed_pool_exit(impl->pool); impl->pool = tb_null; tb_spinlock_leave(&impl->lock); // exit lock tb_spinlock_exit(&impl->lock); // free impl tb_free(impl); }
tb_void_t tb_poller_exit(tb_poller_ref_t self) { // check tb_poller_poll_ref_t poller = (tb_poller_poll_ref_t)self; tb_assert_and_check_return(poller); // exit pair sockets if (poller->pair[0]) tb_socket_exit(poller->pair[0]); if (poller->pair[1]) tb_socket_exit(poller->pair[1]); poller->pair[0] = tb_null; poller->pair[1] = tb_null; // exit hash if (poller->hash) tb_free(poller->hash); poller->hash = tb_null; poller->hash_size = 0; // close pfds if (poller->pfds) tb_vector_exit(poller->pfds); poller->pfds = tb_null; // close cfds if (poller->cfds) tb_vector_exit(poller->cfds); poller->cfds = tb_null; // free it tb_free(poller); }
static tb_void_t tb_demo_http_session_exit(tb_demo_http_session_ref_t session) { // check tb_assert(session); // exit socket if (session->sock) tb_socket_exit(session->sock); session->sock = tb_null; // exit file if (session->file) tb_file_exit(session->file); session->file = tb_null; }
tb_void_t tb_dns_looker_exit(tb_dns_looker_ref_t self) { // the looker tb_dns_looker_t* looker = (tb_dns_looker_t*)self; if (looker) { // exit sock if (looker->sock) tb_socket_exit(looker->sock); looker->sock = tb_null; // exit it tb_free(looker); } }
tb_void_t tb_dns_looker_exit(tb_dns_looker_ref_t looker) { // the impl tb_dns_looker_impl_t* impl = (tb_dns_looker_impl_t*)looker; if (impl) { // exit sock if (impl->sock) tb_socket_exit(impl->sock); impl->sock = tb_null; // exit it tb_free(impl); } }
/* ////////////////////////////////////////////////////////////////////////////////////// * main */ tb_int_t tb_demo_coroutine_http_server_main(tb_int_t argc, tb_char_t** argv) { // done tb_socket_ref_t sock = tb_null; do { // init socket sock = tb_socket_init(TB_SOCKET_TYPE_TCP, TB_IPADDR_FAMILY_IPV4); tb_assert_and_check_break(sock); // bind socket tb_ipaddr_t addr; tb_ipaddr_set(&addr, tb_null, TB_DEMO_PORT, TB_IPADDR_FAMILY_IPV4); if (!tb_socket_bind(sock, &addr)) break; // listen socket if (!tb_socket_listen(sock, 1000)) break; // init the root directory if (argv[1]) tb_strlcpy(g_rootdir, argv[1], sizeof(g_rootdir)); else tb_directory_current(g_rootdir, sizeof(g_rootdir)); // only data? if (!tb_file_info(g_rootdir, tb_null)) g_onlydata = tb_true; // trace tb_trace_i("%s: %s", g_onlydata? "data" : "rootdir", g_rootdir); #if TB_DEMO_CPU > 1 // start workers for multi-threads tb_size_t count = TB_DEMO_CPU - 1; while (count--) tb_thread_init(tb_null, tb_demo_coroutine_worker, sock, 0); #endif // start worker tb_demo_coroutine_worker(sock); } while (0); // exit socket if (sock) tb_socket_exit(sock); sock = tb_null; // ok return 0; }
tb_bool_t tb_aico_open_sock_from_type(tb_aico_ref_t aico, tb_size_t type) { // check tb_aico_impl_t* impl = (tb_aico_impl_t*)aico; tb_aicp_impl_t* aicp_impl = (tb_aicp_impl_t*)impl->aicp; tb_assert_and_check_return_val(impl && aicp_impl && aicp_impl->ptor && aicp_impl->ptor->addo, tb_false); // done tb_bool_t ok = tb_false; tb_socket_ref_t sock = tb_null; do { // closed? tb_assert_and_check_break(tb_atomic_get(&impl->state) == TB_STATE_CLOSED); tb_assert_and_check_break(!impl->type && !impl->handle); // init sock sock = tb_socket_init(type); tb_assert_and_check_break(sock); // bind type and handle impl->type = TB_AICO_TYPE_SOCK; impl->handle = (tb_handle_t)sock; // addo aico ok = aicp_impl->ptor->addo(aicp_impl->ptor, impl); tb_assert_and_check_break(ok); // opened tb_atomic_set(&impl->state, TB_STATE_OPENED); } while (0); // failed? if (!ok) { // exit it if (sock) tb_socket_exit(sock); sock = tb_null; } // ok? return ok; }
/* ////////////////////////////////////////////////////////////////////////////////////// * implementation */ static tb_void_t tb_demo_context_exit(tb_aiop_ref_t aiop, tb_demo_context_t* context) { if (context) { // exit aioo if (context->aioo) tb_aiop_delo(aiop, context->aioo); context->aioo = tb_null; // exit sock if (context->sock) tb_socket_exit(context->sock); context->sock = tb_null; // exit file if (context->file) tb_file_exit(context->file); context->file = tb_null; // exit data if (context->data) tb_free(context->data); context->data = tb_null; // exit it tb_free(context); } }
/* ////////////////////////////////////////////////////////////////////////////////////// * main */ tb_int_t tb_demo_asio_aiopd_main(tb_int_t argc, tb_char_t** argv) { // check tb_assert_and_check_return_val(argv[1], 0); // done tb_socket_ref_t sock = tb_null; tb_aiop_ref_t aiop = tb_null; do { // init sock sock = tb_socket_init(TB_SOCKET_TYPE_TCP); tb_assert_and_check_break(sock); // init aiop aiop = tb_aiop_init(16); tb_assert_and_check_break(aiop); // bind if (!tb_socket_bind(sock, tb_null, 9090)) break; // listen sock if (!tb_socket_listen(sock, 20)) break; // addo sock if (!tb_aiop_addo(aiop, sock, TB_AIOE_CODE_ACPT, tb_null)) break; // accept tb_aioe_t list[16]; while (1) { // wait tb_long_t objn = tb_aiop_wait(aiop, list, 16, -1); tb_assert_and_check_break(objn >= 0); // walk list tb_size_t i = 0; for (i = 0; i < objn; i++) { // the aioo tb_aioo_ref_t aioo = list[i].aioo; // check tb_assert_and_check_break(aioo && tb_aioo_sock(aioo)); // acpt? if (list[i].code & TB_AIOE_CODE_ACPT) { // done acpt tb_bool_t ok = tb_false; tb_demo_context_t* context = tb_null; do { // make context context = tb_malloc0_type(tb_demo_context_t); tb_assert_and_check_break(context); // init sock context->sock = tb_socket_accept(tb_aioo_sock(aioo), tb_null, tb_null); tb_assert_and_check_break(context->sock); // init file context->file = tb_file_init(argv[1], TB_FILE_MODE_RO); tb_assert_and_check_break(context->file); // init data context->data = tb_malloc_bytes(TB_DEMO_FILE_READ_MAXN); tb_assert_and_check_break(context->data); // addo sock context->aioo = tb_aiop_addo(aiop, context->sock, TB_AIOE_CODE_SEND, context); tb_assert_and_check_break(context->aioo); // trace tb_trace_i("acpt[%p]: ok", context->sock); // init left context->left = tb_file_size(context->file); // done read tb_long_t real = tb_file_read(context->file, context->data, tb_min((tb_size_t)context->left, TB_DEMO_FILE_READ_MAXN)); tb_assert_and_check_break(real > 0); // save size context->left -= real; // trace // tb_trace_i("read[%p]: real: %ld", context->file, real); // done send context->send = real; real = tb_socket_send(context->sock, context->data + context->real, context->send - context->real); if (real >= 0) { // save real context->real += real; // trace // tb_trace_i("send[%p]: real: %ld", context->sock, real); } else { // trace tb_trace_i("send[%p]: closed", context->sock); break; } // ok ok = tb_true; } while (0); // failed or closed? if (!ok) { // exit context tb_demo_context_exit(aiop, context); break; } } // writ? else if (list[i].code & TB_AIOE_CODE_SEND) { // the context tb_demo_context_t* context = (tb_demo_context_t*)list[i].priv; tb_assert_and_check_break(context); // continue to send it if not finished if (context->real < context->send) { // done send tb_long_t real = tb_socket_send(tb_aioo_sock(aioo), context->data + context->real, context->send - context->real); if (real > 0) { // save real context->real += real; // trace // tb_trace_i("send[%p]: real: %ld", tb_aioo_sock(aioo), real); } else { // trace tb_trace_i("send[%p]: closed", tb_aioo_sock(aioo)); // exit context tb_demo_context_exit(aiop, context); break; } } // finished? read file else if (context->left) { // init context->real = 0; context->send = 0; // done read tb_size_t tryn = 1; tb_long_t real = 0; while (!(real = tb_file_read(context->file, context->data, tb_min((tb_size_t)context->left, TB_DEMO_FILE_READ_MAXN))) && tryn--); if (real > 0) { // save left context->left -= real; // trace // tb_trace_i("read[%p]: real: %ld", context->file, real); // done send context->send = real; real = tb_socket_send(tb_aioo_sock(aioo), context->data, context->send); if (real >= 0) { // save real context->real += real; // trace // tb_trace_i("send[%p]: real: %ld", tb_aioo_sock(aioo), real); } else { // trace tb_trace_i("send[%p]: closed", tb_aioo_sock(aioo)); // exit context tb_demo_context_exit(aiop, context); break; } } else { // trace tb_trace_i("read[%p]: closed", tb_aioo_sock(aioo)); // exit context tb_demo_context_exit(aiop, context); break; } } else { // trace tb_trace_i("read[%p]: closed", tb_aioo_sock(aioo)); // exit context tb_demo_context_exit(aiop, context); break; } } // error? else { tb_trace_i("aioe[%p]: unknown code: %lu", tb_aioo_sock(aioo), list[i].code); break; } } } } while (0); // trace tb_trace_i("end"); // exit socket if (sock) tb_socket_exit(sock); // exit aiop if (aiop) tb_aiop_exit(aiop); // end return 0; }
/* ////////////////////////////////////////////////////////////////////////////////////// * implementation */ static tb_void_t tb_demo_coroutine_pull(tb_cpointer_t priv) { // done tb_socket_ref_t sock = tb_null; do { // init socket sock = tb_socket_init(TB_SOCKET_TYPE_TCP, TB_IPADDR_FAMILY_IPV4); tb_assert_and_check_break(sock); // init address tb_ipaddr_t addr; tb_ipaddr_set(&addr, "127.0.0.1", TB_DEMO_PORT, TB_IPADDR_FAMILY_IPV4); // trace tb_trace_d("[%p]: connecting %{ipaddr} ..", sock, &addr); // connect socket tb_long_t ok; while (!(ok = tb_socket_connect(sock, &addr))) { // wait it if (tb_socket_wait(sock, TB_SOCKET_EVENT_CONN, TB_DEMO_TIMEOUT) <= 0) break; } // connect ok? tb_check_break(ok > 0); // trace tb_trace_d("[%p]: recving ..", sock); // recv data tb_byte_t data[8192]; tb_hize_t recv = 0; tb_long_t wait = 0; tb_hong_t time = tb_mclock(); while (1) { // read it tb_long_t real = tb_socket_recv(sock, data, sizeof(data)); // trace tb_trace_d("[%p]: recv: %ld, total: %lu", sock, real, recv + (real > 0? real : 0)); // has data? if (real > 0) { recv += real; wait = 0; } // no data? wait it else if (!real && !wait) { // wait it wait = tb_socket_wait(sock, TB_SOCKET_EVENT_RECV, TB_DEMO_TIMEOUT); tb_assert_and_check_break(wait >= 0); } // failed or end? else break; } // trace tb_trace_i("[%p]: recv %llu bytes %lld ms", sock, recv, tb_mclock() - time); } while (0); // exit socket if (sock) tb_socket_exit(sock); sock = tb_null; }
static tb_bool_t tb_aiop_push_acpt(tb_aiop_ptor_impl_t* impl, tb_aice_ref_t aice) { // check tb_assert_and_check_return_val(impl && aice, tb_false); tb_assert_and_check_return_val(aice->code == TB_AICE_CODE_ACPT, tb_false); // the aico tb_aiop_aico_t* aico = (tb_aiop_aico_t*)aice->aico; tb_assert_and_check_return_val(aico && aico->base.handle, tb_false); // the priority tb_size_t priority = tb_aice_impl_priority(aice); tb_assert_and_check_return_val(priority < tb_arrayn(impl->spak) && impl->spak[priority], tb_false); // init the acpt aice tb_aice_t acpt_aice = *aice; acpt_aice.state = TB_STATE_OK; // done tb_size_t list_indx = 0; tb_size_t list_size = 0; tb_socket_ref_t list_sock[2048]; tb_ipaddr_t list_addr[2048]; tb_size_t list_maxn = tb_arrayn(list_sock); tb_socket_ref_t acpt = (tb_socket_ref_t)aico->base.handle; tb_queue_ref_t spak = impl->spak[priority]; tb_socket_ref_t sock = tb_null; do { // accept it for (list_size = 0; list_size < list_maxn && (list_sock[list_size] = tb_socket_accept(acpt, list_addr + list_size)); list_size++) ; // enter tb_spinlock_enter(&impl->lock); // push some acpt aice for (list_indx = 0; list_indx < list_size && (sock = list_sock[list_indx]); list_indx++) { // init aico acpt_aice.u.acpt.aico = tb_aico_init(aico->base.aicp); // trace tb_trace_d("push: acpt[%p]: sock: %p, aico: %p", aico->base.handle, sock, acpt_aice.u.acpt.aico); // open aico and push the acpt aice if not full? if ( acpt_aice.u.acpt.aico && tb_aico_open_sock(acpt_aice.u.acpt.aico, sock) && !tb_queue_full(spak)) { // save addr tb_ipaddr_copy(&acpt_aice.u.acpt.addr, list_addr + list_indx); // push to the spak queue tb_queue_put(spak, &acpt_aice); } else { // close the left sock tb_size_t i; for (i = list_indx; i < list_size; i++) { // close it if (list_sock[i]) tb_socket_exit(list_sock[i]); list_sock[i] = tb_null; } // exit aico if (acpt_aice.u.acpt.aico) tb_aico_exit(acpt_aice.u.acpt.aico); acpt_aice.u.acpt.aico = tb_null; // trace tb_trace_e("push: acpt failed!"); break; } } // leave tb_spinlock_leave(&impl->lock); } while (list_indx == list_maxn); // ok return tb_true; }
static tb_long_t tb_aiop_spak_clos(tb_aiop_ptor_impl_t* impl, tb_aice_ref_t aice) { // check tb_assert_and_check_return_val(impl && impl->aiop && impl->ltimer && impl->timer && aice, -1); tb_assert_and_check_return_val(aice->code == TB_AICE_CODE_CLOS, -1); // the aico tb_aiop_aico_t* aico = (tb_aiop_aico_t*)aice->aico; tb_assert_and_check_return_val(aico, -1); // trace tb_trace_d("clos: aico: %p, code: %u: %s", aico, aice->code, tb_state_cstr(tb_atomic_get(&aico->base.state))); // exit the timer task if (aico->task) { if (aico->bltimer) tb_ltimer_task_exit(impl->ltimer, aico->task); else tb_timer_task_exit(impl->timer, aico->task); aico->bltimer = 0; } aico->task = tb_null; // exit the sock if (aico->base.type == TB_AICO_TYPE_SOCK) { // remove aioo if (aico->aioo) tb_aiop_delo(impl->aiop, aico->aioo); aico->aioo = tb_null; // close the socket handle if (aico->base.handle) tb_socket_exit((tb_socket_ref_t)aico->base.handle); aico->base.handle = tb_null; } // exit file else if (aico->base.type == TB_AICO_TYPE_FILE) { // exit the file handle if (aico->base.handle) tb_file_exit((tb_file_ref_t)aico->base.handle); aico->base.handle = tb_null; } // clear waiting state aico->waiting = 0; aico->wait_ok = 0; aico->aice.code = TB_AICE_CODE_NONE; // clear type aico->base.type = TB_AICO_TYPE_NONE; // clear timeout tb_size_t i = 0; tb_size_t n = tb_arrayn(aico->base.timeout); for (i = 0; i < n; i++) aico->base.timeout[i] = -1; // closed tb_atomic_set(&aico->base.state, TB_STATE_CLOSED); // ok aice->state = TB_STATE_OK; return 1; }
/* ////////////////////////////////////////////////////////////////////////////////////// * implementation */ static tb_long_t tb_dns_looker_reqt(tb_dns_looker_t* looker) { // check tb_check_return_val(!(looker->step & TB_DNS_LOOKER_STEP_REQT), 1); // format it first if the request is null if (!tb_static_buffer_size(&looker->rpkt)) { // check size tb_assert_and_check_return_val(!looker->size, -1); // format query tb_static_stream_t stream; tb_byte_t rpkt[TB_DNS_RPKT_MAXN]; tb_size_t size = 0; tb_byte_t* p = tb_null; tb_static_stream_init(&stream, rpkt, TB_DNS_RPKT_MAXN); // identification number tb_static_stream_writ_u16_be(&stream, TB_DNS_HEADER_MAGIC); /* 0x2104: 0 0000 001 0000 0000 * * tb_uint16_t qr :1; // query/response flag * tb_uint16_t opcode :4; // purpose of message * tb_uint16_t aa :1; // authoritive answer * tb_uint16_t tc :1; // truncated message * tb_uint16_t rd :1; // recursion desired * tb_uint16_t ra :1; // recursion available * tb_uint16_t z :1; // its z! reserved * tb_uint16_t ad :1; // authenticated data * tb_uint16_t cd :1; // checking disabled * tb_uint16_t rcode :4; // response code * * this is a query * this is a standard query * not authoritive answer * not truncated * recursion desired * * recursion not available! hey we dont have it (lol) * */ #if 1 tb_static_stream_writ_u16_be(&stream, 0x0100); #else tb_static_stream_writ_u1(&stream, 0); // this is a query tb_static_stream_writ_ubits32(&stream, 0, 4); // this is a standard query tb_static_stream_writ_u1(&stream, 0); // not authoritive answer tb_static_stream_writ_u1(&stream, 0); // not truncated tb_static_stream_writ_u1(&stream, 1); // recursion desired tb_static_stream_writ_u1(&stream, 0); // recursion not available! hey we dont have it (lol) tb_static_stream_writ_u1(&stream, 0); tb_static_stream_writ_u1(&stream, 0); tb_static_stream_writ_u1(&stream, 0); tb_static_stream_writ_ubits32(&stream, 0, 4); #endif /* we have only one question * * tb_uint16_t question; // number of question entries * tb_uint16_t answer; // number of answer entries * tb_uint16_t authority; // number of authority entries * tb_uint16_t resource; // number of resource entries * */ tb_static_stream_writ_u16_be(&stream, 1); tb_static_stream_writ_u16_be(&stream, 0); tb_static_stream_writ_u16_be(&stream, 0); tb_static_stream_writ_u16_be(&stream, 0); // set questions, see as tb_dns_question_t // name + question1 + question2 + ... tb_static_stream_writ_u8(&stream, '.'); p = (tb_byte_t*)tb_static_stream_writ_cstr(&stream, tb_static_string_cstr(&looker->name)); // only one question now. tb_static_stream_writ_u16_be(&stream, 1); // we are requesting the ipv4 address tb_static_stream_writ_u16_be(&stream, 1); // it's internet (lol) // encode dns name if (!p || !tb_dns_encode_name((tb_char_t*)p - 1)) return -1; // size size = tb_static_stream_offset(&stream); tb_assert_and_check_return_val(size, -1); // copy tb_static_buffer_memncpy(&looker->rpkt, rpkt, size); } // data && size tb_byte_t const* data = tb_static_buffer_data(&looker->rpkt); tb_size_t size = tb_static_buffer_size(&looker->rpkt); // check tb_assert_and_check_return_val(data && size && looker->size < size, -1); // try get addr from the dns list tb_ipaddr_ref_t addr = tb_null; if (looker->maxn && looker->itor && looker->itor <= looker->maxn) addr = &looker->list[looker->itor - 1]; // check tb_assert_and_check_return_val(addr && !tb_ipaddr_is_empty(addr), -1); // family have been changed? reinit socket if (tb_ipaddr_family(addr) != looker->family) { // exit the previous socket if (looker->sock) tb_socket_exit(looker->sock); // init a new socket for the family looker->sock = tb_socket_init(TB_SOCKET_TYPE_UDP, tb_ipaddr_family(addr)); tb_assert_and_check_return_val(looker->sock, -1); // update the new family looker->family = (tb_uint8_t)tb_ipaddr_family(addr); } // need wait if no data looker->step &= ~TB_DNS_LOOKER_STEP_NEVT; // trace tb_trace_d("request: try %{ipaddr}", addr); // send request while (looker->size < size) { // writ data tb_long_t writ = tb_socket_usend(looker->sock, addr, data + looker->size, size - looker->size); tb_assert_and_check_return_val(writ >= 0, -1); // no data? if (!writ) { // abort? tb_check_return_val(!looker->size && !looker->tryn, -1); // tryn++ looker->tryn++; // continue return 0; } else looker->tryn = 0; // update size looker->size += writ; } // finish it looker->step |= TB_DNS_LOOKER_STEP_REQT; looker->tryn = 0; // reset rpkt looker->size = 0; tb_static_buffer_clear(&looker->rpkt); // ok tb_trace_d("request: ok"); return 1; }
/* ////////////////////////////////////////////////////////////////////////////////////// * implementation */ static tb_bool_t tb_mswsock_instance_init(tb_handle_t instance) { // check tb_mswsock_ref_t mswsock = (tb_mswsock_ref_t)instance; tb_assert_and_check_return_val(mswsock, tb_false); // done tb_socket_ref_t sock = tb_null; do { // init sock sock = tb_socket_init(TB_SOCKET_TYPE_TCP); tb_assert_and_check_break(sock); // init AcceptEx DWORD AcceptEx_real = 0; GUID AcceptEx_guid = TB_MSWSOCK_WSAID_ACCEPTEX; tb_ws2_32()->WSAIoctl( (SOCKET)sock - 1 , SIO_GET_EXTENSION_FUNCTION_POINTER , &AcceptEx_guid , sizeof(GUID) , &mswsock->AcceptEx , sizeof(tb_mswsock_AcceptEx_t) , &AcceptEx_real , tb_null , tb_null); // init ConnectEx DWORD ConnectEx_real = 0; GUID ConnectEx_guid = TB_MSWSOCK_WSAID_CONNECTEX; tb_ws2_32()->WSAIoctl( (SOCKET)sock - 1 , SIO_GET_EXTENSION_FUNCTION_POINTER , &ConnectEx_guid , sizeof(GUID) , &mswsock->ConnectEx , sizeof(tb_mswsock_ConnectEx_t) , &ConnectEx_real , tb_null , tb_null); // init DisconnectEx DWORD DisconnectEx_real = 0; GUID DisconnectEx_guid = TB_MSWSOCK_WSAID_DISCONNECTEX; tb_ws2_32()->WSAIoctl( (SOCKET)sock - 1 , SIO_GET_EXTENSION_FUNCTION_POINTER , &DisconnectEx_guid , sizeof(GUID) , &mswsock->DisconnectEx , sizeof(tb_mswsock_DisconnectEx_t) , &DisconnectEx_real , tb_null , tb_null); // init TransmitFile DWORD TransmitFile_real = 0; GUID TransmitFile_guid = TB_MSWSOCK_WSAID_TRANSMITFILE; tb_ws2_32()->WSAIoctl( (SOCKET)sock - 1 , SIO_GET_EXTENSION_FUNCTION_POINTER , &TransmitFile_guid , sizeof(GUID) , &mswsock->TransmitFile , sizeof(tb_mswsock_TransmitFile_t) , &TransmitFile_real , tb_null , tb_null); // init GetAcceptExSockaddrs DWORD GetAcceptExSockaddrs_real = 0; GUID GetAcceptExSockaddrs_guid = TB_MSWSOCK_WSAID_GETACCEPTEXSOCKADDRS; tb_ws2_32()->WSAIoctl( (SOCKET)sock - 1 , SIO_GET_EXTENSION_FUNCTION_POINTER , &GetAcceptExSockaddrs_guid , sizeof(GUID) , &mswsock->GetAcceptExSockaddrs , sizeof(tb_mswsock_GetAcceptExSockaddrs_t) , &GetAcceptExSockaddrs_real , tb_null , tb_null); } while (0); // exit sock if (sock) tb_socket_exit(sock); // ok return tb_true; }