I have a running application under iOS which is using TCP socket streams for communication with remote hosts. Using right now the 30days trial period of Chilkat as I want to figure out how to support a similar pattern.
I manage to connect to the remote host (optional also via SSH tunnel; one of the main reason why I want to change); but the streaming class seems (not yet) supporting sockets as source/sink ? Or did I missed something ?
Is there an example showing how to implement TCP socket streaming ?
Not sure if correct way and it sure "feels" not right these days anymore: an endless polling loop in a async GDC block ?
It do what is should do; but is there a better; more portable way ?
// build the general queue
self.gcdQueue = dispatch_queue_create([qn cStringUsingEncoding:NSASCIIStringEncoding], DISPATCH_QUEUE_CONCURRENT);
dispatch_async(self.gcdQueue, ^{ NSString *connectionString = [self.hostAddress copy]; unsigned int connectionPort = [self.port intValue];
// get reference to runloop in thread
connection.runLoop = [NSRunLoop currentRunLoop];
if ([connectionString length] == 0)
{
connectionString = [client.hostName copy];
connectionPort = [client.hostPort intValue];
}
self.ckStream = [[CkoStream alloc] init];
_ckSocket = NULL;
int maxWaitMs = 5000;
BOOL useTls = NO;
self.ckSocket = [[CkoSocket alloc] init];
if (_ckSocket == nil )
{
CLLog(@"%@", _ckTunnel.LastErrorText);
return;
}
int maxWaitMillisec = 20000;
BOOL success;
success = [_ckSocket Connect: connectionString port: [NSNumber numberWithInt: connectionPort] ssl: useTls maxWaitMs: [NSNumber numberWithInt: maxWaitMillisec]];
if (success != YES)
{
CLLog(@"%@", _ckSocket.LastErrorText);
return;
}
if (_ckSocket.LastMethodSuccess != YES)
{
CLLog(@"%@",_ckSocket.LastErrorText);
return;
}
_ckSocket.UserData = connectionString;
CLLog(@"Connection to %@:%d = %@", connectionString, connectionPort, _ckSocket.ConnectFailReason);
// connection is opened now; send some first instructions
[connection initializeClientConnection:self];
//
// ATTENTION: endless loop in a async block to get the data from/to the chilkat socket
//
while ([_ckSocket IsConnected])
{
[connection ckSocketStreamHandler: _ckSocket]; // doing socket.PollDataAvailable and process data incoming
[_ckSocket SleepMs:[NSNumber numberWithInt:10]];
}
CLLog(@"loop finished with polling socket");
[connection.runLoop run];
});
TCP sockets (including TLS or SSH Tunneled connections) are by definition streaming. In other words, any socket API should have calls to read from the connection (which is a stream of incoming bytes), and write to the connection (which is a stream of outgoing bytes).
The Chilkat Socket API has a variety of methods to read/write the socket connection.
The only reason for abstracting a TCP socket connection to some sort of stream object, is if it is possible that the stream could be something other than a socket. For example, if an application is to read data from some abstract stream, and that stream's source could be a file, or a socket connection, or something else. If the source of data would never come from anything but a socket connection, then it's pointless to abstract the socket API to a stream. What is gained? Nothing. Instead of calling methods on the socket API to read whatever bytes are available, or checking to see if data is available, the app would be calling the semantically identical methods on an abstracted stream API.
Over-abstracting and adding additional unnecessary layers of complexity to an application is common mistake developers make. Avoid these things when there's no need.
Having said that.. Chilkat will eventually be adding the ability for the Socket API to interact with Chilkat's Stream class. But again, this would only be needed if the "stream" needs to be abstracted such that the source of data could come from other things (such as a file).
Regardless, the application logic and control flow to check for the availability of newly arrived incoming data and to read it -- this would be the same even after Chilkat eventually adds new methods to abstract to a Stream. The solution you suggested above could be one valid solution (I'd have to research the specifics of the dispatch_queue_create, as I'm not familiar with it). Another possible solution is to call the Async version of a method that reads the socket, and then handle the TaskCompleted callback when it fires -- which would fire in a background thread, meaning that your application callback method would also be in a background thread and care would need to be taken with that in mind.