Index: /pjproject/trunk/pjsip/build/pjsip_core.dsp
===================================================================
--- /pjproject/trunk/pjsip/build/pjsip_core.dsp	(revision 549)
+++ /pjproject/trunk/pjsip/build/pjsip_core.dsp	(revision 550)
@@ -147,4 +147,9 @@
 # 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
@@ -279,4 +284,8 @@
 # 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
Index: /pjproject/trunk/pjsip/include/pjsip/sip_transport.h
===================================================================
--- /pjproject/trunk/pjsip/include/pjsip/sip_transport.h	(revision 549)
+++ /pjproject/trunk/pjsip/include/pjsip/sip_transport.h	(revision 550)
@@ -493,5 +493,6 @@
     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. */
@@ -550,5 +551,26 @@
 
     /**
-     * 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);
@@ -561,5 +583,12 @@
 
 /**
- * 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,
@@ -568,17 +597,54 @@
 
 /**
- * Unregister transport. This will eventually call the transport to
- * destroy itself.
- */
-PJ_DECL(pj_status_t) pjsip_transport_unregister( pjsip_tpmgr *mgr,
-						 pjsip_transport *tp);
-
-/**
- * Add ref.
+ * 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_destroy( pjsip_transport *tp);
+
+/**
+ * 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 );
@@ -586,5 +652,22 @@
 
 /**
- * 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,
@@ -598,12 +681,18 @@
  *****************************************************************************/
 
-
-/**
- * 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
Index: /pjproject/trunk/pjsip/include/pjsip/sip_transport_tcp.h
===================================================================
--- /pjproject/trunk/pjsip/include/pjsip/sip_transport_tcp.h	(revision 550)
+++ /pjproject/trunk/pjsip/include/pjsip/sip_transport_tcp.h	(revision 550)
@@ -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__ */
Index: /pjproject/trunk/pjsip/include/pjsip/sip_transport_udp.h
===================================================================
--- /pjproject/trunk/pjsip/include/pjsip/sip_transport_udp.h	(revision 549)
+++ /pjproject/trunk/pjsip/include/pjsip/sip_transport_udp.h	(revision 550)
@@ -30,5 +30,5 @@
 
 /**
- * @defgroup PJSIP_TRANSPORT_UDP UDP transport
+ * @defgroup PJSIP_TRANSPORT_UDP UDP Transport
  * @ingroup PJSIP_TRANSPORT
  * @brief API to create and register UDP transport.
Index: /pjproject/trunk/pjsip/src/pjsip/sip_transport.c
===================================================================
--- /pjproject/trunk/pjsip/src/pjsip/sip_transport.c	(revision 549)
+++ /pjproject/trunk/pjsip/src/pjsip/sip_transport.c	(revision 550)
@@ -517,5 +517,5 @@
 
     entry->id = PJ_FALSE;
-    pjsip_transport_unregister(tp->tpmgr, tp);
+    pjsip_transport_destroy(tp);
 }
 
@@ -555,5 +555,16 @@
 	/* 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);
@@ -624,9 +635,45 @@
 }
 
+
+/*
+ * 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. */
@@ -634,5 +681,5 @@
 
     /* Destroy. */
-    return destroy_transport(mgr, tp);
+    return destroy_transport(tp->tpmgr, tp);
 }
 
@@ -1030,5 +1077,5 @@
     }
     
-    if (transport != NULL) {
+    if (transport!=NULL && !transport->is_shutdown) {
 	/*
 	 * Transport found!
Index: /pjproject/trunk/pjsip/src/pjsip/sip_transport_tcp.c
===================================================================
--- /pjproject/trunk/pjsip/src/pjsip/sip_transport_tcp.c	(revision 550)
+++ /pjproject/trunk/pjsip/src/pjsip/sip_transport_tcp.c	(revision 550)
@@ -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)
+{
+}
+
Index: /pjproject/trunk/pjsip/src/pjsip/sip_transport_udp.c
===================================================================
--- /pjproject/trunk/pjsip/src/pjsip/sip_transport_udp.c	(revision 549)
+++ /pjproject/trunk/pjsip/src/pjsip/sip_transport_udp.c	(revision 550)
@@ -378,4 +378,15 @@
 
 /*
+ * 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()
  *
@@ -506,4 +517,5 @@
     /* Set functions. */
     tp->base.send_msg = &udp_send_msg;
+    tp->base.do_shutdown = &udp_shutdown;
     tp->base.destroy = &udp_destroy;
 
@@ -531,5 +543,5 @@
 	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;
 	}
@@ -557,5 +569,5 @@
 	} else if (status != PJ_EPENDING) {
 	    /* Error! */
-	    pjsip_transport_unregister(tp->base.tpmgr, &tp->base);
+	    pjsip_transport_destroy(&tp->base);
 	    return status;
 	}
Index: /pjproject/trunk/pjsip/src/test-pjsip/transport_udp_test.c
===================================================================
--- /pjproject/trunk/pjsip/src/test-pjsip/transport_udp_test.c	(revision 549)
+++ /pjproject/trunk/pjsip/src/test-pjsip/transport_udp_test.c	(revision 550)
@@ -103,5 +103,5 @@
 
     /* 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;
