Skip to content

Commit eaf5fd2

Browse files
committed
[tidev#3344] - Support for outbound Ti.Network.Socket
- New tests for sockets in KS - Updated iOS TCPSocket docs to indicate DEPRECATION
1 parent e6e1edd commit eaf5fd2

16 files changed

+1407
-275
lines changed

apidoc/Titanium/Network/Network.tdoc

+12-3
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,10 @@ android, iphone, ipad
2222
- methods
2323

2424
createHTTPClient : returns an HttpClient instance
25-
createBonjourBrowser : returns a BonjourBrowser instance
26-
createBonjourService : returns a BonjourService instance
27-
createTCPSocket : returns a TPCPSocket instance
25+
createBonjourBrowser : returns a BonjourBrowser instance (iOS only)
26+
createBonjourService : returns a BonjourService instance (iOS only)
27+
createTCPSocket : returns a TPCPSocket instance (iOS only; DEPRECATED)
28+
createSocket : Returns a Socket instance
2829
registerForPushNotifications: register for push notifications with the Apple Push Notification Service. Only available on iPhone.
2930
encodeURIComponent: encode a URI component part using URI encoding
3031
decodeURIComponent: decode a URI component part using URI encoding
@@ -87,6 +88,14 @@ WRITE_MODE[int]: constant value specifying write-only mode for sockets
8788
READ_WRITE_MODE[int]: constant value specifying read-write mode for sockets
8889
INADDR_ANY[string]: constant value representing the ability for sockets to listen on any locally available network device
8990

91+
TCP[int]: constant value representing TCP transport protocol
92+
93+
SOCKET_INITIALIZED[int]: constant value representing a socket in the INITIALIZED state
94+
SOCKET_CONNECTED[int]: constant value representing a socket in the CONNECTED state
95+
SOCKET_LISTENING[int]: constant value representing a socket in the LISTENING state
96+
SOCKET_CLOSED[int]: constant value representing a socket in the CLOSED state
97+
SOCKET_ERROR[int]: constant value representing a socket in the ERROR state
98+
9099
online[boolean]: readonly boolean value that indicates if the network is reachable to the Internet either via WIFI or Carrier network
91100
networkTypeName[string]: the network type name constant. Returns one of `NONE`, `WIFI`, `LAN` or `MOBILE`.
92101
networkType[int]: the network type value as a constant.

apidoc/Titanium/Network/Socket.tdoc

Whitespace-only changes.

apidoc/Titanium/Network/TCPSocket.tdoc

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ object
99

1010
- description
1111

12-
The TCPSocket instance returned from `Titanium.Network.createTCPSocket`. This object represents a socket which either listens locally on the device for connections, or connects to a remote machine.
12+
DEPRECATED: USE `Ti.Network.Socket` WHERE POSSIBLE. The TCPSocket instance returned from `Titanium.Network.createTCPSocket`. This object represents a socket which either listens locally on the device for connections, or connects to a remote machine.
1313

1414
- since
1515

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
var win = Ti.UI.currentWindow;
2+
3+
/*
4+
* Assumes the existence of a `Ti.Blob Ti.createBlob(string text)` method
5+
*/
6+
var connectingSocket = null;
7+
8+
var hostField = Ti.UI.createTextField({
9+
value:'HOSTNAME',
10+
top:20,
11+
left:20,
12+
width:140,
13+
height:40,
14+
borderStyle:Ti.UI.INPUT_BORDERSTYLE_ROUNDED,
15+
autocorrect:false,
16+
autocapitalization:Ti.UI.TEXT_AUTOCAPITALIZATION_NONE,
17+
clearOnEdit:true
18+
});
19+
win.add(hostField);
20+
21+
var portField = Ti.UI.createTextField({
22+
value:'PORT',
23+
top:20,
24+
right:20,
25+
width:100,
26+
height:40,
27+
borderStyle:Ti.UI.INPUT_BORDERSTYLE_ROUNDED,
28+
autocorrect:false,
29+
autocapitalization:Ti.UI.TEXT_AUTOCAPITALIZATION_NONE,
30+
clearOnEdit:true
31+
});
32+
win.add(portField);
33+
34+
var writeArea = Ti.UI.createTextArea({
35+
editable:true,
36+
value:'Data to write',
37+
height:100,
38+
width:300,
39+
top:80,
40+
textAlign:'left',
41+
borderWidth:2,
42+
borderColor:'#bbb',
43+
borderRadius:5,
44+
suppressReturn:false
45+
});
46+
win.add(writeArea);
47+
48+
var statusArea = Ti.UI.createTextArea({
49+
editable:false,
50+
value:'Socket status',
51+
height:100,
52+
width:300,
53+
bottom:80,
54+
textAlign:'left',
55+
borderWidth:2,
56+
borderColor:'#bbb',
57+
borderRadius:5,
58+
suppressReturn:false
59+
});
60+
win.add(statusArea);
61+
62+
var connectButton = Ti.UI.createButton({
63+
title:'Connect',
64+
width:80,
65+
height:40,
66+
left:20,
67+
bottom:20
68+
});
69+
connectButton.addEventListener('click', function() {
70+
if (connectingSocket == null) {
71+
try {
72+
connectingSocket = Ti.Network.createSocket({
73+
hostName:hostField.value,
74+
port:portField.value,
75+
type:Ti.Network.TCP,
76+
connected:function(e) {
77+
e.socket.write(Ti.createBlob("Well, hello there!"));
78+
},
79+
error:function(e) {
80+
statusArea.value = "ERROR ("+e.errorCode+"): "+e.error;
81+
},
82+
closed:function(e) {
83+
statusArea.value = "CLOSED CONNECTION TO: "+e.socket.host+":"+e.socket.port;
84+
},
85+
read:function(e) {
86+
statusArea.value = "DATA: "+e.data.toString();
87+
}
88+
});
89+
connectingSocket.connect();
90+
}
91+
catch (e) {
92+
statusArea.value = "EXCEPTION (connect): "+e.toString();
93+
}
94+
}
95+
else {
96+
statusArea.value = 'Already connected: '+connectingSocket.hostName +':'+connectingSocket.port;
97+
}
98+
});
99+
win.add(connectButton);
100+
101+
var disconnectButton = Ti.UI.createButton({
102+
title:'Disconnect',
103+
width:100,
104+
height:40,
105+
right:20,
106+
bottom:20
107+
});
108+
disconnectButton.addEventListener('click', function() {
109+
if (connectingSocket != null) {
110+
try {
111+
connectingSocket.close();
112+
connectingSocket = null;
113+
}
114+
catch (e) {
115+
statusArea.value = "EXCEPTION (close): "+e.toString();
116+
}
117+
}
118+
else {
119+
statusArea.value = 'Not connected';
120+
}
121+
});
122+
win.add(disconnectButton);
123+
124+
var writeButton = Ti.UI.createButton({
125+
title:'Write',
126+
width:80,
127+
height:40,
128+
bottom:20,
129+
left:110
130+
});
131+
writeButton.addEventListener('click', function() {
132+
if (connectingSocket != null && connectingSocket.state == Ti.Network.SOCKET_CONNECTED) {
133+
connectingSocket.write(Ti.createBlob(writeArea.value));
134+
}
135+
});
136+
win.add(writeButton);

demos/KitchenSink/Resources/main_windows/platform.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@ var data = [
1818
{title:'JSON', hasChild:true, test:'../examples/json.js'},
1919
{title:'JS search', hasChild:true, test:'../examples/search_case_insensitive.js'},
2020
{title:'Clipboard', hasChild:true, test:'../examples/clipboard.js'},
21-
{title:'Sockets', hasChild:true, test:'../examples/sockets.js'}
21+
{title:'Connecting socket', hasChild:true, test:'../examples/socket_connect.js'},
22+
{title:'Listening socket', hasChild:true, test:'../examples/socket_listener.js'}
2223
];
2324

2425
if (Titanium.Platform.name == 'iPhone OS')

iphone/Classes/AsyncSocket.h

+52-20
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,8 @@ enum AsyncSocketError
2929
};
3030
typedef enum AsyncSocketError AsyncSocketError;
3131

