activesock.c 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517
  1. /*
  2. * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
  3. * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
  4. *
  5. * This program is free software; you can redistribute it and/or modify
  6. * it under the terms of the GNU General Public License as published by
  7. * the Free Software Foundation; either version 2 of the License, or
  8. * (at your option) any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU General Public License
  16. * along with this program; if not, write to the Free Software
  17. * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  18. */
  19. #include "test.h"
  20. #include <pjlib.h>
  21. /**
  22. * \page page_pjlib_activesock_test Test: Active Socket
  23. *
  24. * This file is <b>pjlib-test/activesock.c</b>
  25. *
  26. * \include pjlib-test/activesock.c
  27. */
  28. #if INCLUDE_ACTIVESOCK_TEST
  29. #define THIS_FILE "activesock.c"
  30. /*******************************************************************
  31. * Simple UDP echo server.
  32. */
  33. struct udp_echo_srv
  34. {
  35. pj_activesock_t *asock;
  36. pj_bool_t echo_enabled;
  37. pj_uint16_t port;
  38. pj_ioqueue_op_key_t send_key;
  39. pj_status_t status;
  40. unsigned rx_cnt;
  41. unsigned rx_err_cnt, tx_err_cnt;
  42. };
  43. static void udp_echo_err(const char *title, pj_status_t status)
  44. {
  45. char errmsg[PJ_ERR_MSG_SIZE];
  46. pj_strerror(status, errmsg, sizeof(errmsg));
  47. PJ_LOG(3,(THIS_FILE, " error: %s: %s", title, errmsg));
  48. }
  49. static pj_bool_t udp_echo_srv_on_data_recvfrom(pj_activesock_t *asock,
  50. void *data,
  51. pj_size_t size,
  52. const pj_sockaddr_t *src_addr,
  53. int addr_len,
  54. pj_status_t status)
  55. {
  56. struct udp_echo_srv *srv;
  57. pj_ssize_t sent;
  58. srv = (struct udp_echo_srv*) pj_activesock_get_user_data(asock);
  59. if (status != PJ_SUCCESS) {
  60. srv->status = status;
  61. srv->rx_err_cnt++;
  62. udp_echo_err("recvfrom() callback", status);
  63. return PJ_TRUE;
  64. }
  65. srv->rx_cnt++;
  66. /* Send back if echo is enabled */
  67. if (srv->echo_enabled) {
  68. sent = size;
  69. srv->status = pj_activesock_sendto(asock, &srv->send_key, data,
  70. &sent, 0,
  71. src_addr, addr_len);
  72. if (srv->status != PJ_SUCCESS) {
  73. srv->tx_err_cnt++;
  74. udp_echo_err("sendto()", status);
  75. }
  76. }
  77. return PJ_TRUE;
  78. }
  79. static pj_status_t udp_echo_srv_create(pj_pool_t *pool,
  80. pj_ioqueue_t *ioqueue,
  81. pj_bool_t enable_echo,
  82. struct udp_echo_srv **p_srv)
  83. {
  84. struct udp_echo_srv *srv;
  85. pj_sockaddr addr;
  86. pj_activesock_cb activesock_cb;
  87. pj_status_t status;
  88. srv = PJ_POOL_ZALLOC_T(pool, struct udp_echo_srv);
  89. srv->echo_enabled = enable_echo;
  90. pj_sockaddr_in_init(&addr.ipv4, NULL, 0);
  91. pj_bzero(&activesock_cb, sizeof(activesock_cb));
  92. activesock_cb.on_data_recvfrom = &udp_echo_srv_on_data_recvfrom;
  93. status = pj_activesock_create_udp(pool, &addr, NULL, ioqueue, &activesock_cb,
  94. srv, &srv->asock, &addr);
  95. if (status != PJ_SUCCESS) {
  96. udp_echo_err("pj_activesock_create()", status);
  97. return status;
  98. }
  99. srv->port = pj_ntohs(addr.ipv4.sin_port);
  100. pj_ioqueue_op_key_init(&srv->send_key, sizeof(srv->send_key));
  101. status = pj_activesock_start_recvfrom(srv->asock, pool, 32, 0);
  102. if (status != PJ_SUCCESS) {
  103. pj_activesock_close(srv->asock);
  104. udp_echo_err("pj_activesock_start_recvfrom()", status);
  105. return status;
  106. }
  107. *p_srv = srv;
  108. return PJ_SUCCESS;
  109. }
  110. static void udp_echo_srv_destroy(struct udp_echo_srv *srv)
  111. {
  112. pj_activesock_close(srv->asock);
  113. }
  114. /*******************************************************************
  115. * UDP ping pong test (send packet back and forth between two UDP echo
  116. * servers.
  117. */
  118. static int udp_ping_pong_test(void)
  119. {
  120. pj_ioqueue_t *ioqueue = NULL;
  121. pj_pool_t *pool = NULL;
  122. struct udp_echo_srv *srv1=NULL, *srv2=NULL;
  123. pj_bool_t need_send = PJ_TRUE;
  124. unsigned data = 0;
  125. int count, ret;
  126. pj_status_t status;
  127. pool = pj_pool_create(mem, "pingpong", 512, 512, NULL);
  128. if (!pool)
  129. return -10;
  130. status = pj_ioqueue_create(pool, 4, &ioqueue);
  131. if (status != PJ_SUCCESS) {
  132. ret = -20;
  133. udp_echo_err("pj_ioqueue_create()", status);
  134. goto on_return;
  135. }
  136. status = udp_echo_srv_create(pool, ioqueue, PJ_TRUE, &srv1);
  137. if (status != PJ_SUCCESS) {
  138. ret = -30;
  139. goto on_return;
  140. }
  141. status = udp_echo_srv_create(pool, ioqueue, PJ_TRUE, &srv2);
  142. if (status != PJ_SUCCESS) {
  143. ret = -40;
  144. goto on_return;
  145. }
  146. /* initiate the first send */
  147. for (count=0; count<1000; ++count) {
  148. unsigned last_rx1, last_rx2;
  149. unsigned i;
  150. if (need_send) {
  151. pj_str_t loopback;
  152. pj_sockaddr_in addr;
  153. pj_ssize_t sent;
  154. ++data;
  155. sent = sizeof(data);
  156. loopback = pj_str("127.0.0.1");
  157. pj_sockaddr_in_init(&addr, &loopback, srv2->port);
  158. status = pj_activesock_sendto(srv1->asock, &srv1->send_key,
  159. &data, &sent, 0,
  160. &addr, sizeof(addr));
  161. if (status != PJ_SUCCESS && status != PJ_EPENDING) {
  162. ret = -50;
  163. udp_echo_err("sendto()", status);
  164. goto on_return;
  165. }
  166. need_send = PJ_FALSE;
  167. }
  168. last_rx1 = srv1->rx_cnt;
  169. last_rx2 = srv2->rx_cnt;
  170. for (i=0; i<10 && last_rx1 == srv1->rx_cnt && last_rx2 == srv2->rx_cnt; ++i) {
  171. pj_time_val delay = {0, 10};
  172. #ifdef PJ_SYMBIAN
  173. PJ_UNUSED_ARG(delay);
  174. pj_symbianos_poll(-1, 100);
  175. #else
  176. pj_ioqueue_poll(ioqueue, &delay);
  177. #endif
  178. }
  179. if (srv1->rx_err_cnt+srv1->tx_err_cnt != 0 ||
  180. srv2->rx_err_cnt+srv2->tx_err_cnt != 0)
  181. {
  182. /* Got error */
  183. ret = -60;
  184. goto on_return;
  185. }
  186. if (last_rx1 == srv1->rx_cnt && last_rx2 == srv2->rx_cnt) {
  187. /* Packet lost */
  188. ret = -70;
  189. udp_echo_err("packets have been lost", PJ_ETIMEDOUT);
  190. goto on_return;
  191. }
  192. }
  193. ret = 0;
  194. on_return:
  195. if (srv2)
  196. udp_echo_srv_destroy(srv2);
  197. if (srv1)
  198. udp_echo_srv_destroy(srv1);
  199. if (ioqueue)
  200. pj_ioqueue_destroy(ioqueue);
  201. if (pool)
  202. pj_pool_release(pool);
  203. return ret;
  204. }
  205. #define SIGNATURE 0xdeadbeef
  206. struct tcp_pkt
  207. {
  208. pj_uint32_t signature;
  209. pj_uint32_t seq;
  210. char fill[513];
  211. };
  212. struct tcp_state
  213. {
  214. pj_bool_t err;
  215. pj_bool_t sent;
  216. pj_uint32_t next_recv_seq;
  217. pj_uint8_t pkt[600];
  218. };
  219. struct send_key
  220. {
  221. pj_ioqueue_op_key_t op_key;
  222. };
  223. static pj_bool_t tcp_on_data_read(pj_activesock_t *asock,
  224. void *data,
  225. pj_size_t size,
  226. pj_status_t status,
  227. pj_size_t *remainder)
  228. {
  229. struct tcp_state *st = (struct tcp_state*) pj_activesock_get_user_data(asock);
  230. char *next = (char*) data;
  231. if (status != PJ_SUCCESS && status != PJ_EPENDING) {
  232. PJ_LOG(1,("", " err: status=%d", status));
  233. st->err = PJ_TRUE;
  234. return PJ_FALSE;
  235. }
  236. while (size >= sizeof(struct tcp_pkt)) {
  237. struct tcp_pkt *tcp_pkt = (struct tcp_pkt*) next;
  238. if (tcp_pkt->signature != SIGNATURE) {
  239. PJ_LOG(1,("", " err: invalid signature at seq=%d",
  240. st->next_recv_seq));
  241. st->err = PJ_TRUE;
  242. return PJ_FALSE;
  243. }
  244. if (tcp_pkt->seq != st->next_recv_seq) {
  245. PJ_LOG(1,("", " err: wrong sequence"));
  246. st->err = PJ_TRUE;
  247. return PJ_FALSE;
  248. }
  249. st->next_recv_seq++;
  250. next += sizeof(struct tcp_pkt);
  251. size -= sizeof(struct tcp_pkt);
  252. }
  253. if (size) {
  254. pj_memmove(data, next, size);
  255. *remainder = size;
  256. }
  257. return PJ_TRUE;
  258. }
  259. static pj_bool_t tcp_on_data_sent(pj_activesock_t *asock,
  260. pj_ioqueue_op_key_t *op_key,
  261. pj_ssize_t sent)
  262. {
  263. struct tcp_state *st=(struct tcp_state*)pj_activesock_get_user_data(asock);
  264. PJ_UNUSED_ARG(op_key);
  265. st->sent = 1;
  266. if (sent < 1) {
  267. st->err = PJ_TRUE;
  268. return PJ_FALSE;
  269. }
  270. return PJ_TRUE;
  271. }
  272. static int tcp_perf_test(void)
  273. {
  274. enum { COUNT=10000 };
  275. pj_pool_t *pool = NULL;
  276. pj_ioqueue_t *ioqueue = NULL;
  277. pj_sock_t sock1=PJ_INVALID_SOCKET, sock2=PJ_INVALID_SOCKET;
  278. pj_activesock_t *asock1 = NULL, *asock2 = NULL;
  279. pj_activesock_cb cb;
  280. struct tcp_state *state1, *state2;
  281. unsigned i;
  282. pj_status_t status;
  283. pool = pj_pool_create(mem, "tcpperf", 256, 256, NULL);
  284. status = app_socketpair(pj_AF_INET(), pj_SOCK_STREAM(), 0, &sock1,
  285. &sock2);
  286. if (status != PJ_SUCCESS) {
  287. status = -100;
  288. goto on_return;
  289. }
  290. status = pj_ioqueue_create(pool, 4, &ioqueue);
  291. if (status != PJ_SUCCESS) {
  292. status = -110;
  293. goto on_return;
  294. }
  295. pj_bzero(&cb, sizeof(cb));
  296. cb.on_data_read = &tcp_on_data_read;
  297. cb.on_data_sent = &tcp_on_data_sent;
  298. state1 = PJ_POOL_ZALLOC_T(pool, struct tcp_state);
  299. status = pj_activesock_create(pool, sock1, pj_SOCK_STREAM(), NULL, ioqueue,
  300. &cb, state1, &asock1);
  301. if (status != PJ_SUCCESS) {
  302. status = -120;
  303. goto on_return;
  304. }
  305. state2 = PJ_POOL_ZALLOC_T(pool, struct tcp_state);
  306. status = pj_activesock_create(pool, sock2, pj_SOCK_STREAM(), NULL, ioqueue,
  307. &cb, state2, &asock2);
  308. if (status != PJ_SUCCESS) {
  309. status = -130;
  310. goto on_return;
  311. }
  312. status = pj_activesock_start_read(asock1, pool, 1000, 0);
  313. if (status != PJ_SUCCESS) {
  314. status = -140;
  315. goto on_return;
  316. }
  317. /* Send packet as quickly as possible */
  318. for (i=0; i<COUNT && !state1->err && !state2->err; ++i) {
  319. struct tcp_pkt *pkt;
  320. struct send_key send_key[2], *op_key;
  321. pj_ssize_t len;
  322. pkt = (struct tcp_pkt*)state2->pkt;
  323. pkt->signature = SIGNATURE;
  324. pkt->seq = i;
  325. pj_memset(pkt->fill, 'a', sizeof(pkt->fill));
  326. op_key = &send_key[i%2];
  327. pj_ioqueue_op_key_init(&op_key->op_key, sizeof(*op_key));
  328. state2->sent = PJ_FALSE;
  329. len = sizeof(*pkt);
  330. status = pj_activesock_send(asock2, &op_key->op_key, pkt, &len, 0);
  331. if (status == PJ_EPENDING) {
  332. do {
  333. #if PJ_SYMBIAN
  334. pj_symbianos_poll(-1, -1);
  335. #else
  336. pj_ioqueue_poll(ioqueue, NULL);
  337. #endif
  338. } while (!state2->sent);
  339. } else {
  340. #if PJ_SYMBIAN
  341. /* The Symbian socket always returns PJ_SUCCESS for TCP send,
  342. * eventhough the remote end hasn't received the data yet.
  343. * If we continue sending, eventually send() will block,
  344. * possibly because the send buffer is full. So we need to
  345. * poll the ioqueue periodically, to let receiver gets the
  346. * data.
  347. */
  348. pj_symbianos_poll(-1, 0);
  349. #endif
  350. if (status != PJ_SUCCESS) {
  351. PJ_LOG(1,("", " err: send status=%d", status));
  352. status = -180;
  353. break;
  354. } else if (status == PJ_SUCCESS) {
  355. if (len != sizeof(*pkt)) {
  356. PJ_LOG(1,("", " err: shouldn't report partial sent"));
  357. status = -190;
  358. break;
  359. }
  360. }
  361. }
  362. #ifndef PJ_SYMBIAN
  363. for (;;) {
  364. pj_time_val timeout = {0, 10};
  365. if (pj_ioqueue_poll(ioqueue, &timeout) < 1)
  366. break;
  367. }
  368. #endif
  369. }
  370. /* Wait until everything has been sent/received */
  371. if (state1->next_recv_seq < COUNT) {
  372. #ifdef PJ_SYMBIAN
  373. while (pj_symbianos_poll(-1, 1000) == PJ_TRUE)
  374. ;
  375. #else
  376. pj_time_val delay = {0, 100};
  377. while (pj_ioqueue_poll(ioqueue, &delay) > 0)
  378. ;
  379. #endif
  380. }
  381. if (status == PJ_EPENDING)
  382. status = PJ_SUCCESS;
  383. if (status != 0)
  384. goto on_return;
  385. if (state1->err) {
  386. status = -183;
  387. goto on_return;
  388. }
  389. if (state2->err) {
  390. status = -186;
  391. goto on_return;
  392. }
  393. if (state1->next_recv_seq != COUNT) {
  394. PJ_LOG(3,("", " err: only %u packets received, expecting %u",
  395. state1->next_recv_seq, COUNT));
  396. status = -195;
  397. goto on_return;
  398. }
  399. on_return:
  400. if (asock2)
  401. pj_activesock_close(asock2);
  402. if (asock1)
  403. pj_activesock_close(asock1);
  404. if (ioqueue)
  405. pj_ioqueue_destroy(ioqueue);
  406. if (pool)
  407. pj_pool_release(pool);
  408. return status;
  409. }
  410. int activesock_test(void)
  411. {
  412. int ret;
  413. PJ_LOG(3,("", "..udp ping/pong test"));
  414. ret = udp_ping_pong_test();
  415. if (ret != 0)
  416. return ret;
  417. PJ_LOG(3,("", "..tcp perf test"));
  418. ret = tcp_perf_test();
  419. if (ret != 0)
  420. return ret;
  421. return 0;
  422. }
  423. #else /* INCLUDE_ACTIVESOCK_TEST */
  424. /* To prevent warning about "translation unit is empty"
  425. * when this test is disabled.
  426. */
  427. int dummy_active_sock_test;
  428. #endif /* INCLUDE_ACTIVESOCK_TEST */