// // blocking accept() // Pj_Sock_Stream accept(Pj_Inet_Addr *remote_addr = NULL) { pj_sock_t newsock; int *addrlen = remote_addr ? &remote_addr->addrlen_ : NULL; pj_status_t status; status = pj_sock_accept(sock_, &newsock, remote_addr, addrlen); if (status != PJ_SUCCESS) return Pj_Sock_Stream(-1); return Pj_Sock_Stream(newsock); }
static int server_thread(void *p) { struct server_t *srv = (struct server_t*)p; char *pkt = (char*)pj_pool_alloc(pool, srv->buf_size); pj_sock_t newsock = PJ_INVALID_SOCKET; while (!thread_quit) { pj_ssize_t pkt_len; int rc; pj_fd_set_t rset; pj_time_val timeout = {0, 500}; while (!thread_quit) { PJ_FD_ZERO(&rset); PJ_FD_SET(srv->sock, &rset); rc = pj_sock_select((int)srv->sock+1, &rset, NULL, NULL, &timeout); if (rc != 1) { continue; } rc = pj_sock_accept(srv->sock, &newsock, NULL, NULL); if (rc == PJ_SUCCESS) { break; } } if (thread_quit) break; while (!thread_quit) { PJ_FD_ZERO(&rset); PJ_FD_SET(newsock, &rset); rc = pj_sock_select((int)newsock+1, &rset, NULL, NULL, &timeout); if (rc != 1) { PJ_LOG(3,("http test", "client timeout")); continue; } pkt_len = srv->buf_size; rc = pj_sock_recv(newsock, pkt, &pkt_len, 0); if (rc == PJ_SUCCESS) { break; } } if (thread_quit) break; /* Simulate network RTT */ pj_thread_sleep(50); if (srv->action == ACTION_IGNORE) { continue; } else if (srv->action == ACTION_REPLY) { pj_size_t send_size = 0; unsigned ctr = 0; pj_ansi_sprintf(pkt, "HTTP/1.0 200 OK\r\n"); if (srv->send_content_length) { pj_ansi_sprintf(pkt + pj_ansi_strlen(pkt), "Content-Length: %d\r\n", srv->data_size); } pj_ansi_sprintf(pkt + pj_ansi_strlen(pkt), "\r\n"); pkt_len = pj_ansi_strlen(pkt); rc = pj_sock_send(newsock, pkt, &pkt_len, 0); if (rc != PJ_SUCCESS) { pj_sock_close(newsock); continue; } while (send_size < srv->data_size) { pkt_len = srv->data_size - send_size; if (pkt_len > (signed)srv->buf_size) pkt_len = srv->buf_size; send_size += pkt_len; pj_create_random_string(pkt, pkt_len); pj_ansi_sprintf(pkt, "\nPacket: %d", ++ctr); pkt[pj_ansi_strlen(pkt)] = '\n'; rc = pj_sock_send(newsock, pkt, &pkt_len, 0); if (rc != PJ_SUCCESS) break; } pj_sock_close(newsock); } } return 0; }
pj_status_t app_socketpair(int family, int type, int protocol, pj_sock_t *serverfd, pj_sock_t *clientfd) { int i; static unsigned short port = 11000; pj_sockaddr_in addr; pj_str_t s; pj_status_t rc = 0; pj_sock_t sock[2]; /* Create both sockets. */ for (i=0; i<2; ++i) { rc = pj_sock_socket(family, type, protocol, &sock[i]); if (rc != PJ_SUCCESS) { if (i==1) pj_sock_close(sock[0]); return rc; } } /* Retry bind */ pj_bzero(&addr, sizeof(addr)); addr.sin_family = pj_AF_INET(); for (i=0; i<5; ++i) { addr.sin_port = pj_htons(port++); rc = pj_sock_bind(sock[SERVER], &addr, sizeof(addr)); if (rc == PJ_SUCCESS) break; } if (rc != PJ_SUCCESS) goto on_error; /* For TCP, listen the socket. */ #if PJ_HAS_TCP if (type == pj_SOCK_STREAM()) { rc = pj_sock_listen(sock[SERVER], PJ_SOMAXCONN); if (rc != PJ_SUCCESS) goto on_error; } #endif /* Connect client socket. */ addr.sin_addr = pj_inet_addr(pj_cstr(&s, "127.0.0.1")); rc = pj_sock_connect(sock[CLIENT], &addr, sizeof(addr)); if (rc != PJ_SUCCESS) goto on_error; /* For TCP, must accept(), and get the new socket. */ #if PJ_HAS_TCP if (type == pj_SOCK_STREAM()) { pj_sock_t newserver; rc = pj_sock_accept(sock[SERVER], &newserver, NULL, NULL); if (rc != PJ_SUCCESS) goto on_error; /* Replace server socket with new socket. */ pj_sock_close(sock[SERVER]); sock[SERVER] = newserver; } #endif *serverfd = sock[SERVER]; *clientfd = sock[CLIENT]; return rc; on_error: for (i=0; i<2; ++i) pj_sock_close(sock[i]); return rc; }
/* * Initiate overlapped accept() operation. */ PJ_DEF(pj_status_t) pj_ioqueue_accept( pj_ioqueue_key_t *key, pj_ioqueue_op_key_t *op_key, pj_sock_t *new_sock, pj_sockaddr_t *local, pj_sockaddr_t *remote, int *addrlen) { struct accept_operation *accept_op; pj_status_t status; /* check parameters. All must be specified! */ PJ_ASSERT_RETURN(key && op_key && new_sock, PJ_EINVAL); /* Check if key is closing. */ if (IS_CLOSING(key)) return PJ_ECANCELLED; accept_op = (struct accept_operation*)op_key; accept_op->op = PJ_IOQUEUE_OP_NONE; /* Fast track: * See if there's new connection available immediately. */ if (pj_list_empty(&key->accept_list)) { status = pj_sock_accept(key->fd, new_sock, remote, addrlen); if (status == PJ_SUCCESS) { /* Yes! New connection is available! */ if (local && addrlen) { status = pj_sock_getsockname(*new_sock, local, addrlen); if (status != PJ_SUCCESS) { pj_sock_close(*new_sock); *new_sock = PJ_INVALID_SOCKET; return status; } } return PJ_SUCCESS; } else { /* If error is not EWOULDBLOCK (or EAGAIN on Linux), report * the error to caller. */ if (status != PJ_STATUS_FROM_OS(PJ_BLOCKING_ERROR_VAL)) { return status; } } } /* * No connection is available immediately. * Schedule accept() operation to be completed when there is incoming * connection available. */ accept_op->op = PJ_IOQUEUE_OP_ACCEPT; accept_op->accept_fd = new_sock; accept_op->rmt_addr = remote; accept_op->addrlen= addrlen; accept_op->local_addr = local; pj_mutex_lock(key->mutex); pj_list_insert_before(&key->accept_list, accept_op); ioqueue_add_to_set(key->ioqueue, key, READABLE_EVENT); pj_mutex_unlock(key->mutex); return PJ_EPENDING; }
void ioqueue_dispatch_read_event( pj_ioqueue_t *ioqueue, pj_ioqueue_key_t *h ) { pj_status_t rc; /* Lock the key. */ pj_mutex_lock(h->mutex); if (IS_CLOSING(h)) { pj_mutex_unlock(h->mutex); return; } # if PJ_HAS_TCP if (!pj_list_empty(&h->accept_list)) { struct accept_operation *accept_op; pj_bool_t has_lock; /* Get one accept operation from the list. */ accept_op = h->accept_list.next; pj_list_erase(accept_op); accept_op->op = PJ_IOQUEUE_OP_NONE; /* Clear bit in fdset if there is no more pending accept */ if (pj_list_empty(&h->accept_list)) ioqueue_remove_from_set(ioqueue, h, READABLE_EVENT); rc=pj_sock_accept(h->fd, accept_op->accept_fd, accept_op->rmt_addr, accept_op->addrlen); if (rc==PJ_SUCCESS && accept_op->local_addr) { rc = pj_sock_getsockname(*accept_op->accept_fd, accept_op->local_addr, accept_op->addrlen); } /* Unlock; from this point we don't need to hold key's mutex * (unless concurrency is disabled, which in this case we should * hold the mutex while calling the callback) */ if (h->allow_concurrent) { /* concurrency may be changed while we're in the callback, so * save it to a flag. */ has_lock = PJ_FALSE; pj_mutex_unlock(h->mutex); } else { has_lock = PJ_TRUE; } /* Call callback. */ if (h->cb.on_accept_complete && !IS_CLOSING(h)) { (*h->cb.on_accept_complete)(h, (pj_ioqueue_op_key_t*)accept_op, *accept_op->accept_fd, rc); } if (has_lock) { pj_mutex_unlock(h->mutex); } } else # endif if (key_has_pending_read(h)) { struct read_operation *read_op; pj_ssize_t bytes_read; pj_bool_t has_lock; /* Get one pending read operation from the list. */ read_op = h->read_list.next; pj_list_erase(read_op); /* Clear fdset if there is no pending read. */ if (pj_list_empty(&h->read_list)) ioqueue_remove_from_set(ioqueue, h, READABLE_EVENT); bytes_read = read_op->size; if ((read_op->op == PJ_IOQUEUE_OP_RECV_FROM)) { read_op->op = PJ_IOQUEUE_OP_NONE; rc = pj_sock_recvfrom(h->fd, read_op->buf, &bytes_read, read_op->flags, read_op->rmt_addr, read_op->rmt_addrlen); } else if ((read_op->op == PJ_IOQUEUE_OP_RECV)) { read_op->op = PJ_IOQUEUE_OP_NONE; rc = pj_sock_recv(h->fd, read_op->buf, &bytes_read, read_op->flags); } else { pj_assert(read_op->op == PJ_IOQUEUE_OP_READ); read_op->op = PJ_IOQUEUE_OP_NONE; /* * User has specified pj_ioqueue_read(). * On Win32, we should do ReadFile(). But because we got * here because of select() anyway, user must have put a * socket descriptor on h->fd, which in this case we can * just call pj_sock_recv() instead of ReadFile(). * On Unix, user may put a file in h->fd, so we'll have * to call read() here. * This may not compile on systems which doesn't have * read(). That's why we only specify PJ_LINUX here so * that error is easier to catch. */ # if defined(PJ_WIN32) && PJ_WIN32 != 0 || \ defined(PJ_WIN32_WINCE) && PJ_WIN32_WINCE != 0 rc = pj_sock_recv(h->fd, read_op->buf, &bytes_read, read_op->flags); //rc = ReadFile((HANDLE)h->fd, read_op->buf, read_op->size, // &bytes_read, NULL); # elif (defined(PJ_HAS_UNISTD_H) && PJ_HAS_UNISTD_H != 0) bytes_read = read(h->fd, read_op->buf, bytes_read); rc = (bytes_read >= 0) ? PJ_SUCCESS : pj_get_os_error(); # elif defined(PJ_LINUX_KERNEL) && PJ_LINUX_KERNEL != 0 bytes_read = sys_read(h->fd, read_op->buf, bytes_read); rc = (bytes_read >= 0) ? PJ_SUCCESS : -bytes_read; # else # error "Implement read() for this platform!" # endif } if (rc != PJ_SUCCESS) { # if defined(PJ_WIN32) && PJ_WIN32 != 0 /* On Win32, for UDP, WSAECONNRESET on the receive side * indicates that previous sending has triggered ICMP Port * Unreachable message. * But we wouldn't know at this point which one of previous * key that has triggered the error, since UDP socket can * be shared! * So we'll just ignore it! */ if (rc == PJ_STATUS_FROM_OS(WSAECONNRESET)) { //PJ_LOG(4,(THIS_FILE, // "Ignored ICMP port unreach. on key=%p", h)); } # endif /* In any case we would report this to caller. */ bytes_read = -rc; } /* Unlock; from this point we don't need to hold key's mutex * (unless concurrency is disabled, which in this case we should * hold the mutex while calling the callback) */ if (h->allow_concurrent) { /* concurrency may be changed while we're in the callback, so * save it to a flag. */ has_lock = PJ_FALSE; pj_mutex_unlock(h->mutex); } else { has_lock = PJ_TRUE; } /* Call callback. */ if (h->cb.on_read_complete && !IS_CLOSING(h)) { (*h->cb.on_read_complete)(h, (pj_ioqueue_op_key_t*)read_op, bytes_read); } if (has_lock) { pj_mutex_unlock(h->mutex); } } else { /* * This is normal; execution may fall here when multiple threads * are signalled for the same event, but only one thread eventually * able to process the event. */ pj_mutex_unlock(h->mutex); } }
PJ_DEF(int) pj_ioqueue_poll( pj_ioqueue_t *ioque, const pj_time_val *timeout) { pj_fdset_t rfdset, wfdset, xfdset; int rc; pj_ioqueue_key_t *h; /* Copy ioqueue's fd_set to local variables. */ pj_mutex_lock(ioque->mutex); rfdset = ioque->rfdset; wfdset = ioque->wfdset; #if PJ_HAS_TCP xfdset = ioque->xfdset; #else PJ_FD_ZERO(&xfdset); #endif /* Unlock ioqueue before select(). */ pj_mutex_unlock(ioque->mutex); rc = pj_sock_select(FD_SETSIZE, &rfdset, &wfdset, &xfdset, timeout); if (rc <= 0) return rc; /* Lock ioqueue again before scanning for signalled sockets. */ pj_mutex_lock(ioque->mutex); #if PJ_HAS_TCP /* Scan for exception socket */ h = ioque->hlist.next; for ( ; h!=&ioque->hlist; h = h->next) { if ((h->op & PJ_IOQUEUE_OP_CONNECT) && PJ_FD_ISSET(h->fd, &xfdset)) break; } if (h != &ioque->hlist) { /* 'connect()' should be the only operation. */ pj_assert((h->op == PJ_IOQUEUE_OP_CONNECT)); /* Clear operation. */ h->op &= ~(PJ_IOQUEUE_OP_CONNECT); PJ_FD_CLR(h->fd, &ioque->wfdset); PJ_FD_CLR(h->fd, &ioque->xfdset); /* Unlock I/O queue before calling callback. */ pj_mutex_unlock(ioque->mutex); /* Call callback. */ (*h->cb.on_connect_complete)(h, -1); return 1; } #endif /* PJ_HAS_TCP */ /* Scan for writable socket */ h = ioque->hlist.next; for ( ; h!=&ioque->hlist; h = h->next) { if ((PJ_IOQUEUE_IS_WRITE_OP(h->op) || PJ_IOQUEUE_IS_CONNECT_OP(h->op)) && PJ_FD_ISSET(h->fd, &wfdset)) break; } if (h != &ioque->hlist) { pj_assert(PJ_IOQUEUE_IS_WRITE_OP(h->op) || PJ_IOQUEUE_IS_CONNECT_OP(h->op)); #if PJ_HAS_TCP if ((h->op & PJ_IOQUEUE_OP_CONNECT)) { /* Completion of connect() operation */ pj_ssize_t bytes_transfered; #if defined(PJ_LINUX) /* from connect(2): * On Linux, use getsockopt to read the SO_ERROR option at * level SOL_SOCKET to determine whether connect() completed * successfully (if SO_ERROR is zero). */ int value; socklen_t vallen = sizeof(value); int rc = getsockopt(h->fd, SOL_SOCKET, SO_ERROR, &value, &vallen); if (rc != 0) { /* Argh!! What to do now??? * Just indicate that the socket is connected. The * application will get error as soon as it tries to use * the socket to send/receive. */ PJ_PERROR(("ioqueue", "Unable to determine connect() status")); bytes_transfered = 0; } else { bytes_transfered = value; } #elif defined(PJ_WIN32) bytes_transfered = 0; /* success */ #else # error "Got to check this one!" #endif /* Clear operation. */ h->op &= (~PJ_IOQUEUE_OP_CONNECT); PJ_FD_CLR(h->fd, &ioque->wfdset); PJ_FD_CLR(h->fd, &ioque->xfdset); /* Unlock mutex before calling callback. */ pj_mutex_unlock(ioque->mutex); /* Call callback. */ (*h->cb.on_connect_complete)(h, bytes_transfered); return 1; } else #endif /* PJ_HAS_TCP */ { /* Completion of write(), send(), or sendto() operation. */ /* Clear operation. */ h->op &= ~(PJ_IOQUEUE_OP_WRITE | PJ_IOQUEUE_OP_SEND_TO); PJ_FD_CLR(h->fd, &ioque->wfdset); /* Unlock mutex before calling callback. */ pj_mutex_unlock(ioque->mutex); /* Call callback. */ /* All data must have been sent? */ (*h->cb.on_write_complete)(h, h->wr_buflen); return 1; } /* Unreached. */ } /* Scan for readable socket. */ h = ioque->hlist.next; for ( ; h!=&ioque->hlist; h = h->next) { if ((PJ_IOQUEUE_IS_READ_OP(h->op) || PJ_IOQUEUE_IS_ACCEPT_OP(h->op)) && PJ_FD_ISSET(h->fd, &rfdset)) break; } if (h != &ioque->hlist) { pj_assert(PJ_IOQUEUE_IS_READ_OP(h->op) || PJ_IOQUEUE_IS_ACCEPT_OP(h->op)); # if PJ_HAS_TCP if ((h->op & PJ_IOQUEUE_OP_ACCEPT)) { /* accept() must be the only operation specified on server socket */ pj_assert(h->op == PJ_IOQUEUE_OP_ACCEPT); *h->accept_fd = pj_sock_accept(h->fd, h->rmt_addr, h->rmt_addrlen); if (*h->accept_fd == PJ_INVALID_SOCKET) { rc = -1; } else if (h->local_addr) { rc = pj_sock_getsockname(*h->accept_fd, h->local_addr, h->local_addrlen); } else { rc = 0; } h->op &= ~(PJ_IOQUEUE_OP_ACCEPT); PJ_FD_CLR(h->fd, &ioque->rfdset); /* Unlock mutex before calling callback. */ pj_mutex_unlock(ioque->mutex); /* Call callback. */ (*h->cb.on_accept_complete)(h, rc); return 1; } else # endif if ((h->op & PJ_IOQUEUE_OP_RECV_FROM)) { rc = pj_sock_recvfrom(h->fd, h->rd_buf, h->rd_buflen, 0, h->rmt_addr, h->rmt_addrlen); } else { rc = pj_sock_recv(h->fd, h->rd_buf, h->rd_buflen, 0); } if (rc < 0) { pj_status_t sock_err = -1; # if defined(_WIN32) /* On Win32, for UDP, WSAECONNRESET on the receive side * indicates that previous sending has triggered ICMP Port * Unreachable message. * But we wouldn't know at this point which one of previous * key that has triggered the error, since UDP socket can * be shared! * So we'll just ignore it! */ sock_err = pj_sock_getlasterror(); if (sock_err == PJ_ECONNRESET) { pj_mutex_unlock(ioque->mutex); PJ_LOG(4,(THIS_FILE, "Received ICMP port unreachable on key=%p (ignored)!", h)); return 0; } # endif PJ_LOG(4, (THIS_FILE, "socket recv error on key %p, rc=%d, err=%d", h, rc, sock_err)); } h->op &= ~(PJ_IOQUEUE_OP_READ | PJ_IOQUEUE_OP_RECV_FROM); PJ_FD_CLR(h->fd, &ioque->rfdset); /* Unlock mutex before callback. */ pj_mutex_unlock(ioque->mutex); /* Call callback. */ (*h->cb.on_read_complete)(h, rc); return 1; } /* Shouldn't happen. */ /* For strange reason on WinXP select() can return 1 while there is no * fd_set signaled. */ /* pj_assert(0); */ rc = 0; pj_mutex_unlock(ioque->mutex); return rc; }