32-
@interface NSObject (AsyncSocketDelegate)
32+
@protocol AsyncSocketDelegate
33+
@optional
3334

3435
/**
3536
* In the event of an error, the socket is closed.
@@ -49,11 +50,17 @@ typedef enum AsyncSocketError AsyncSocketError;
4950
- (void)onSocketDidDisconnect:(AsyncSocket *)sock;
5051

5152
/**
52-
* Called when a socket accepts a connection. Another socket is spawned to handle it. The new socket will have
53+
* Called when a socket accepts a connection (listening w/autoaccept 'YES'). Another socket is spawned to handle it. The new socket will have
5354
* the same delegate and will call "onSocket:didConnectToHost:port:".
5455
**/
5556
- (void)onSocket:(AsyncSocket *)sock didAcceptNewSocket:(AsyncSocket *)newSocket;
5657

58+
/**
59+
* Called when there is a connection to accept (listening w/autoaccept 'NO'). Can retrieve the BSD socket
60+
* handle from the passed socket and call accept() on it, guaranteed to nonblock
61+
**/
62+
- (void)onSocketHasConnectionToAccept:(AsyncSocket *)sock;
63+
5764
/**
5865
* Called when a new socket is spawned to handle a connection. This method should return the run-loop of the
5966
* thread on which the new socket and its delegate should operate. If omitted, [NSRunLoop currentRunLoop] is used.
@@ -90,7 +97,7 @@ typedef enum AsyncSocketError AsyncSocketError;
9097
* This would occur if using readToData: or readToLength: methods.
9198
* It may be used to for things such as updating progress bars.
9299
**/
93-
- (void)onSocket:(AsyncSocket *)sock didReadPartialDataOfLength:(CFIndex)partialLength tag:(long)tag;
100+
- (void)onSocket:(AsyncSocket *)sock didReadPartialDataOfLength:(NSUInteger)partialLength tag:(long)tag;
94101

