static void on_read_complete(pj_ioqueue_key_t *key, pj_ioqueue_op_key_t *op_key, pj_ssize_t bytes_read) { pj_ssize_t size; char *sendbuf = "Hello world"; pj_status_t status; if (sock_data.unregistered) return; pj_mutex_lock(sock_data.mutex); if (sock_data.unregistered) { pj_mutex_unlock(sock_data.mutex); return; } if (bytes_read < 0) { if (-bytes_read != PJ_STATUS_FROM_OS(PJ_BLOCKING_ERROR_VAL)) app_perror("ioqueue reported recv error", -bytes_read); } else { sock_data.received += bytes_read; } if (test_method == UNREGISTER_IN_CALLBACK) { pj_time_val now; pj_gettimeofday(&now); if (PJ_TIME_VAL_GTE(now, time_to_unregister)) { sock_data.unregistered = 1; pj_ioqueue_unregister(key); pj_mutex_destroy(sock_data.mutex); pj_pool_release(sock_data.pool); sock_data.pool = NULL; return; } } do { size = sock_data.bufsize; status = pj_ioqueue_recv(key, op_key, sock_data.buffer, &size, 0); if (status != PJ_EPENDING && status != PJ_SUCCESS) app_perror("recv() error", status); } while (status == PJ_SUCCESS); pj_mutex_unlock(sock_data.mutex); size = pj_ansi_strlen(sendbuf); status = pj_sock_send(sock_data.csock, sendbuf, &size, 0); if (status != PJ_SUCCESS) app_perror("send() error", status); size = pj_ansi_strlen(sendbuf); status = pj_sock_send(sock_data.csock, sendbuf, &size, 0); if (status != PJ_SUCCESS) app_perror("send() error", status); }
// // send data. // pj_ssize_t send(const void *buf, pj_ssize_t len, int flag = 0) { pj_ssize_t bytes = len; if (pj_sock_send(sock_, buf, &bytes, flag) != PJ_SUCCESS) return -1; return bytes; }
void SendMessage(pj_uint32_t Msg, WPARAM wParam, LPARAM lParam) { param_t param = {Msg, wParam, lParam}; pj_ssize_t sndlen = sizeof(param_t); pj_sock_send(g_mainframe_pipe[1], ¶m, &sndlen, 0); }
PJ_DEF(int) pj_ioqueue_write( pj_ioqueue_t *ioque, pj_ioqueue_key_t *key, const void *data, pj_size_t datalen) { if (pj_sock_send(key->fd, data, datalen, 0) != (pj_ssize_t)datalen) return -1; pj_mutex_lock(ioque->mutex); key->op |= PJ_IOQUEUE_OP_WRITE; key->wr_buf = NULL; key->wr_buflen = datalen; PJ_FD_SET(key->fd, &ioque->wfdset); pj_mutex_unlock(ioque->mutex); return PJ_IOQUEUE_PENDING; }
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; }
static int send_recv_test(int sock_type, pj_sock_t ss, pj_sock_t cs, pj_sockaddr_in *dstaddr, pj_sockaddr_in *srcaddr, int addrlen) { enum { DATA_LEN = 16 }; char senddata[DATA_LEN+4], recvdata[DATA_LEN+4]; pj_ssize_t sent, received, total_received; pj_status_t rc; TRACE_(("test", "....create_random_string()")); pj_create_random_string(senddata, DATA_LEN); senddata[DATA_LEN-1] = '\0'; /* * Test send/recv small data. */ TRACE_(("test", "....sendto()")); if (dstaddr) { sent = DATA_LEN; rc = pj_sock_sendto(cs, senddata, &sent, 0, dstaddr, addrlen); if (rc != PJ_SUCCESS || sent != DATA_LEN) { app_perror("...sendto error", rc); rc = -140; goto on_error; } } else { sent = DATA_LEN; rc = pj_sock_send(cs, senddata, &sent, 0); if (rc != PJ_SUCCESS || sent != DATA_LEN) { app_perror("...send error", rc); rc = -145; goto on_error; } } TRACE_(("test", "....recv()")); if (srcaddr) { pj_sockaddr_in addr; int srclen = sizeof(addr); pj_bzero(&addr, sizeof(addr)); received = DATA_LEN; rc = pj_sock_recvfrom(ss, recvdata, &received, 0, &addr, &srclen); if (rc != PJ_SUCCESS || received != DATA_LEN) { app_perror("...recvfrom error", rc); rc = -150; goto on_error; } if (srclen != addrlen) return -151; if (pj_sockaddr_cmp(&addr, srcaddr) != 0) { char srcaddr_str[32], addr_str[32]; strcpy(srcaddr_str, pj_inet_ntoa(srcaddr->sin_addr)); strcpy(addr_str, pj_inet_ntoa(addr.sin_addr)); PJ_LOG(3,("test", "...error: src address mismatch (original=%s, " "recvfrom addr=%s)", srcaddr_str, addr_str)); return -152; } } else { /* Repeat recv() until all data is received. * This applies only for non-UDP of course, since for UDP * we would expect all data to be received in one packet. */ total_received = 0; do { received = DATA_LEN-total_received; rc = pj_sock_recv(ss, recvdata+total_received, &received, 0); if (rc != PJ_SUCCESS) { app_perror("...recv error", rc); rc = -155; goto on_error; } if (received <= 0) { PJ_LOG(3,("", "...error: socket has closed! (received=%d)", received)); rc = -156; goto on_error; } if (received != DATA_LEN-total_received) { if (sock_type != pj_SOCK_STREAM()) { PJ_LOG(3,("", "...error: expecting %u bytes, got %u bytes", DATA_LEN-total_received, received)); rc = -157; goto on_error; } } total_received += received; } while (total_received < DATA_LEN); } TRACE_(("test", "....memcmp()")); if (pj_memcmp(senddata, recvdata, DATA_LEN) != 0) { PJ_LOG(3,("","...error: received data mismatch " "(got:'%s' expecting:'%s'", recvdata, senddata)); rc = -160; goto on_error; } /* * Test send/recv big data. */ TRACE_(("test", "....sendto()")); if (dstaddr) { sent = BIG_DATA_LEN; rc = pj_sock_sendto(cs, bigdata, &sent, 0, dstaddr, addrlen); if (rc != PJ_SUCCESS || sent != BIG_DATA_LEN) { app_perror("...sendto error", rc); rc = -161; goto on_error; } } else { sent = BIG_DATA_LEN; rc = pj_sock_send(cs, bigdata, &sent, 0); if (rc != PJ_SUCCESS || sent != BIG_DATA_LEN) { app_perror("...send error", rc); rc = -165; goto on_error; } } TRACE_(("test", "....recv()")); /* Repeat recv() until all data is received. * This applies only for non-UDP of course, since for UDP * we would expect all data to be received in one packet. */ total_received = 0; do { received = BIG_DATA_LEN-total_received; rc = pj_sock_recv(ss, bigbuffer+total_received, &received, 0); if (rc != PJ_SUCCESS) { app_perror("...recv error", rc); rc = -170; goto on_error; } if (received <= 0) { PJ_LOG(3,("", "...error: socket has closed! (received=%d)", received)); rc = -173; goto on_error; } if (received != BIG_DATA_LEN-total_received) { if (sock_type != pj_SOCK_STREAM()) { PJ_LOG(3,("", "...error: expecting %u bytes, got %u bytes", BIG_DATA_LEN-total_received, received)); rc = -176; goto on_error; } } total_received += received; } while (total_received < BIG_DATA_LEN); TRACE_(("test", "....memcmp()")); if (pj_memcmp(bigdata, bigbuffer, BIG_DATA_LEN) != 0) { PJ_LOG(3,("", "...error: received data has been altered!")); rc = -180; goto on_error; } rc = 0; on_error: return rc; }
/* * pj_ioqueue_send() * * Start asynchronous send() to the descriptor. */ PJ_DEF(pj_status_t) pj_ioqueue_send( pj_ioqueue_key_t *key, pj_ioqueue_op_key_t *op_key, const void *data, pj_ssize_t *length, unsigned flags) { struct write_operation *write_op; pj_status_t status; unsigned retry; pj_ssize_t sent; PJ_ASSERT_RETURN(key && op_key && data && length, PJ_EINVAL); PJ_CHECK_STACK(); /* Check if key is closing. */ if (IS_CLOSING(key)) return PJ_ECANCELLED; /* We can not use PJ_IOQUEUE_ALWAYS_ASYNC for socket write. */ flags &= ~(PJ_IOQUEUE_ALWAYS_ASYNC); /* Fast track: * Try to send data immediately, only if there's no pending write! * Note: * We are speculating that the list is empty here without properly * acquiring ioqueue's mutex first. This is intentional, to maximize * performance via parallelism. * * This should be safe, because: * - by convention, we require caller to make sure that the * key is not unregistered while other threads are invoking * an operation on the same key. * - pj_list_empty() is safe to be invoked by multiple threads, * even when other threads are modifying the list. */ if (pj_list_empty(&key->write_list)) { /* * See if data can be sent immediately. */ sent = *length; status = pj_sock_send(key->fd, data, &sent, flags); if (status == PJ_SUCCESS) { /* Success! */ *length = sent; 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; } } } /* * Schedule asynchronous send. */ write_op = (struct write_operation*)op_key; /* Spin if write_op has pending operation */ for (retry=0; write_op->op != 0 && retry<PENDING_RETRY; ++retry) pj_thread_sleep(0); /* Last chance */ if (write_op->op) { /* Unable to send packet because there is already pending write in the * write_op. We could not put the operation into the write_op * because write_op already contains a pending operation! And * we could not send the packet directly with send() either, * because that will break the order of the packet. So we can * only return error here. * * This could happen for example in multithreads program, * where polling is done by one thread, while other threads are doing * the sending only. If the polling thread runs on lower priority * than the sending thread, then it's possible that the pending * write flag is not cleared in-time because clearing is only done * during polling. * * Aplication should specify multiple write operation keys on * situation like this. */ //pj_assert(!"ioqueue: there is pending operation on this key!"); return PJ_EBUSY; } write_op->op = PJ_IOQUEUE_OP_SEND; write_op->buf = (char*)data; write_op->size = *length; write_op->written = 0; write_op->flags = flags; pj_mutex_lock(key->mutex); pj_list_insert_before(&key->write_list, write_op); ioqueue_add_to_set(key->ioqueue, key, WRITEABLE_EVENT); pj_mutex_unlock(key->mutex); return PJ_EPENDING; }
/* * ioqueue_dispatch_event() * * Report occurence of an event in the key to be processed by the * framework. */ void ioqueue_dispatch_write_event(pj_ioqueue_t *ioqueue, pj_ioqueue_key_t *h) { /* Lock the key. */ pj_mutex_lock(h->mutex); if (IS_CLOSING(h)) { pj_mutex_unlock(h->mutex); return; } #if defined(PJ_HAS_TCP) && PJ_HAS_TCP!=0 if (h->connecting) { /* Completion of connect() operation */ pj_ssize_t bytes_transfered; pj_bool_t has_lock; /* Clear operation. */ h->connecting = 0; ioqueue_remove_from_set(ioqueue, h, WRITEABLE_EVENT); ioqueue_remove_from_set(ioqueue, h, EXCEPTION_EVENT); #if (defined(PJ_HAS_SO_ERROR) && PJ_HAS_SO_ERROR!=0) /* 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; int vallen = sizeof(value); int gs_rc = pj_sock_getsockopt(h->fd, SOL_SOCKET, SO_ERROR, &value, &vallen); if (gs_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. */ bytes_transfered = 0; } else { bytes_transfered = value; } } #elif defined(PJ_WIN32) && PJ_WIN32!=0 bytes_transfered = 0; /* success */ #else /* Excellent information in D.J. Bernstein page: * http://cr.yp.to/docs/connect.html * * Seems like the most portable way of detecting connect() * failure is to call getpeername(). If socket is connected, * getpeername() will return 0. If the socket is not connected, * it will return ENOTCONN, and read(fd, &ch, 1) will produce * the right errno through error slippage. This is a combination * of suggestions from Douglas C. Schmidt and Ken Keys. */ { int gp_rc; struct sockaddr_in addr; socklen_t addrlen = sizeof(addr); gp_rc = getpeername(h->fd, (struct sockaddr*)&addr, &addrlen); bytes_transfered = (gp_rc < 0) ? gp_rc : -gp_rc; } #endif /* 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_connect_complete && !IS_CLOSING(h)) (*h->cb.on_connect_complete)(h, bytes_transfered); /* Unlock if we still hold the lock */ if (has_lock) { pj_mutex_unlock(h->mutex); } /* Done. */ } else #endif /* PJ_HAS_TCP */ if (key_has_pending_write(h)) { /* Socket is writable. */ struct write_operation *write_op; pj_ssize_t sent; pj_status_t send_rc; /* Get the first in the queue. */ write_op = h->write_list.next; /* For datagrams, we can remove the write_op from the list * so that send() can work in parallel. */ if (h->fd_type == pj_SOCK_DGRAM()) { pj_list_erase(write_op); if (pj_list_empty(&h->write_list)) ioqueue_remove_from_set(ioqueue, h, WRITEABLE_EVENT); } /* Send the data. * Unfortunately we must do this while holding key's mutex, thus * preventing parallel write on a single key.. :-(( */ sent = write_op->size - write_op->written; if (write_op->op == PJ_IOQUEUE_OP_SEND) { send_rc = pj_sock_send(h->fd, write_op->buf+write_op->written, &sent, write_op->flags); /* Can't do this. We only clear "op" after we're finished sending * the whole buffer. */ //write_op->op = 0; } else if (write_op->op == PJ_IOQUEUE_OP_SEND_TO) { send_rc = pj_sock_sendto(h->fd, write_op->buf+write_op->written, &sent, write_op->flags, &write_op->rmt_addr, write_op->rmt_addrlen); /* Can't do this. We only clear "op" after we're finished sending * the whole buffer. */ //write_op->op = 0; } else { pj_assert(!"Invalid operation type!"); write_op->op = PJ_IOQUEUE_OP_NONE; send_rc = PJ_EBUG; } if (send_rc == PJ_SUCCESS) { write_op->written += sent; } else { pj_assert(send_rc > 0); write_op->written = -send_rc; } /* Are we finished with this buffer? */ if (send_rc!=PJ_SUCCESS || write_op->written == (pj_ssize_t)write_op->size || h->fd_type == pj_SOCK_DGRAM()) { pj_bool_t has_lock; write_op->op = PJ_IOQUEUE_OP_NONE; if (h->fd_type != pj_SOCK_DGRAM()) { /* Write completion of the whole stream. */ pj_list_erase(write_op); /* Clear operation if there's no more data to send. */ if (pj_list_empty(&h->write_list)) ioqueue_remove_from_set(ioqueue, h, WRITEABLE_EVENT); } /* 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_write_complete && !IS_CLOSING(h)) { (*h->cb.on_write_complete)(h, (pj_ioqueue_op_key_t*)write_op, write_op->written); } if (has_lock) { pj_mutex_unlock(h->mutex); } } else { pj_mutex_unlock(h->mutex); } /* Done. */ } 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); } }
/* * sock_producer_consumer() * * Simple producer-consumer benchmarking. Send loop number of * buf_size size packets as fast as possible. */ static int sock_producer_consumer(int sock_type, unsigned buf_size, unsigned loop, unsigned *p_bandwidth) { pj_sock_t consumer, producer; pj_pool_t *pool; char *outgoing_buffer, *incoming_buffer; pj_timestamp start, stop; unsigned i; pj_highprec_t elapsed, bandwidth; pj_size_t total_received; pj_status_t rc; /* Create pool. */ pool = pj_pool_create(mem, NULL, 4096, 4096, NULL); if (!pool) return -10; /* Create producer-consumer pair. */ rc = app_socketpair(PJ_AF_INET, sock_type, 0, &consumer, &producer); if (rc != PJ_SUCCESS) { app_perror("...error: create socket pair", rc); return -20; } /* Create buffers. */ outgoing_buffer = pj_pool_alloc(pool, buf_size); incoming_buffer = pj_pool_alloc(pool, buf_size); /* Start loop. */ pj_get_timestamp(&start); total_received = 0; for (i=0; i<loop; ++i) { pj_ssize_t sent, part_received, received; pj_time_val delay; sent = buf_size; rc = pj_sock_send(producer, outgoing_buffer, &sent, 0); if (rc != PJ_SUCCESS || sent != (pj_ssize_t)buf_size) { app_perror("...error: send()", rc); return -61; } /* Repeat recv() until all data is part_received. * This applies only for non-UDP of course, since for UDP * we would expect all data to be part_received in one packet. */ received = 0; do { part_received = buf_size-received; rc = pj_sock_recv(consumer, incoming_buffer+received, &part_received, 0); if (rc != PJ_SUCCESS) { app_perror("...recv error", rc); return -70; } if (part_received <= 0) { PJ_LOG(3,("", "...error: socket has closed (part_received=%d)!", part_received)); return -73; } if ((pj_size_t)part_received != buf_size-received) { if (sock_type != PJ_SOCK_STREAM) { PJ_LOG(3,("", "...error: expecting %u bytes, got %u bytes", buf_size-received, part_received)); return -76; } } received += part_received; } while ((pj_size_t)received < buf_size); total_received += received; /* Stop test if it's been runnign for more than 10 secs. */ pj_get_timestamp(&stop); delay = pj_elapsed_time(&start, &stop); if (delay.sec > 10) break; } /* Stop timer. */ pj_get_timestamp(&stop); elapsed = pj_elapsed_usec(&start, &stop); /* bandwidth = total_received * 1000 / elapsed */ bandwidth = total_received; pj_highprec_mul(bandwidth, 1000); pj_highprec_div(bandwidth, elapsed); *p_bandwidth = (pj_uint32_t)bandwidth; /* Close sockets. */ pj_sock_close(consumer); pj_sock_close(producer); /* Done */ pj_pool_release(pool); return 0; }