123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517 |
- /*
- * 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_sockaddr addr;
- 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);
- 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) {
- 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_UNUSED_ARG(delay);
- 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 */
|