Ejemplo n.º 1
0
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);

} 
Ejemplo n.º 2
0
    //
    // 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;
    }
Ejemplo n.º 3
0
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], &param, &sndlen, 0);
}
Ejemplo n.º 4
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;
}
Ejemplo n.º 5
0
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;
}
Ejemplo n.º 6
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;
}
Ejemplo n.º 7
0
/*
 * 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;
}
Ejemplo n.º 8
0
/*
 * 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);
    }
}
Ejemplo n.º 9
0
/*
 * 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;
}