95102
/**
96103
* Called when a socket has completed writing the requested data. Not called if there is an error.
@@ -101,7 +108,7 @@ typedef enum AsyncSocketError AsyncSocketError;
101108
* Called when a socket has written some data, but has not yet completed the entire write.
102109
* It may be used to for things such as updating progress bars.
103110
**/
104-
- (void)onSocket:(AsyncSocket *)sock didWritePartialDataOfLength:(CFIndex)partialLength tag:(long)tag;
111+
- (void)onSocket:(AsyncSocket *)sock didWritePartialDataOfLength:(NSUInteger)partialLength tag:(long)tag;
105112

106113
/**
107114
* Called if a read operation has reached its timeout without completing.
@@ -116,8 +123,8 @@ typedef enum AsyncSocketError AsyncSocketError;
116123
**/
117124
- (NSTimeInterval)onSocket:(AsyncSocket *)sock
118125
shouldTimeoutReadWithTag:(long)tag
119-
elapsed:(NSTimeInterval)elapsed
120-
bytesDone:(CFIndex)length;
126+
elapsed:(NSTimeInterval)elapsed
127+
bytesDone:(NSUInteger)length;
121128

122129
/**
123130
* Called if a write operation has reached its timeout without completing.
@@ -132,8 +139,8 @@ typedef enum AsyncSocketError AsyncSocketError;
132139
**/
133140
- (NSTimeInterval)onSocket:(AsyncSocket *)sock
134141
shouldTimeoutWriteWithTag:(long)tag
135-
elapsed:(NSTimeInterval)elapsed
136-
bytesDone:(CFIndex)length;
142+
elapsed:(NSTimeInterval)elapsed
143+
bytesDone:(NSUInteger)length;
137144

138145
/**
139146
* Called after the socket has successfully completed SSL/TLS negotiation.
@@ -249,6 +256,20 @@ typedef enum AsyncSocketError AsyncSocketError;
249256
**/
250257
- (BOOL)acceptOnInterface:(NSString *)interface port:(UInt16)port error:(NSError **)errPtr;
251258

