diff options
author | Benny Prijono <bennylp@teluu.com> | 2006-06-23 15:04:54 +0000 |
---|---|---|
committer | Benny Prijono <bennylp@teluu.com> | 2006-06-23 15:04:54 +0000 |
commit | e90d549a310e36299d349eef746e966f9172273f (patch) | |
tree | 9b2af4dc963aa5dba236417f7253c47a8f1e001f /pjsip | |
parent | daf2b65313488296bb1a563d6189a0e6774304af (diff) |
Renamed pjsip_transport_unregister() to pjsip_transport_destroy(), also initial implementation of TCP transport
git-svn-id: http://svn.pjsip.org/repos/pjproject/trunk@550 74dad513-b988-da41-8d7b-12977e46ad98
Diffstat (limited to 'pjsip')
-rw-r--r-- | pjsip/build/pjsip_core.dsp | 9 | ||||
-rw-r--r-- | pjsip/include/pjsip/sip_transport.h | 117 | ||||
-rw-r--r-- | pjsip/include/pjsip/sip_transport_tcp.h | 62 | ||||
-rw-r--r-- | pjsip/include/pjsip/sip_transport_udp.h | 2 | ||||
-rw-r--r-- | pjsip/src/pjsip/sip_transport.c | 59 | ||||
-rw-r--r-- | pjsip/src/pjsip/sip_transport_tcp.c | 220 | ||||
-rw-r--r-- | pjsip/src/pjsip/sip_transport_udp.c | 16 | ||||
-rw-r--r-- | pjsip/src/test-pjsip/transport_udp_test.c | 2 |
8 files changed, 463 insertions, 24 deletions
diff --git a/pjsip/build/pjsip_core.dsp b/pjsip/build/pjsip_core.dsp index 3d33540b..8345242f 100644 --- a/pjsip/build/pjsip_core.dsp +++ b/pjsip/build/pjsip_core.dsp @@ -146,6 +146,11 @@ SOURCE=..\src\pjsip\sip_transport_loop.c # End Source File
# Begin Source File
+SOURCE=..\src\pjsip\sip_transport_tcp.c
+# PROP Exclude_From_Build 1
+# End Source File
+# Begin Source File
+
SOURCE=..\src\pjsip\sip_transport_udp.c
# End Source File
# End Group
@@ -278,6 +283,10 @@ SOURCE=..\include\pjsip\sip_transport_loop.h # End Source File
# Begin Source File
+SOURCE=..\include\pjsip\sip_transport_tcp.h
+# End Source File
+# Begin Source File
+
SOURCE=..\include\pjsip\sip_transport_udp.h
# End Source File
# End Group
diff --git a/pjsip/include/pjsip/sip_transport.h b/pjsip/include/pjsip/sip_transport.h index 2adcea01..e09583db 100644 --- a/pjsip/include/pjsip/sip_transport.h +++ b/pjsip/include/pjsip/sip_transport.h @@ -492,7 +492,8 @@ struct pjsip_transport pj_pool_t *pool; /**< Pool used by transport. */ pj_atomic_t *ref_cnt; /**< Reference counter. */ pj_lock_t *lock; /**< Lock object. */ - int tracing; /**< Tracing enabled? */ + pj_bool_t tracing; /**< Tracing enabled? */ + pj_bool_t is_shutdown; /**< Being shutdown? */ /** Key for indexing this transport in hash table. */ struct { @@ -549,7 +550,28 @@ struct pjsip_transport pj_ssize_t sent_bytes)); /** - * Destroy this transport. + * Instruct the transport to initiate graceful shutdown procedure. + * After all objects release their reference to this transport, + * the transport will be deleted. + * + * Note that application MUST use #pjsip_transport_shutdown() instead. + * + * @param transport The transport. + * + * @return PJ_SUCCESS on success. + */ + pj_status_t (*do_shutdown)(pjsip_transport *transport); + + /** + * Forcefully destroy this transport regardless whether there are + * objects that currently use this transport. This function should only + * be called by transport manager or other internal objects (such as the + * transport itself) who know what they're doing. Application should use + * #pjsip_transport_shutdown() instead. + * + * @param transport The transport. + * + * @return PJ_SUCCESS on success. */ pj_status_t (*destroy)(pjsip_transport *transport); @@ -560,32 +582,93 @@ struct pjsip_transport /** - * Register a transport. + * Register a transport instance to the transport manager. This function + * is normally called by the transport instance when it is created + * by application. + * + * @param mgr The transport manager. + * @param tp The new transport to be registered. + * + * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjsip_transport_register( pjsip_tpmgr *mgr, pjsip_transport *tp ); /** - * Unregister transport. This will eventually call the transport to - * destroy itself. + * Start graceful shutdown procedure for this transport. After graceful + * shutdown has been initiated, no new reference can be obtained for + * the transport. However, existing objects that currently uses the + * transport may still use this transport to send and receive packets. + * + * After all objects release their reference to this transport, + * the transport will be destroyed immediately. + * + * @param tp The transport. + * + * @return PJ_SUCCESS on success. + */ +PJ_DECL(pj_status_t) pjsip_transport_shutdown(pjsip_transport *tp); + +/** + * Destroy a transport when there is no object currently uses the transport. + * This function is normally called internally by transport manager or the + * transport itself. Application should use #pjsip_transport_shutdown() + * instead. + * + * @param tp The transport instance. + * + * @return PJ_SUCCESS on success or the appropriate error code. + * Some of possible errors are PJSIP_EBUSY if the + * transport's reference counter is not zero. */ -PJ_DECL(pj_status_t) pjsip_transport_unregister( pjsip_tpmgr *mgr, - pjsip_transport *tp); +PJ_DECL(pj_status_t) pjsip_transport_destroy( pjsip_transport *tp); /** - * Add ref. + * Add reference counter to the specified transport. Any objects that wishes + * to keep the reference of the transport MUST increment the transport's + * reference counter to prevent it from being destroyed. + * + * @param tp The transport instance. + * + * @return PJ_SUCCESS on success or the appropriate error code. */ PJ_DECL(pj_status_t) pjsip_transport_add_ref( pjsip_transport *tp ); /** - * Dec ref. + * Decrement reference counter of the specified transport. When an object no + * longer want to keep the reference to the transport, it must decrement the + * reference counter. When the reference counter of the transport reaches + * zero, the transport manager will start the idle timer to destroy the + * transport if no objects acquire the reference counter during the idle + * interval. + * + * @param tp The transport instance. + * + * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjsip_transport_dec_ref( pjsip_transport *tp ); /** - * Call for incoming message. + * This function is called by transport instances to report an incoming + * packet to the transport manager. The transport manager then would try to + * parse all SIP messages in the packet, and for each parsed SIP message, it + * would report the message to the SIP endpoint (#pjsip_endpoint). + * + * @param mgr The transport manager instance. + * @param rdata The receive data buffer containing the packet. The + * transport MUST fully initialize tp_info and pkt_info + * member of the rdata. + * + * @return The number of bytes successfully processed from the + * packet. If the transport is datagram oriented, the + * value will be equal to the size of the packet. For + * stream oriented transport (e.g. TCP, TLS), the value + * returned may be less than the packet size, if + * partial message is received. The transport then MUST + * keep the remainder part and report it again to + * this function once more data/packet is received. */ PJ_DECL(pj_ssize_t) pjsip_tpmgr_receive_packet(pjsip_tpmgr *mgr, pjsip_rx_data *rdata); @@ -597,14 +680,20 @@ PJ_DECL(pj_ssize_t) pjsip_tpmgr_receive_packet(pjsip_tpmgr *mgr, * *****************************************************************************/ - -/** - * Transport factory. +/* + * Forward declaration for transport factory (since it is referenced by + * the transport factory itself). */ typedef struct pjsip_tpfactory pjsip_tpfactory; + /** - * Transport factory. + * A transport factory is normally used for connection oriented transports + * (such as TCP or TLS) to create instances of transports. It registers + * a new transport type to the transport manager, and the transport manager + * would ask the factory to create a transport instance when it received + * command from application to send a SIP message using the specified + * transport type. */ struct pjsip_tpfactory { diff --git a/pjsip/include/pjsip/sip_transport_tcp.h b/pjsip/include/pjsip/sip_transport_tcp.h new file mode 100644 index 00000000..317d7772 --- /dev/null +++ b/pjsip/include/pjsip/sip_transport_tcp.h @@ -0,0 +1,62 @@ +/* $Id$ */ +/* + * Copyright (C) 2003-2006 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 + */ +#ifndef __PJSIP_TRANSPORT_TCP_H__ +#define __PJSIP_TRANSPORT_TCP_H__ + +/** + * @file sip_transport_tcp.h + * @brief SIP TCP Transport. + */ + +#include <pjsip/sip_transport.h> + +PJ_BEGIN_DECL + +/** + * @defgroup PJSIP_TRANSPORT_TCP TCP Transport + * @ingroup PJSIP_TRANSPORT + * @brief API to create and register TCP transport. + * @{ + * The functions below are used to create TCP transport and register + * the transport to the framework. + */ + +/** + * Create, register, and start TCP transport. + * + * @param endpt The SIP endpoint. + * @param local Local address to bind. + * @param async_cnt Number of simultaneous async operations. + * + * @return PJ_SUCCESS when the transport has been successfully + * started and registered to transport manager, or + * the appropriate error code. + */ +PJ_DECL(pj_status_t) pjsip_tcp_transport_start(pjsip_endpoint *endpt, + const pj_sockaddr_in *local, + unsigned async_cnt); + + +PJ_END_DECL + +/** + * @} + */ + +#endif /* __PJSIP_TRANSPORT_TCP_H__ */ diff --git a/pjsip/include/pjsip/sip_transport_udp.h b/pjsip/include/pjsip/sip_transport_udp.h index 7cf8f553..77e76657 100644 --- a/pjsip/include/pjsip/sip_transport_udp.h +++ b/pjsip/include/pjsip/sip_transport_udp.h @@ -29,7 +29,7 @@ PJ_BEGIN_DECL /** - * @defgroup PJSIP_TRANSPORT_UDP UDP transport + * @defgroup PJSIP_TRANSPORT_UDP UDP Transport * @ingroup PJSIP_TRANSPORT * @brief API to create and register UDP transport. * @{ diff --git a/pjsip/src/pjsip/sip_transport.c b/pjsip/src/pjsip/sip_transport.c index 10d11007..9583e049 100644 --- a/pjsip/src/pjsip/sip_transport.c +++ b/pjsip/src/pjsip/sip_transport.c @@ -516,7 +516,7 @@ static void transport_idle_callback(pj_timer_heap_t *timer_heap, PJ_UNUSED_ARG(timer_heap); entry->id = PJ_FALSE; - pjsip_transport_unregister(tp->tpmgr, tp); + pjsip_transport_destroy(tp); } /* @@ -554,7 +554,18 @@ PJ_DEF(pj_status_t) pjsip_transport_dec_ref( pjsip_transport *tp ) pj_lock_acquire(tp->tpmgr->lock); /* Verify again. */ if (pj_atomic_get(tp->ref_cnt) == 0) { - pj_time_val delay = { PJSIP_TRANSPORT_IDLE_TIME, 0 }; + pj_time_val delay; + + /* If transport is in graceful shutdown, then this is the + * last user who uses the transport. Schedule to destroy the + * transport immediately. Otherwise schedule idle timer. + */ + if (tp->is_shutdown) { + delay.sec = delay.msec = 0; + } else { + delay.sec = PJSIP_TRANSPORT_IDLE_TIME; + delay.msec = 0; + } pj_assert(tp->idle_timer.id == 0); tp->idle_timer.id = PJ_TRUE; @@ -623,17 +634,53 @@ static pj_status_t destroy_transport( pjsip_tpmgr *mgr, return tp->destroy(tp); } + +/* + * Start graceful shutdown procedure for this transport. + */ +PJ_DEF(pj_status_t) pjsip_transport_shutdown(pjsip_transport *tp) +{ + pjsip_tpmgr *mgr; + pj_status_t status; + + pj_lock_acquire(tp->lock); + + mgr = tp->tpmgr; + pj_lock_acquire(mgr->lock); + + /* Do nothing if transport is being shutdown already */ + if (tp->is_shutdown) { + pj_lock_release(tp->lock); + pj_lock_release(mgr->lock); + return PJ_SUCCESS; + } + + status = PJ_SUCCESS; + + /* Instruct transport to shutdown itself */ + if (tp->do_shutdown) + status = tp->do_shutdown(tp); + + if (status == PJ_SUCCESS) + tp->is_shutdown = PJ_TRUE; + + pj_lock_release(tp->lock); + pj_lock_release(mgr->lock); + + return status; +} + + /** * Unregister transport. */ -PJ_DEF(pj_status_t) pjsip_transport_unregister( pjsip_tpmgr *mgr, - pjsip_transport *tp) +PJ_DEF(pj_status_t) pjsip_transport_destroy( pjsip_transport *tp) { /* Must have no user. */ PJ_ASSERT_RETURN(pj_atomic_get(tp->ref_cnt) == 0, PJSIP_EBUSY); /* Destroy. */ - return destroy_transport(mgr, tp); + return destroy_transport(tp->tpmgr, tp); } @@ -1029,7 +1076,7 @@ PJ_DEF(pj_status_t) pjsip_tpmgr_acquire_transport(pjsip_tpmgr *mgr, } } - if (transport != NULL) { + if (transport!=NULL && !transport->is_shutdown) { /* * Transport found! */ diff --git a/pjsip/src/pjsip/sip_transport_tcp.c b/pjsip/src/pjsip/sip_transport_tcp.c new file mode 100644 index 00000000..8072ddd7 --- /dev/null +++ b/pjsip/src/pjsip/sip_transport_tcp.c @@ -0,0 +1,220 @@ +/* $Id$ */ +/* + * Copyright (C) 2003-2006 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 <pjsip/sip_transport_tcp.h> +#include <pjsip/sip_endpoint.h> +#include <pj/assert.h> +#include <pj/errno.h> +#include <pj/ioqueue.h> +#include <pj/lock.h> +#include <pj/pool.h> +#include <pj/string.h> + + +#define MAX_ASYNC_CNT 16 +#define POOL_INIT 4000 +#define POOL_INC 4000 + + +struct tcp_listener; + +struct pending_accept +{ + pj_ioqueue_op_key_t op_key; + struct tcp_listener *listener; + pj_sock_t new_sock; + int addr_len; + pj_sockaddr_in local_addr; + pj_sockaddr_in remote_addr; +}; + +struct tcp_listener +{ + pjsip_tpfactory factory; + pj_bool_t active; + pjsip_tpmgr *tpmgr; + pj_sock_t sock; + pj_ioqueue_key_t *key; + unsigned async_cnt; + struct pending_accept accept_op[MAX_ASYNC_CNT]; +}; + + +struct tcp_transport +{ + pjsip_transport base; + pj_sock_t sock; +}; + + +/* + * This callback is called when #pj_ioqueue_accept completes. + */ +static void on_accept_complete( pj_ioqueue_key_t *key, + pj_ioqueue_op_key_t *op_key, + pj_sock_t sock, + pj_status_t status); + +static pj_status_t destroy_listener(struct tcp_listener *listener); + +static pj_status_t create_transport(pjsip_tpfactory *factory, + pjsip_tpmgr *mgr, + pjsip_endpoint *endpt, + const pj_sockaddr *rem_addr, + int addr_len, + pjsip_transport **transport); + +PJ_DEF(pj_status_t) pjsip_tcp_transport_start( pjsip_endpoint *endpt, + const pj_sockaddr_in *local, + unsigned async_cnt) +{ + pj_pool_t *pool; + struct tcp_listener *listener; + pj_ioqueue_callback listener_cb; + unsigned i; + pj_status_t status; + + /* Sanity check */ + PJ_ASSERT_RETURN(endpt && local && async_cnt, PJ_EINVAL); + + + pool = pjsip_endpt_create_pool(endpt, "tcplis", POOL_INIT, POOL_INC); + PJ_ASSERT_RETURN(pool, PJ_ENOMEM); + + + listener = pj_pool_zalloc(pool, sizeof(struct tcp_listener)); + listener->factory.pool = pool; + listener->factory.type = PJSIP_TRANSPORT_TCP; + pj_ansi_strcpy(listener->factory.type_name, "tcp"); + listener->factory.flag = + pjsip_transport_get_flag_from_type(PJSIP_TRANSPORT_TCP); + listener->sock = PJ_INVALID_SOCKET; + + status = pj_lock_create_recursive_mutex(pool, "tcplis", + &listener->factory.lock); + if (status != PJ_SUCCESS) + goto on_error; + + + /* Create and bind socket */ + status = pj_sock_socket(PJ_AF_INET, PJ_SOCK_STREAM, 0, &listener->sock); + if (status != PJ_SUCCESS) + goto on_error; + + pj_memcpy(&listener->factory.local_addr, local, sizeof(pj_sockaddr_in)); + status = pj_sock_bind(listener->sock, local, sizeof(*local)); + if (status != PJ_SUCCESS) + goto on_error; + + /* Register socket to ioqeuue */ + pj_memset(&listener_cb, 0, sizeof(listener_cb)); + listener_cb.on_accept_complete = &on_accept_complete; + status = pj_ioqueue_register_sock(pool, pjsip_endpt_get_ioqueue(endpt), + listener->sock, listener, + &listener_cb, &listener->key); + if (status != PJ_SUCCESS) + goto on_error; + + /* Start pending accept() operation */ + if (async_cnt > MAX_ASYNC_CNT) async_cnt = MAX_ASYNC_CNT; + listener->async_cnt = async_cnt; + + for (i=0; i<async_cnt; ++i) { + pj_ioqueue_op_key_init(&listener->accept_op[i].op_key, + sizeof(listener->accept_op[i].op_key)); + listener->accept_op[i].listener = listener; + + status = pj_ioqueue_accept(listener->key, + &listener->accept_op[i].op_key, + &listener->accept_op[i].new_sock, + &listener->accept_op[i].local_addr, + &listener->accept_op[i].remote_addr, + &listener->accept_op[i].addr_len); + if (status != PJ_SUCCESS) + goto on_error; + } + + /* Register to transport manager */ + listener->tpmgr = pjsip_endpt_get_tpmgr(endpt); + status = pjsip_tpmgr_register_tpfactory(listener->tpmgr, + &listener->factory); + if (status != PJ_SUCCESS) + goto on_error; + + /* Done! */ + listener->active = PJ_TRUE; + + return PJ_SUCCESS; + +on_error: + destroy_listener(listener); + return status; +} + + + + +static pj_status_t destroy_listener(struct tcp_listener *listener) +{ + if (listener->active) { + pjsip_tpmgr_unregister_tpfactory(listener->tpmgr, &listener->factory); + listener->active = PJ_FALSE; + } + + if (listener->key) { + pj_ioqueue_unregister(listener->key); + listener->key = NULL; + listener->sock = PJ_INVALID_SOCKET; + } + + if (listener->sock != PJ_INVALID_SOCKET) { + pj_sock_close(listener->sock); + listener->sock = PJ_INVALID_SOCKET; + } + + if (listener->factory.lock) { + pj_lock_destroy(listener->factory.lock); + listener->factory.lock = NULL; + } + + if (listener->factory.pool) { + pj_pool_release(listener->factory.pool); + listener->factory.pool = NULL; + } + + return PJ_SUCCESS; +} + + +static void on_accept_complete( pj_ioqueue_key_t *key, + pj_ioqueue_op_key_t *op_key, + pj_sock_t sock, + pj_status_t status) +{ +} + + +static pj_status_t create_transport(pjsip_tpfactory *factory, + pjsip_tpmgr *mgr, + pjsip_endpoint *endpt, + const pj_sockaddr *rem_addr, + int addr_len, + pjsip_transport **transport) +{ +} + diff --git a/pjsip/src/pjsip/sip_transport_udp.c b/pjsip/src/pjsip/sip_transport_udp.c index 6312756c..1c72dca7 100644 --- a/pjsip/src/pjsip/sip_transport_udp.c +++ b/pjsip/src/pjsip/sip_transport_udp.c @@ -377,6 +377,17 @@ static pj_status_t udp_destroy( pjsip_transport *transport ) /* + * udp_shutdown() + * + * Start graceful UDP shutdown. + */ +static pj_status_t udp_shutdown(pjsip_transport *transport) +{ + return pjsip_transport_dec_ref(transport); +} + + +/* * pjsip_udp_transport_attach() * * Attach UDP socket and start transport. @@ -505,6 +516,7 @@ PJ_DEF(pj_status_t) pjsip_udp_transport_attach( pjsip_endpoint *endpt, /* Set functions. */ tp->base.send_msg = &udp_send_msg; + tp->base.do_shutdown = &udp_shutdown; tp->base.destroy = &udp_destroy; /* This is a permanent transport, so we initialize the ref count @@ -530,7 +542,7 @@ PJ_DEF(pj_status_t) pjsip_udp_transport_attach( pjsip_endpoint *endpt, PJSIP_POOL_RDATA_INC); if (!rdata_pool) { pj_atomic_set(tp->base.ref_cnt, 0); - pjsip_transport_unregister(tp->base.tpmgr, &tp->base); + pjsip_transport_destroy(&tp->base); return PJ_ENOMEM; } @@ -556,7 +568,7 @@ PJ_DEF(pj_status_t) pjsip_udp_transport_attach( pjsip_endpoint *endpt, size); } else if (status != PJ_EPENDING) { /* Error! */ - pjsip_transport_unregister(tp->base.tpmgr, &tp->base); + pjsip_transport_destroy(&tp->base); return status; } } diff --git a/pjsip/src/test-pjsip/transport_udp_test.c b/pjsip/src/test-pjsip/transport_udp_test.c index cf5f0ca5..4db0a7ca 100644 --- a/pjsip/src/test-pjsip/transport_udp_test.c +++ b/pjsip/src/test-pjsip/transport_udp_test.c @@ -102,7 +102,7 @@ int transport_udp_test(void) pjsip_transport_dec_ref(udp_tp); /* Force destroy this transport. */ - status = pjsip_transport_unregister( pjsip_endpt_get_tpmgr(endpt), udp_tp); + status = pjsip_transport_destroy(udp_tp); if (status != PJ_SUCCESS) return -90; |