diff options
author | David M. Lee <dlee@digium.com> | 2013-01-07 14:24:28 -0600 |
---|---|---|
committer | David M. Lee <dlee@digium.com> | 2013-01-07 14:24:28 -0600 |
commit | f3ab456a17af1c89a6e3be4d20c5944853df1cb0 (patch) | |
tree | d00e1a332cd038a6d906a1ea0ac91e1a4458e617 /pjlib/src/pjlib-test/activesock.c |
Import pjproject-2.0.1
Diffstat (limited to 'pjlib/src/pjlib-test/activesock.c')
-rw-r--r-- | pjlib/src/pjlib-test/activesock.c | 521 |
1 files changed, 521 insertions, 0 deletions
diff --git a/pjlib/src/pjlib-test/activesock.c b/pjlib/src/pjlib-test/activesock.c new file mode 100644 index 0000000..399cc29 --- /dev/null +++ b/pjlib/src/pjlib-test/activesock.c @@ -0,0 +1,521 @@ +/* $Id: activesock.c 3553 2011-05-05 06:14:19Z nanang $ */ +/* + * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) + * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include "test.h" +#include <pjlib.h> + +/** + * \page page_pjlib_activesock_test Test: Active Socket + * + * This file is <b>pjlib-test/activesock.c</b> + * + * \include pjlib-test/activesock.c + */ + +#if INCLUDE_ACTIVESOCK_TEST + +#define THIS_FILE "activesock.c" + + +/******************************************************************* + * Simple UDP echo server. + */ +struct udp_echo_srv +{ + pj_activesock_t *asock; + pj_bool_t echo_enabled; + pj_uint16_t port; + pj_ioqueue_op_key_t send_key; + pj_status_t status; + unsigned rx_cnt; + unsigned rx_err_cnt, tx_err_cnt; +}; + +static void udp_echo_err(const char *title, pj_status_t status) +{ + char errmsg[PJ_ERR_MSG_SIZE]; + + pj_strerror(status, errmsg, sizeof(errmsg)); + PJ_LOG(3,(THIS_FILE, " error: %s: %s", title, errmsg)); +} + +static pj_bool_t udp_echo_srv_on_data_recvfrom(pj_activesock_t *asock, + void *data, + pj_size_t size, + const pj_sockaddr_t *src_addr, + int addr_len, + pj_status_t status) +{ + struct udp_echo_srv *srv; + pj_ssize_t sent; + + + srv = (struct udp_echo_srv*) pj_activesock_get_user_data(asock); + + if (status != PJ_SUCCESS) { + srv->status = status; + srv->rx_err_cnt++; + udp_echo_err("recvfrom() callback", status); + return PJ_TRUE; + } + + srv->rx_cnt++; + + /* Send back if echo is enabled */ + if (srv->echo_enabled) { + sent = size; + srv->status = pj_activesock_sendto(asock, &srv->send_key, data, + &sent, 0, + src_addr, addr_len); + if (srv->status != PJ_SUCCESS) { + srv->tx_err_cnt++; + udp_echo_err("sendto()", status); + } + } + + return PJ_TRUE; +} + + +static pj_status_t udp_echo_srv_create(pj_pool_t *pool, + pj_ioqueue_t *ioqueue, + pj_bool_t enable_echo, + struct udp_echo_srv **p_srv) +{ + struct udp_echo_srv *srv; + pj_sock_t sock_fd = PJ_INVALID_SOCKET; + pj_sockaddr addr; + int addr_len; + pj_activesock_cb activesock_cb; + pj_status_t status; + + srv = PJ_POOL_ZALLOC_T(pool, struct udp_echo_srv); + srv->echo_enabled = enable_echo; + + pj_sockaddr_in_init(&addr.ipv4, NULL, 0); + addr_len = sizeof(addr); + + pj_bzero(&activesock_cb, sizeof(activesock_cb)); + activesock_cb.on_data_recvfrom = &udp_echo_srv_on_data_recvfrom; + + status = pj_activesock_create_udp(pool, &addr, NULL, ioqueue, &activesock_cb, + srv, &srv->asock, &addr); + if (status != PJ_SUCCESS) { + pj_sock_close(sock_fd); + udp_echo_err("pj_activesock_create()", status); + return status; + } + + srv->port = pj_ntohs(addr.ipv4.sin_port); + + pj_ioqueue_op_key_init(&srv->send_key, sizeof(srv->send_key)); + + status = pj_activesock_start_recvfrom(srv->asock, pool, 32, 0); + if (status != PJ_SUCCESS) { + pj_activesock_close(srv->asock); + udp_echo_err("pj_activesock_start_recvfrom()", status); + return status; + } + + + *p_srv = srv; + return PJ_SUCCESS; +} + +static void udp_echo_srv_destroy(struct udp_echo_srv *srv) +{ + pj_activesock_close(srv->asock); +} + +/******************************************************************* + * UDP ping pong test (send packet back and forth between two UDP echo + * servers. + */ +static int udp_ping_pong_test(void) +{ + pj_ioqueue_t *ioqueue = NULL; + pj_pool_t *pool = NULL; + struct udp_echo_srv *srv1=NULL, *srv2=NULL; + pj_bool_t need_send = PJ_TRUE; + unsigned data = 0; + int count, ret; + pj_status_t status; + + pool = pj_pool_create(mem, "pingpong", 512, 512, NULL); + if (!pool) + return -10; + + status = pj_ioqueue_create(pool, 4, &ioqueue); + if (status != PJ_SUCCESS) { + ret = -20; + udp_echo_err("pj_ioqueue_create()", status); + goto on_return; + } + + status = udp_echo_srv_create(pool, ioqueue, PJ_TRUE, &srv1); + if (status != PJ_SUCCESS) { + ret = -30; + goto on_return; + } + + status = udp_echo_srv_create(pool, ioqueue, PJ_TRUE, &srv2); + if (status != PJ_SUCCESS) { + ret = -40; + goto on_return; + } + + /* initiate the first send */ + for (count=0; count<1000; ++count) { + unsigned last_rx1, last_rx2; + unsigned i; + + if (need_send) { + pj_str_t loopback; + pj_sockaddr_in addr; + pj_ssize_t sent; + + ++data; + + sent = sizeof(data); + loopback = pj_str("127.0.0.1"); + pj_sockaddr_in_init(&addr, &loopback, srv2->port); + status = pj_activesock_sendto(srv1->asock, &srv1->send_key, + &data, &sent, 0, + &addr, sizeof(addr)); + if (status != PJ_SUCCESS && status != PJ_EPENDING) { + ret = -50; + udp_echo_err("sendto()", status); + goto on_return; + } + + need_send = PJ_FALSE; + } + + last_rx1 = srv1->rx_cnt; + last_rx2 = srv2->rx_cnt; + + for (i=0; i<10 && last_rx1 == srv1->rx_cnt && last_rx2 == srv2->rx_cnt; ++i) { + pj_time_val delay = {0, 10}; +#ifdef PJ_SYMBIAN + pj_symbianos_poll(-1, 100); +#else + pj_ioqueue_poll(ioqueue, &delay); +#endif + } + + if (srv1->rx_err_cnt+srv1->tx_err_cnt != 0 || + srv2->rx_err_cnt+srv2->tx_err_cnt != 0) + { + /* Got error */ + ret = -60; + goto on_return; + } + + if (last_rx1 == srv1->rx_cnt && last_rx2 == srv2->rx_cnt) { + /* Packet lost */ + ret = -70; + udp_echo_err("packets have been lost", PJ_ETIMEDOUT); + goto on_return; + } + } + + ret = 0; + +on_return: + if (srv2) + udp_echo_srv_destroy(srv2); + if (srv1) + udp_echo_srv_destroy(srv1); + if (ioqueue) + pj_ioqueue_destroy(ioqueue); + if (pool) + pj_pool_release(pool); + + return ret; +} + + + +#define SIGNATURE 0xdeadbeef +struct tcp_pkt +{ + pj_uint32_t signature; + pj_uint32_t seq; + char fill[513]; +}; + +struct tcp_state +{ + pj_bool_t err; + pj_bool_t sent; + pj_uint32_t next_recv_seq; + pj_uint8_t pkt[600]; +}; + +struct send_key +{ + pj_ioqueue_op_key_t op_key; +}; + + +static pj_bool_t tcp_on_data_read(pj_activesock_t *asock, + void *data, + pj_size_t size, + pj_status_t status, + pj_size_t *remainder) +{ + struct tcp_state *st = (struct tcp_state*) pj_activesock_get_user_data(asock); + char *next = (char*) data; + + if (status != PJ_SUCCESS && status != PJ_EPENDING) { + PJ_LOG(1,("", " err: status=%d", status)); + st->err = PJ_TRUE; + return PJ_FALSE; + } + + while (size >= sizeof(struct tcp_pkt)) { + struct tcp_pkt *tcp_pkt = (struct tcp_pkt*) next; + + if (tcp_pkt->signature != SIGNATURE) { + PJ_LOG(1,("", " err: invalid signature at seq=%d", + st->next_recv_seq)); + st->err = PJ_TRUE; + return PJ_FALSE; + } + if (tcp_pkt->seq != st->next_recv_seq) { + PJ_LOG(1,("", " err: wrong sequence")); + st->err = PJ_TRUE; + return PJ_FALSE; + } + + st->next_recv_seq++; + next += sizeof(struct tcp_pkt); + size -= sizeof(struct tcp_pkt); + } + + if (size) { + pj_memmove(data, next, size); + *remainder = size; + } + + return PJ_TRUE; +} + +static pj_bool_t tcp_on_data_sent(pj_activesock_t *asock, + pj_ioqueue_op_key_t *op_key, + pj_ssize_t sent) +{ + struct tcp_state *st=(struct tcp_state*)pj_activesock_get_user_data(asock); + + PJ_UNUSED_ARG(op_key); + + st->sent = 1; + + if (sent < 1) { + st->err = PJ_TRUE; + return PJ_FALSE; + } + + return PJ_TRUE; +} + +static int tcp_perf_test(void) +{ + enum { COUNT=10000 }; + pj_pool_t *pool = NULL; + pj_ioqueue_t *ioqueue = NULL; + pj_sock_t sock1=PJ_INVALID_SOCKET, sock2=PJ_INVALID_SOCKET; + pj_activesock_t *asock1 = NULL, *asock2 = NULL; + pj_activesock_cb cb; + struct tcp_state *state1, *state2; + unsigned i; + pj_status_t status; + + pool = pj_pool_create(mem, "tcpperf", 256, 256, NULL); + + status = app_socketpair(pj_AF_INET(), pj_SOCK_STREAM(), 0, &sock1, + &sock2); + if (status != PJ_SUCCESS) { + status = -100; + goto on_return; + } + + status = pj_ioqueue_create(pool, 4, &ioqueue); + if (status != PJ_SUCCESS) { + status = -110; + goto on_return; + } + + pj_bzero(&cb, sizeof(cb)); + cb.on_data_read = &tcp_on_data_read; + cb.on_data_sent = &tcp_on_data_sent; + + state1 = PJ_POOL_ZALLOC_T(pool, struct tcp_state); + status = pj_activesock_create(pool, sock1, pj_SOCK_STREAM(), NULL, ioqueue, + &cb, state1, &asock1); + if (status != PJ_SUCCESS) { + status = -120; + goto on_return; + } + + state2 = PJ_POOL_ZALLOC_T(pool, struct tcp_state); + status = pj_activesock_create(pool, sock2, pj_SOCK_STREAM(), NULL, ioqueue, + &cb, state2, &asock2); + if (status != PJ_SUCCESS) { + status = -130; + goto on_return; + } + + status = pj_activesock_start_read(asock1, pool, 1000, 0); + if (status != PJ_SUCCESS) { + status = -140; + goto on_return; + } + + /* Send packet as quickly as possible */ + for (i=0; i<COUNT && !state1->err && !state2->err; ++i) { + struct tcp_pkt *pkt; + struct send_key send_key[2], *op_key; + pj_ssize_t len; + + pkt = (struct tcp_pkt*)state2->pkt; + pkt->signature = SIGNATURE; + pkt->seq = i; + pj_memset(pkt->fill, 'a', sizeof(pkt->fill)); + + op_key = &send_key[i%2]; + pj_ioqueue_op_key_init(&op_key->op_key, sizeof(*op_key)); + + state2->sent = PJ_FALSE; + len = sizeof(*pkt); + status = pj_activesock_send(asock2, &op_key->op_key, pkt, &len, 0); + if (status == PJ_EPENDING) { + do { +#if PJ_SYMBIAN + pj_symbianos_poll(-1, -1); +#else + pj_ioqueue_poll(ioqueue, NULL); +#endif + } while (!state2->sent); + } else { +#if PJ_SYMBIAN + /* The Symbian socket always returns PJ_SUCCESS for TCP send, + * eventhough the remote end hasn't received the data yet. + * If we continue sending, eventually send() will block, + * possibly because the send buffer is full. So we need to + * poll the ioqueue periodically, to let receiver gets the + * data. + */ + pj_symbianos_poll(-1, 0); +#endif + if (status != PJ_SUCCESS) { + PJ_LOG(1,("", " err: send status=%d", status)); + status = -180; + break; + } else if (status == PJ_SUCCESS) { + if (len != sizeof(*pkt)) { + PJ_LOG(1,("", " err: shouldn't report partial sent")); + status = -190; + break; + } + } + } + +#ifndef PJ_SYMBIAN + for (;;) { + pj_time_val timeout = {0, 10}; + if (pj_ioqueue_poll(ioqueue, &timeout) < 1) + break; + } +#endif + + } + + /* Wait until everything has been sent/received */ + if (state1->next_recv_seq < COUNT) { +#ifdef PJ_SYMBIAN + while (pj_symbianos_poll(-1, 1000) == PJ_TRUE) + ; +#else + pj_time_val delay = {0, 100}; + while (pj_ioqueue_poll(ioqueue, &delay) > 0) + ; +#endif + } + + if (status == PJ_EPENDING) + status = PJ_SUCCESS; + + if (status != 0) + goto on_return; + + if (state1->err) { + status = -183; + goto on_return; + } + if (state2->err) { + status = -186; + goto on_return; + } + if (state1->next_recv_seq != COUNT) { + PJ_LOG(3,("", " err: only %u packets received, expecting %u", + state1->next_recv_seq, COUNT)); + status = -195; + goto on_return; + } + +on_return: + if (asock2) + pj_activesock_close(asock2); + if (asock1) + pj_activesock_close(asock1); + if (ioqueue) + pj_ioqueue_destroy(ioqueue); + if (pool) + pj_pool_release(pool); + + return status; +} + + + +int activesock_test(void) +{ + int ret; + + PJ_LOG(3,("", "..udp ping/pong test")); + ret = udp_ping_pong_test(); + if (ret != 0) + return ret; + + PJ_LOG(3,("", "..tcp perf test")); + ret = tcp_perf_test(); + if (ret != 0) + return ret; + + return 0; +} + +#else /* INCLUDE_ACTIVESOCK_TEST */ +/* To prevent warning about "translation unit is empty" + * when this test is disabled. + */ +int dummy_active_sock_test; +#endif /* INCLUDE_ACTIVESOCK_TEST */ + |