259+
/**
260+
* This method is the same as acceptOnInterface:port:error: with the additional option
261+
* of specifying whether or not the listening socket will autoaccept incoming connections. If 'YES',
262+
* then the onSocket:didAcceptNewSocket: callback is called; otherwise, onSocketHasConnectionToAccept:
263+
* is called.
264+
**/
265+
- (BOOL)acceptOnInterface:(NSString *)interface port:(UInt16)port autoaccept:(BOOL)autoaccept error:(NSError **)errPtr;
266+
267+
/**
268+
* This method is used to create a new async socket from an accepted socket, and fires the onSocket:didAcceptNewSocket: delegate callback.
269+
* Returns the new async socket, or nil if the accept failed.
270+
**/
271+
- (AsyncSocket*)doAcceptFromSocket:(CFSocketRef)parentSocket withNewNativeSocket:(CFSocketNativeHandle)newNativeSocket;
272+
252273
/**
253274
* Connects to the given host and port.
254275
* The host may be a domain name (e.g. "deusty.com") or an IP address string (e.g. "192.168.0.2")
@@ -280,6 +301,11 @@ typedef enum AsyncSocketError AsyncSocketError;
280301
**/
281302
- (BOOL)connectToAddress:(NSData *)remoteAddr withTimeout:(NSTimeInterval)timeout error:(NSError **)errPtr;
282303

304+
- (BOOL)connectToAddress:(NSData *)remoteAddr
305+
viaInterfaceAddress:(NSData *)interfaceAddr
306+
withTimeout:(NSTimeInterval)timeout
307+
error:(NSError **)errPtr;
308+
283309
/**
284310
* Disconnects immediately. Any pending reads or writes are dropped.
285311
* If the socket is not already disconnected, the onSocketDidDisconnect delegate method
@@ -389,7 +415,7 @@ typedef enum AsyncSocketError AsyncSocketError;
389415
*
390416
* If the timeout value is negative, the read operation will not use a timeout.
391417
* If the buffer if nil, a buffer will automatically be created for you.
392-
* If maxLength is negative, no length restriction is enforced.
418+
* If maxLength is zero, no length restriction is enforced.
393419
*
394420
* If the bufferOffset is greater than the length of the given buffer,
395421
* the method will do nothing, and the delegate will not be called.
@@ -401,7 +427,7 @@ typedef enum AsyncSocketError AsyncSocketError;
401427
- (void)readDataWithTimeout:(NSTimeInterval)timeout
402428
buffer:(NSMutableData *)buffer
403429
bufferOffset:(NSUInteger)offset
404-
maxLength:(CFIndex)length
430+
maxLength:(NSUInteger)length
405431
tag:(long)tag;
406432

407433
/**
@@ -411,7 +437,7 @@ typedef enum AsyncSocketError AsyncSocketError;
411437
*
412438
* If the length is 0, this method does nothing and the delegate is not called.
413439
**/
414-
- (void)readDataToLength:(CFIndex)length withTimeout:(NSTimeInterval)timeout tag:(long)tag;
440+
- (void)readDataToLength:(NSUInteger)length withTimeout:(NSTimeInterval)timeout tag:(long)tag;
415441

