void __CFSocketCallBack(CFSocketRef s, CFSocketCallBackType callbackType, CFDataRef address, const void *data, void *info) { // Extract the context tnet_transport_t *transport = (tnet_transport_t *) info; transport_context_t *context = transport->context; // Extract the native socket int fd = CFSocketGetNative(s); transport_socket_t *sock = (transport_socket_t *) getSocket(context, fd); /* lock context */ tsk_safeobj_lock(context); switch (callbackType) { case kCFSocketReadCallBack: { int ret; tsk_size_t len = 0; void* buffer = 0; tnet_transport_event_t* e; if (tnet_ioctlt(sock->fd, FIONREAD, &len) < 0) { TNET_PRINT_LAST_ERROR("IOCTLT FAILED."); break; } if (!len) { TSK_DEBUG_WARN("IOCTLT returned zero."); TSK_RUNNABLE_ENQUEUE(transport, event_closed, transport->callback_data, sock->fd); removeSocket(sock, context); break; } if (!(buffer = tsk_calloc(len, sizeof(uint8_t)))) { TSK_DEBUG_ERROR("TSK_CALLOC FAILED."); break; } if ((ret = tnet_sockfd_recv(sock->fd, buffer, len, 0)) < 0) { TSK_FREE(buffer); removeSocket(sock, context); TNET_PRINT_LAST_ERROR("recv have failed."); break; } else if ((len != (tsk_size_t)ret) && len) { // useless test ? len = (tsk_size_t)ret; // buffer = tsk_realloc(buffer, len); } TSK_DEBUG_INFO("__CFSocketCallBack -> %u bytes read", len); e = tnet_transport_event_create(event_data, transport->callback_data, sock->fd); e->data = buffer; e->size = len; TSK_RUNNABLE_ENQUEUE_OBJECT(TSK_RUNNABLE(transport), e); break; } case kCFSocketAcceptCallBack: case kCFSocketConnectCallBack: case kCFSocketDataCallBack: case kCFSocketWriteCallBack: default: { // Not Implemented assert(42 == 0); break; } } /* unlock context */ tsk_safeobj_unlock(context); }
/*=== Main thread */ void *tnet_transport_mainthread(void *param) { tnet_transport_t *transport = param; transport_context_t *context = transport->context; int ret, status; tsk_size_t i; tsk_bool_t is_stream; tnet_fd_t fd; struct sockaddr_storage remote_addr = {0}; transport_socket_xt* active_socket; /* check whether the transport is already prepared */ if(!transport->prepared) { TSK_DEBUG_ERROR("Transport must be prepared before strating."); goto bail; } is_stream = TNET_SOCKET_TYPE_IS_STREAM(transport->master->type); TSK_DEBUG_INFO("Starting [%s] server with IP {%s} on port {%d} using master fd {%d} with type {%d} with max_fds {%lu}...", transport->description, transport->master->ip, transport->master->port, transport->master->fd, transport->master->type, sizeof(context->ufds)/sizeof(context->ufds[0])); while(TSK_RUNNABLE(transport)->running || TSK_RUNNABLE(transport)->started) { context->polling = tsk_true; ret = tnet_poll(context->ufds, context->count, -1); context->polling = tsk_false; if(ret < 0) { TNET_PRINT_LAST_ERROR("poll() have failed."); goto bail; } if(!TSK_RUNNABLE(transport)->running && !TSK_RUNNABLE(transport)->started) { TSK_DEBUG_INFO("Stopping [%s] server with IP {%s} on port {%d} with type {%d}...", transport->description, transport->master->ip, transport->master->port, transport->master->type); goto bail; } /* lock context */ tsk_safeobj_lock(context); /* == == */ for(i=0; i<context->count; i++) { if(!context->ufds[i].revents) { continue; } // TSK_DEBUG_INFO("REVENTS(i=%d) = %d", i, context->ufds[i].revents); if(context->ufds[i].fd == context->pipeR) { TSK_DEBUG_INFO("PipeR event = %d", context->ufds[i].revents); if(context->ufds[i].revents & TNET_POLLIN) { static char __buffer[1024]; if(read(context->pipeR, __buffer, sizeof(__buffer)) < 0) { TNET_PRINT_LAST_ERROR("Failed to read from the Pipe"); } } else if(context->ufds[i].revents & TNET_POLLHUP) { TNET_PRINT_LAST_ERROR("Pipe Error"); goto bail; } context->ufds[i].revents = 0; continue; } /* Get active event and socket */ active_socket = context->sockets[i]; /*================== TNET_POLLHUP ==================*/ if(context->ufds[i].revents & (TNET_POLLHUP)) { if(context->ufds[i].revents & TNET_POLLOUT) { TSK_DEBUG_INFO("POLLOUT and POLLHUP are exclusive"); } else { fd = active_socket->fd; TSK_DEBUG_INFO("NETWORK EVENT FOR SERVER [%s] -- TNET_POLLHUP(%d)", transport->description, fd); tnet_transport_remove_socket(transport, &active_socket->fd); TSK_RUNNABLE_ENQUEUE(transport, event_closed, transport->callback_data, fd); continue; } } /*================== TNET_POLLERR ==================*/ if(context->ufds[i].revents & (TNET_POLLERR)) { fd = active_socket->fd; TSK_DEBUG_INFO("NETWORK EVENT FOR SERVER [%s] -- TNET_POLLERR(%d)", transport->description, fd); tnet_transport_remove_socket(transport, &active_socket->fd); TSK_RUNNABLE_ENQUEUE(transport, event_error, transport->callback_data, fd); continue; } /*================== TNET_POLLNVAL ==================*/ if(context->ufds[i].revents & (TNET_POLLNVAL)) { fd = active_socket->fd; TSK_DEBUG_INFO("NETWORK EVENT FOR SERVER [%s] -- TNET_POLLNVAL(%d)", transport->description, fd); tnet_transport_remove_socket(transport, &active_socket->fd); TSK_RUNNABLE_ENQUEUE(transport, event_error, transport->callback_data, fd); continue; } /*================== POLLIN ==================*/ if(context->ufds[i].revents & TNET_POLLIN) { tsk_size_t len = 0; void* buffer = tsk_null; tnet_transport_event_t* e; // TSK_DEBUG_INFO("NETWORK EVENT FOR SERVER [%s] -- TNET_POLLIN(%d)", transport->description, active_socket->fd); /* check whether the socket is paused or not */ if(active_socket->paused) { TSK_DEBUG_INFO("Socket is paused"); goto TNET_POLLIN_DONE; } /* Retrieve the amount of pending data. * IMPORTANT: If you are using Symbian please update your SDK to the latest build (August 2009) to have 'FIONREAD'. * This apply whatever you are using the 3rd or 5th edition. * Download link: http://wiki.forum.nokia.com/index.php/Open_C/C%2B%2B_Release_History */ ret = tnet_ioctlt(active_socket->fd, FIONREAD, &len); if((ret < 0 || !len) && is_stream) { /* It's probably an incoming connection --> try to accept() it */ int listening = 0, remove_socket = 0; socklen_t socklen = sizeof(listening); TSK_DEBUG_INFO("ioctlt(%d), len=%u returned zero or failed", active_socket->fd, (unsigned)len); // check if socket is listening if(getsockopt(active_socket->fd, SOL_SOCKET, SO_ACCEPTCONN, &listening, &socklen) != 0) { #if defined(BSD) /* old FreeBSD versions (and OSX up to Lion) do not support SO_ACCEPTCONN */ listening = 1; #else TNET_PRINT_LAST_ERROR("getsockopt(SO_ACCEPTCONN, %d) failed\n", active_socket->fd); /* not socket accepted -> no socket to remove */ goto TNET_POLLIN_DONE; #endif } if (listening) { if((fd = accept(active_socket->fd, tsk_null, tsk_null)) != TNET_INVALID_SOCKET) { TSK_DEBUG_INFO("NETWORK EVENT FOR SERVER [%s] -- FD_ACCEPT(fd=%d)", transport->description, fd); addSocket(fd, transport->master->type, transport, tsk_true, tsk_false, tsk_null); TSK_RUNNABLE_ENQUEUE(transport, event_accepted, transport->callback_data, fd); if(active_socket->tlshandle) { transport_socket_xt* tls_socket; if((tls_socket = getSocket(context, fd))) { if(tnet_tls_socket_accept(tls_socket->tlshandle) != 0) { TSK_RUNNABLE_ENQUEUE(transport, event_closed, transport->callback_data, fd); tnet_transport_remove_socket(transport, &fd); TNET_PRINT_LAST_ERROR("SSL_accept() failed"); continue; } } } } else { TNET_PRINT_LAST_ERROR("accept(%d) failed", active_socket->fd); remove_socket = 1; } } else { TSK_DEBUG_INFO("Closing socket with fd = %d because ioctlt() returned zero or failed", active_socket->fd); remove_socket = 1; } if(remove_socket) { fd = active_socket->fd; tnet_transport_remove_socket(transport, &active_socket->fd); TSK_RUNNABLE_ENQUEUE(transport, event_closed, transport->callback_data, fd); continue; } goto TNET_POLLIN_DONE; } if(len <= 0) { #if defined(__ANDROID__) || defined(ANDROID) // workaround for indoona OSX which sends bodiless UDP packets // vand Android requires to call recv() even if len is equal to zero if(len == 0 && ret == 0) { static char __fake_buff[1]; ret = recv(active_socket->fd, __fake_buff, len, 0); } #endif goto TNET_POLLIN_DONE; } if (!(buffer = tsk_calloc(len, sizeof(uint8_t)))) { TSK_DEBUG_ERROR("TSK_CALLOC FAILED"); goto TNET_POLLIN_DONE; } // Retrieve the remote address if (TNET_SOCKET_TYPE_IS_STREAM(transport->master->type)) { ret = tnet_getpeername(active_socket->fd, &remote_addr); } // Receive the waiting data if (active_socket->tlshandle) { int isEncrypted; tsk_size_t tlslen = len; if ((ret = tnet_tls_socket_recv(active_socket->tlshandle, &buffer, &tlslen, &isEncrypted)) == 0) { if (isEncrypted) { TSK_FREE(buffer); goto TNET_POLLIN_DONE; } if (ret == 0) { len = ret = tlslen; } } } else { if (is_stream) { ret = tnet_sockfd_recv(active_socket->fd, buffer, len, 0); } else { ret = tnet_sockfd_recvfrom(active_socket->fd, buffer, len, 0, (struct sockaddr*)&remote_addr); } } if(ret < 0) { TSK_FREE(buffer); status = tnet_geterrno(); // do not remove the socket for i/o pending errors if (status == TNET_ERROR_WOULDBLOCK || status == TNET_ERROR_INPROGRESS || status == TNET_ERROR_EAGAIN) { TSK_DEBUG_WARN("recv returned error code:%d", status); } else { TNET_PRINT_LAST_ERROR("recv/recvfrom have failed"); removeSocket(i, context); } goto TNET_POLLIN_DONE; } if((len != (tsk_size_t)ret) && len) { len = (tsk_size_t)ret; // buffer = tsk_realloc(buffer, len); } if(len > 0) { transport->bytes_in += len; e = tnet_transport_event_create(event_data, transport->callback_data, active_socket->fd); e->data = buffer, buffer = tsk_null; e->size = len; e->remote_addr = remote_addr; TSK_RUNNABLE_ENQUEUE_OBJECT_SAFE(TSK_RUNNABLE(transport), e); } TSK_FREE(buffer); TNET_POLLIN_DONE: /*context->ufds[i].revents &= ~TNET_POLLIN*/ ; } /*================== TNET_POLLOUT ==================*/ if(context->ufds[i].revents & TNET_POLLOUT) { TSK_DEBUG_INFO("NETWORK EVENT FOR SERVER [%s] -- TNET_POLLOUT", transport->description); if(!active_socket->connected) { active_socket->connected = tsk_true; TSK_RUNNABLE_ENQUEUE(transport, event_connected, transport->callback_data, active_socket->fd); } //else{ context->ufds[i].events &= ~TNET_POLLOUT; //} } /*================== TNET_POLLPRI ==================*/ if(context->ufds[i].revents & TNET_POLLPRI) { TSK_DEBUG_INFO("NETWORK EVENT FOR SERVER [%s] -- TNET_POLLPRI", transport->description); } context->ufds[i].revents = 0; }/* for */ done: /* unlock context */ tsk_safeobj_unlock(context); } /* while */ bail: TSK_DEBUG_INFO("Stopped [%s] server with IP {%s} on port {%d}", transport->description, transport->master->ip, transport->master->port); return 0; }