1
/* $Id: activesock.c 4359 2013-02-21 11:18:36Z bennylp $ */
3
* Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
4
* Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
6
* This program is free software; you can redistribute it and/or modify
7
* it under the terms of the GNU General Public License as published by
8
* the Free Software Foundation; either version 2 of the License, or
9
* (at your option) any later version.
11
* This program is distributed in the hope that it will be useful,
12
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
* GNU General Public License for more details.
16
* You should have received a copy of the GNU General Public License
17
* along with this program; if not, write to the Free Software
18
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20
#include <pj/activesock.h>
21
#include <pj/compat/socket.h>
22
#include <pj/assert.h>
27
#include <pj/string.h>
29
#if defined(PJ_IPHONE_OS_HAS_MULTITASKING_SUPPORT) && \
30
PJ_IPHONE_OS_HAS_MULTITASKING_SUPPORT!=0
31
# include <CFNetwork/CFNetwork.h>
33
static pj_bool_t ios_bg_support = PJ_TRUE;
36
#define PJ_ACTIVESOCK_MAX_LOOP 50
55
pj_ioqueue_op_key_t op_key;
65
pj_ioqueue_op_key_t op_key;
79
struct pj_activesock_t
81
pj_ioqueue_key_t *key;
82
pj_bool_t stream_oriented;
84
pj_ioqueue_t *ioqueue;
90
#if defined(PJ_IPHONE_OS_HAS_MULTITASKING_SUPPORT) && \
91
PJ_IPHONE_OS_HAS_MULTITASKING_SUPPORT!=0
94
CFReadStreamRef readStream;
100
struct send_data send_data;
102
struct read_op *read_op;
103
pj_uint32_t read_flags;
104
enum read_type read_type;
106
struct accept_op *accept_op;
110
static void ioqueue_on_read_complete(pj_ioqueue_key_t *key,
111
pj_ioqueue_op_key_t *op_key,
112
pj_ssize_t bytes_read);
113
static void ioqueue_on_write_complete(pj_ioqueue_key_t *key,
114
pj_ioqueue_op_key_t *op_key,
115
pj_ssize_t bytes_sent);
117
static void ioqueue_on_accept_complete(pj_ioqueue_key_t *key,
118
pj_ioqueue_op_key_t *op_key,
121
static void ioqueue_on_connect_complete(pj_ioqueue_key_t *key,
125
PJ_DEF(void) pj_activesock_cfg_default(pj_activesock_cfg *cfg)
127
pj_bzero(cfg, sizeof(*cfg));
129
cfg->concurrency = -1;
130
cfg->whole_data = PJ_TRUE;
133
#if defined(PJ_IPHONE_OS_HAS_MULTITASKING_SUPPORT) && \
134
PJ_IPHONE_OS_HAS_MULTITASKING_SUPPORT!=0
135
static void activesock_destroy_iphone_os_stream(pj_activesock_t *asock)
137
if (asock->readStream) {
138
CFReadStreamClose(asock->readStream);
139
CFRelease(asock->readStream);
140
asock->readStream = NULL;
144
static void activesock_create_iphone_os_stream(pj_activesock_t *asock)
146
if (ios_bg_support && asock->bg_setting && asock->stream_oriented) {
147
activesock_destroy_iphone_os_stream(asock);
149
CFStreamCreatePairWithSocket(kCFAllocatorDefault, asock->sock,
150
&asock->readStream, NULL);
152
if (!asock->readStream ||
153
CFReadStreamSetProperty(asock->readStream,
154
kCFStreamNetworkServiceType,
155
kCFStreamNetworkServiceTypeVoIP)
157
CFReadStreamOpen(asock->readStream) != TRUE)
159
PJ_LOG(2,("", "Failed to configure TCP transport for VoIP "
160
"usage. Background mode will not be supported."));
162
activesock_destroy_iphone_os_stream(asock);
168
PJ_DEF(void) pj_activesock_set_iphone_os_bg(pj_activesock_t *asock,
171
asock->bg_setting = val;
172
if (asock->bg_setting)
173
activesock_create_iphone_os_stream(asock);
175
activesock_destroy_iphone_os_stream(asock);
178
PJ_DEF(void) pj_activesock_enable_iphone_os_bg(pj_bool_t val)
180
ios_bg_support = val;
184
PJ_DEF(pj_status_t) pj_activesock_create( pj_pool_t *pool,
187
const pj_activesock_cfg *opt,
188
pj_ioqueue_t *ioqueue,
189
const pj_activesock_cb *cb,
191
pj_activesock_t **p_asock)
193
pj_activesock_t *asock;
194
pj_ioqueue_callback ioq_cb;
197
PJ_ASSERT_RETURN(pool && ioqueue && cb && p_asock, PJ_EINVAL);
198
PJ_ASSERT_RETURN(sock!=0 && sock!=PJ_INVALID_SOCKET, PJ_EINVAL);
199
PJ_ASSERT_RETURN(sock_type==pj_SOCK_STREAM() ||
200
sock_type==pj_SOCK_DGRAM(), PJ_EINVAL);
201
PJ_ASSERT_RETURN(!opt || opt->async_cnt >= 1, PJ_EINVAL);
203
asock = PJ_POOL_ZALLOC_T(pool, pj_activesock_t);
204
asock->ioqueue = ioqueue;
205
asock->stream_oriented = (sock_type == pj_SOCK_STREAM());
206
asock->async_count = (opt? opt->async_cnt : 1);
207
asock->whole_data = (opt? opt->whole_data : 1);
208
asock->max_loop = PJ_ACTIVESOCK_MAX_LOOP;
209
asock->user_data = user_data;
210
pj_memcpy(&asock->cb, cb, sizeof(*cb));
212
pj_bzero(&ioq_cb, sizeof(ioq_cb));
213
ioq_cb.on_read_complete = &ioqueue_on_read_complete;
214
ioq_cb.on_write_complete = &ioqueue_on_write_complete;
216
ioq_cb.on_connect_complete = &ioqueue_on_connect_complete;
217
ioq_cb.on_accept_complete = &ioqueue_on_accept_complete;
220
status = pj_ioqueue_register_sock2(pool, ioqueue, sock,
221
(opt? opt->grp_lock : NULL),
222
asock, &ioq_cb, &asock->key);
223
if (status != PJ_SUCCESS) {
224
pj_activesock_close(asock);
228
if (asock->whole_data) {
229
/* Must disable concurrency otherwise there is a race condition */
230
pj_ioqueue_set_concurrency(asock->key, 0);
231
} else if (opt && opt->concurrency >= 0) {
232
pj_ioqueue_set_concurrency(asock->key, opt->concurrency);
235
#if defined(PJ_IPHONE_OS_HAS_MULTITASKING_SUPPORT) && \
236
PJ_IPHONE_OS_HAS_MULTITASKING_SUPPORT!=0
238
asock->bg_setting = PJ_ACTIVESOCK_TCP_IPHONE_OS_BG;
246
PJ_DEF(pj_status_t) pj_activesock_create_udp( pj_pool_t *pool,
247
const pj_sockaddr *addr,
248
const pj_activesock_cfg *opt,
249
pj_ioqueue_t *ioqueue,
250
const pj_activesock_cb *cb,
252
pj_activesock_t **p_asock,
253
pj_sockaddr *bound_addr)
256
pj_sockaddr default_addr;
260
pj_sockaddr_init(pj_AF_INET(), &default_addr, NULL, 0);
261
addr = &default_addr;
264
status = pj_sock_socket(addr->addr.sa_family, pj_SOCK_DGRAM(), 0,
266
if (status != PJ_SUCCESS) {
270
status = pj_sock_bind(sock_fd, addr, pj_sockaddr_get_len(addr));
271
if (status != PJ_SUCCESS) {
272
pj_sock_close(sock_fd);
276
status = pj_activesock_create(pool, sock_fd, pj_SOCK_DGRAM(), opt,
277
ioqueue, cb, user_data, p_asock);
278
if (status != PJ_SUCCESS) {
279
pj_sock_close(sock_fd);
284
int addr_len = sizeof(*bound_addr);
285
status = pj_sock_getsockname(sock_fd, bound_addr, &addr_len);
286
if (status != PJ_SUCCESS) {
287
pj_activesock_close(*p_asock);
295
PJ_DEF(pj_status_t) pj_activesock_close(pj_activesock_t *asock)
297
PJ_ASSERT_RETURN(asock, PJ_EINVAL);
298
asock->shutdown = SHUT_RX | SHUT_TX;
300
#if defined(PJ_IPHONE_OS_HAS_MULTITASKING_SUPPORT) && \
301
PJ_IPHONE_OS_HAS_MULTITASKING_SUPPORT!=0
302
activesock_destroy_iphone_os_stream(asock);
305
pj_ioqueue_unregister(asock->key);
312
PJ_DEF(pj_status_t) pj_activesock_set_user_data( pj_activesock_t *asock,
315
PJ_ASSERT_RETURN(asock, PJ_EINVAL);
316
asock->user_data = user_data;
321
PJ_DEF(void*) pj_activesock_get_user_data(pj_activesock_t *asock)
323
PJ_ASSERT_RETURN(asock, NULL);
324
return asock->user_data;
328
PJ_DEF(pj_status_t) pj_activesock_start_read(pj_activesock_t *asock,
336
PJ_ASSERT_RETURN(asock && pool && buff_size, PJ_EINVAL);
338
readbuf = (void**) pj_pool_calloc(pool, asock->async_count,
341
for (i=0; i<asock->async_count; ++i) {
342
readbuf[i] = pj_pool_alloc(pool, buff_size);
345
return pj_activesock_start_read2(asock, pool, buff_size, readbuf, flags);
349
PJ_DEF(pj_status_t) pj_activesock_start_read2( pj_activesock_t *asock,
358
PJ_ASSERT_RETURN(asock && pool && buff_size, PJ_EINVAL);
359
PJ_ASSERT_RETURN(asock->read_type == TYPE_NONE, PJ_EINVALIDOP);
360
PJ_ASSERT_RETURN(asock->read_op == NULL, PJ_EINVALIDOP);
362
asock->read_op = (struct read_op*)
363
pj_pool_calloc(pool, asock->async_count,
364
sizeof(struct read_op));
365
asock->read_type = TYPE_RECV;
366
asock->read_flags = flags;
368
for (i=0; i<asock->async_count; ++i) {
369
struct read_op *r = &asock->read_op[i];
370
pj_ssize_t size_to_read;
372
r->pkt = (pj_uint8_t*)readbuf[i];
373
r->max_size = size_to_read = buff_size;
375
status = pj_ioqueue_recv(asock->key, &r->op_key, r->pkt, &size_to_read,
376
PJ_IOQUEUE_ALWAYS_ASYNC | flags);
377
PJ_ASSERT_RETURN(status != PJ_SUCCESS, PJ_EBUG);
379
if (status != PJ_EPENDING)
387
PJ_DEF(pj_status_t) pj_activesock_start_recvfrom(pj_activesock_t *asock,
395
PJ_ASSERT_RETURN(asock && pool && buff_size, PJ_EINVAL);
397
readbuf = (void**) pj_pool_calloc(pool, asock->async_count,
400
for (i=0; i<asock->async_count; ++i) {
401
readbuf[i] = pj_pool_alloc(pool, buff_size);
404
return pj_activesock_start_recvfrom2(asock, pool, buff_size,
409
PJ_DEF(pj_status_t) pj_activesock_start_recvfrom2( pj_activesock_t *asock,
418
PJ_ASSERT_RETURN(asock && pool && buff_size, PJ_EINVAL);
419
PJ_ASSERT_RETURN(asock->read_type == TYPE_NONE, PJ_EINVALIDOP);
421
asock->read_op = (struct read_op*)
422
pj_pool_calloc(pool, asock->async_count,
423
sizeof(struct read_op));
424
asock->read_type = TYPE_RECV_FROM;
425
asock->read_flags = flags;
427
for (i=0; i<asock->async_count; ++i) {
428
struct read_op *r = &asock->read_op[i];
429
pj_ssize_t size_to_read;
431
r->pkt = (pj_uint8_t*) readbuf[i];
432
r->max_size = size_to_read = buff_size;
433
r->src_addr_len = sizeof(r->src_addr);
435
status = pj_ioqueue_recvfrom(asock->key, &r->op_key, r->pkt,
437
PJ_IOQUEUE_ALWAYS_ASYNC | flags,
438
&r->src_addr, &r->src_addr_len);
439
PJ_ASSERT_RETURN(status != PJ_SUCCESS, PJ_EBUG);
441
if (status != PJ_EPENDING)
449
static void ioqueue_on_read_complete(pj_ioqueue_key_t *key,
450
pj_ioqueue_op_key_t *op_key,
451
pj_ssize_t bytes_read)
453
pj_activesock_t *asock;
454
struct read_op *r = (struct read_op*)op_key;
458
asock = (pj_activesock_t*) pj_ioqueue_get_user_data(key);
460
/* Ignore if we've been shutdown */
461
if (asock->shutdown & SHUT_RX)
467
if (bytes_read > 0) {
469
* We've got new data.
474
/* Append this new data to existing data. If socket is stream
475
* oriented, user might have left some data in the buffer.
476
* Otherwise if socket is datagram there will be nothing in
477
* existing packet hence the packet will contain only the new
480
r->size += bytes_read;
482
/* Set default remainder to zero */
485
/* And return value to TRUE */
488
/* Notify callback */
489
if (asock->read_type == TYPE_RECV && asock->cb.on_data_read) {
490
ret = (*asock->cb.on_data_read)(asock, r->pkt, r->size,
491
PJ_SUCCESS, &remainder);
492
} else if (asock->read_type == TYPE_RECV_FROM &&
493
asock->cb.on_data_recvfrom)
495
ret = (*asock->cb.on_data_recvfrom)(asock, r->pkt, r->size,
501
/* If callback returns false, we have been destroyed! */
505
/* Only stream oriented socket may leave data in the packet */
506
if (asock->stream_oriented) {
512
} else if (bytes_read <= 0 &&
513
-bytes_read != PJ_STATUS_FROM_OS(OSERR_EWOULDBLOCK) &&
514
-bytes_read != PJ_STATUS_FROM_OS(OSERR_EINPROGRESS) &&
515
(asock->stream_oriented ||
516
-bytes_read != PJ_STATUS_FROM_OS(OSERR_ECONNRESET)))
521
if (bytes_read == 0) {
522
/* For stream/connection oriented socket, this means the
523
* connection has been closed. For datagram sockets, it means
524
* we've received datagram with zero length.
526
if (asock->stream_oriented)
531
/* This means we've got an error. If this is stream/connection
532
* oriented, it means connection has been closed. For datagram
533
* sockets, it means we've got some error (e.g. EWOULDBLOCK).
535
status = -bytes_read;
538
/* Set default remainder to zero */
541
/* And return value to TRUE */
544
/* Notify callback */
545
if (asock->read_type == TYPE_RECV && asock->cb.on_data_read) {
546
/* For connection oriented socket, we still need to report
547
* the remainder data (if any) to the user to let user do
548
* processing with the remainder data before it closes the
550
* If there is no remainder data, set the packet to NULL.
553
/* Shouldn't set the packet to NULL, as there may be active
554
* socket user, such as SSL socket, that needs to have access
555
* to the read buffer packet.
557
//ret = (*asock->cb.on_data_read)(asock, (r->size? r->pkt:NULL),
558
// r->size, status, &remainder);
559
ret = (*asock->cb.on_data_read)(asock, r->pkt, r->size,
562
} else if (asock->read_type == TYPE_RECV_FROM &&
563
asock->cb.on_data_recvfrom)
565
/* This would always be datagram oriented hence there's
566
* nothing in the packet. We can't be sure if there will be
567
* anything useful in the source_addr, so just put NULL
570
/* In some scenarios, status may be PJ_SUCCESS. The upper
571
* layer application may not expect the callback to be called
572
* with successful status and NULL data, so lets not call the
573
* callback if the status is PJ_SUCCESS.
575
if (status != PJ_SUCCESS ) {
576
ret = (*asock->cb.on_data_recvfrom)(asock, NULL, 0,
581
/* If callback returns false, we have been destroyed! */
585
/* Also stop further read if we've been shutdown */
586
if (asock->shutdown & SHUT_RX)
589
/* Only stream oriented socket may leave data in the packet */
590
if (asock->stream_oriented) {
597
/* Read next data. We limit ourselves to processing max_loop immediate
598
* data, so when the loop counter has exceeded this value, force the
599
* read()/recvfrom() to return pending operation to allow the program
602
bytes_read = r->max_size - r->size;
603
flags = asock->read_flags;
604
if (++loop >= asock->max_loop)
605
flags |= PJ_IOQUEUE_ALWAYS_ASYNC;
607
if (asock->read_type == TYPE_RECV) {
608
status = pj_ioqueue_recv(key, op_key, r->pkt + r->size,
611
r->src_addr_len = sizeof(r->src_addr);
612
status = pj_ioqueue_recvfrom(key, op_key, r->pkt + r->size,
614
&r->src_addr, &r->src_addr_len);
617
if (status == PJ_SUCCESS) {
620
} else if (status != PJ_EPENDING && status != PJ_ECANCELLED) {
622
bytes_read = -status;
631
static pj_status_t send_remaining(pj_activesock_t *asock,
632
pj_ioqueue_op_key_t *send_key)
634
struct send_data *sd = (struct send_data*)send_key->activesock_data;
640
size = sd->len - sd->sent;
641
status = pj_ioqueue_send(asock->key, send_key,
642
sd->data+sd->sent, &size, sd->flags);
643
if (status != PJ_SUCCESS) {
644
/* Pending or error */
649
if (sd->sent == sd->len) {
650
/* The whole data has been sent. */
654
} while (sd->sent < sd->len);
660
PJ_DEF(pj_status_t) pj_activesock_send( pj_activesock_t *asock,
661
pj_ioqueue_op_key_t *send_key,
666
PJ_ASSERT_RETURN(asock && send_key && data && size, PJ_EINVAL);
668
if (asock->shutdown & SHUT_TX)
669
return PJ_EINVALIDOP;
671
send_key->activesock_data = NULL;
673
if (asock->whole_data) {
679
status = pj_ioqueue_send(asock->key, send_key, data, size, flags);
680
if (status != PJ_SUCCESS) {
681
/* Pending or error */
685
if (*size == whole) {
686
/* The whole data has been sent. */
690
/* Data was partially sent */
691
asock->send_data.data = (pj_uint8_t*)data;
692
asock->send_data.len = whole;
693
asock->send_data.sent = *size;
694
asock->send_data.flags = flags;
695
send_key->activesock_data = &asock->send_data;
698
status = send_remaining(asock, send_key);
699
if (status == PJ_SUCCESS) {
705
return pj_ioqueue_send(asock->key, send_key, data, size, flags);
710
PJ_DEF(pj_status_t) pj_activesock_sendto( pj_activesock_t *asock,
711
pj_ioqueue_op_key_t *send_key,
715
const pj_sockaddr_t *addr,
718
PJ_ASSERT_RETURN(asock && send_key && data && size && addr && addr_len,
721
if (asock->shutdown & SHUT_TX)
722
return PJ_EINVALIDOP;
724
return pj_ioqueue_sendto(asock->key, send_key, data, size, flags,
729
static void ioqueue_on_write_complete(pj_ioqueue_key_t *key,
730
pj_ioqueue_op_key_t *op_key,
731
pj_ssize_t bytes_sent)
733
pj_activesock_t *asock;
735
asock = (pj_activesock_t*) pj_ioqueue_get_user_data(key);
737
/* Ignore if we've been shutdown. This may cause data to be partially
738
* sent even when 'wholedata' was requested if the OS only sent partial
741
if (asock->shutdown & SHUT_TX)
744
if (bytes_sent > 0 && op_key->activesock_data) {
745
/* whole_data is requested. Make sure we send all the data */
746
struct send_data *sd = (struct send_data*)op_key->activesock_data;
748
sd->sent += bytes_sent;
749
if (sd->sent == sd->len) {
750
/* all has been sent */
751
bytes_sent = sd->sent;
752
op_key->activesock_data = NULL;
754
/* send remaining data */
757
status = send_remaining(asock, op_key);
758
if (status == PJ_EPENDING)
760
else if (status == PJ_SUCCESS)
761
bytes_sent = sd->sent;
763
bytes_sent = -status;
765
op_key->activesock_data = NULL;
769
if (asock->cb.on_data_sent) {
772
ret = (*asock->cb.on_data_sent)(asock, op_key, bytes_sent);
774
/* If callback returns false, we have been destroyed! */
781
PJ_DEF(pj_status_t) pj_activesock_start_accept(pj_activesock_t *asock,
786
PJ_ASSERT_RETURN(asock, PJ_EINVAL);
787
PJ_ASSERT_RETURN(asock->accept_op==NULL, PJ_EINVALIDOP);
789
/* Ignore if we've been shutdown */
791
return PJ_EINVALIDOP;
793
asock->accept_op = (struct accept_op*)
794
pj_pool_calloc(pool, asock->async_count,
795
sizeof(struct accept_op));
796
for (i=0; i<asock->async_count; ++i) {
797
struct accept_op *a = &asock->accept_op[i];
801
a->new_sock = PJ_INVALID_SOCKET;
802
a->rem_addr_len = sizeof(a->rem_addr);
804
status = pj_ioqueue_accept(asock->key, &a->op_key, &a->new_sock,
805
NULL, &a->rem_addr, &a->rem_addr_len);
806
if (status == PJ_SUCCESS) {
807
/* We've got immediate connection. Not sure if it's a good
808
* idea to call the callback now (probably application will
809
* not be prepared to process it), so lets just silently
812
pj_sock_close(a->new_sock);
814
} while (status == PJ_SUCCESS);
816
if (status != PJ_EPENDING) {
825
static void ioqueue_on_accept_complete(pj_ioqueue_key_t *key,
826
pj_ioqueue_op_key_t *op_key,
830
pj_activesock_t *asock = (pj_activesock_t*) pj_ioqueue_get_user_data(key);
831
struct accept_op *accept_op = (struct accept_op*) op_key;
833
PJ_UNUSED_ARG(new_sock);
835
/* Ignore if we've been shutdown */
840
if (status == asock->last_err && status != PJ_SUCCESS) {
841
asock->err_counter++;
842
if (asock->err_counter >= PJ_ACTIVESOCK_MAX_CONSECUTIVE_ACCEPT_ERROR) {
843
PJ_LOG(3, ("", "Received %d consecutive errors: %d for the accept()"
844
" operation, stopping further ioqueue accepts.",
845
asock->err_counter, asock->last_err));
849
asock->err_counter = 0;
850
asock->last_err = status;
853
if (status==PJ_SUCCESS && asock->cb.on_accept_complete) {
856
/* Notify callback */
857
ret = (*asock->cb.on_accept_complete)(asock, accept_op->new_sock,
858
&accept_op->rem_addr,
859
accept_op->rem_addr_len);
861
/* If callback returns false, we have been destroyed! */
865
#if defined(PJ_IPHONE_OS_HAS_MULTITASKING_SUPPORT) && \
866
PJ_IPHONE_OS_HAS_MULTITASKING_SUPPORT!=0
867
activesock_create_iphone_os_stream(asock);
869
} else if (status==PJ_SUCCESS) {
870
/* Application doesn't handle the new socket, we need to
871
* close it to avoid resource leak.
873
pj_sock_close(accept_op->new_sock);
876
/* Don't start another accept() if we've been shutdown */
880
/* Prepare next accept() */
881
accept_op->new_sock = PJ_INVALID_SOCKET;
882
accept_op->rem_addr_len = sizeof(accept_op->rem_addr);
884
status = pj_ioqueue_accept(asock->key, op_key, &accept_op->new_sock,
885
NULL, &accept_op->rem_addr,
886
&accept_op->rem_addr_len);
888
} while (status != PJ_EPENDING && status != PJ_ECANCELLED);
892
PJ_DEF(pj_status_t) pj_activesock_start_connect( pj_activesock_t *asock,
894
const pj_sockaddr_t *remaddr,
900
return PJ_EINVALIDOP;
902
return pj_ioqueue_connect(asock->key, remaddr, addr_len);
905
static void ioqueue_on_connect_complete(pj_ioqueue_key_t *key,
908
pj_activesock_t *asock = (pj_activesock_t*) pj_ioqueue_get_user_data(key);
910
/* Ignore if we've been shutdown */
914
if (asock->cb.on_connect_complete) {
917
ret = (*asock->cb.on_connect_complete)(asock, status);
920
/* We've been destroyed */
924
#if defined(PJ_IPHONE_OS_HAS_MULTITASKING_SUPPORT) && \
925
PJ_IPHONE_OS_HAS_MULTITASKING_SUPPORT!=0
926
activesock_create_iphone_os_stream(asock);
931
#endif /* PJ_HAS_TCP */