416442
/**
417443
* Reads the given number of bytes.
@@ -429,7 +455,7 @@ typedef enum AsyncSocketError AsyncSocketError;
429455
* After completion, the data returned in onSocket:didReadData:withTag: will be a subset of the given buffer.
430456
* That is, it will reference the bytes that were appended to the given buffer.
431457
**/
432-
- (void)readDataToLength:(CFIndex)length
458+
- (void)readDataToLength:(NSUInteger)length
433459
withTimeout:(NSTimeInterval)timeout
434460
buffer:(NSMutableData *)buffer
435461
bufferOffset:(NSUInteger)offset
@@ -478,9 +504,11 @@ typedef enum AsyncSocketError AsyncSocketError;
478504
* Reads bytes until (and including) the passed "data" parameter, which acts as a separator.
479505
*
480506
* If the timeout value is negative, the read operation will not use a timeout.
481-
* If maxLength is negative, no length restriction is enforced.
482507
*
483-
* If the max length is surpassed, it is treated the same as a timeout - the socket is closed.
508+
* If maxLength is zero, no length restriction is enforced.
509+
* Otherwise if maxLength bytes are read without completing the read,
510+
* it is treated similarly to a timeout - the socket is closed with a AsyncSocketReadMaxedOutError.
511+
* The read will complete successfully if exactly maxLength bytes are read and the given data is found at the end.
484512
*
485513
* If you pass nil or zero-length data as the "data" parameter,
486514
* the method will do nothing, and the delegate will not be called.
@@ -491,7 +519,7 @@ typedef enum AsyncSocketError AsyncSocketError;
491519
* Note that this method is not character-set aware, so if a separator can occur naturally as part of the encoding for
492520
* a character, the read will prematurely end.
493521
**/
494-
- (void)readDataToData:(NSData *)data withTimeout:(NSTimeInterval)timeout maxLength:(CFIndex)length tag:(long)tag;
522+
- (void)readDataToData:(NSData *)data withTimeout:(NSTimeInterval)timeout maxLength:(NSUInteger)length tag:(long)tag;
495523

496524
/**
497525
* Reads bytes until (and including) the passed "data" parameter, which acts as a separator.
@@ -501,9 +529,11 @@ typedef enum AsyncSocketError AsyncSocketError;
501529
*
502530
* If the timeout value is negative, the read operation will not use a timeout.
503531
* If the buffer if nil, a buffer will automatically be created for you.
504-
* If maxLength is negative, no length restriction is enforced.
505532
*
506-
* If the max length is surpassed, it is treated the same as a timeout - the socket is closed.
533+
* If maxLength is zero, no length restriction is enforced.
534+
* Otherwise if maxLength bytes are read without completing the read,
535+
* it is treated similarly to a timeout - the socket is closed with a AsyncSocketReadMaxedOutError.
536+
* The read will complete successfully if exactly maxLength bytes are read and the given data is found at the end.
507537
*
508538
* If you pass a maxLength parameter that is less than the length of the data parameter,
509539
* the method will do nothing, and the delegate will not be called.
@@ -522,7 +552,7 @@ typedef enum AsyncSocketError AsyncSocketError;
522552
withTimeout:(NSTimeInterval)timeout
523553
buffer:(NSMutableData *)buffer
524554
bufferOffset:(NSUInteger)offset
525-
maxLength:(CFIndex)length
555+
maxLength:(NSUInteger)length
526556
tag:(long)tag;
527557

528558
/**
@@ -537,8 +567,8 @@ typedef enum AsyncSocketError AsyncSocketError;
537567
* Returns progress of current read or write, from 0.0 to 1.0, or NaN if no read/write (use isnan() to check).
538568
* "tag", "done" and "total" will be filled in if they aren't NULL.
539569
**/
540-
- (float)progressOfReadReturningTag:(long *)tag bytesDone:(CFIndex *)done total:(CFIndex *)total;
541-
- (float)progressOfWriteReturningTag:(long *)tag bytesDone:(CFIndex *)done total:(CFIndex *)total;
570+
- (float)progressOfReadReturningTag:(long *)tag bytesDone:(NSUInteger *)done total:(NSUInteger *)total;
571+
- (float)progressOfWriteReturningTag:(long *)tag bytesDone:(NSUInteger *)done total:(NSUInteger *)total;
542572

543573
/**
544574
* Secures the connection using SSL/TLS.
@@ -625,6 +655,8 @@ typedef enum AsyncSocketError AsyncSocketError;
625655
* Note: NSRunLoopCommonModes is defined in 10.5. For previous versions one can use kCFRunLoopCommonModes.
626656
**/
627657
- (BOOL)setRunLoopModes:(NSArray *)runLoopModes;
658+
- (BOOL)addRunLoopMode:(NSString *)runLoopMode;
659+
- (BOOL)removeRunLoopMode:(NSString *)runLoopMode;
628660

629661
/**
630662
* Returns the current run loop modes the AsyncSocket instance is operating in.

0 commit comments

Comments
 (0)