We are currently developing a TLS-server-client-application. For testing the client send a command with the content "0001" and the server sends an answer.
The client send the data via the write-method of an NSOutputStream. When the data is send first time, it arrives completely. When I'm sending it a second or third time the data is split after the first byte. The server get first "0" and than the rest "001". I have no idea why it is split.
The communication looks like: ServerConnect send to server:
0001 receive from server: OK send to server: 0 receive
from server: error send to server: 001 receive from server:
error send to server: 0 receive from server: error send
to server: 001 receive from server: error ServerDisconnect
I hope someone can help me with this.
- (void)initNetworkCommunication:(NSString*)ns_IP :(UInt32)ui_Port :(BOOL)b_ValidateCertificate
{
CFReadStreamRef readStream;
CFWriteStreamRef writeStream;
CFStreamCreatePairWithSocketToHost(NULL, (__bridge CFStringRef)ns_IP, ui_Port, &readStream, &writeStream);
inputStream = (__bridge NSInputStream *)readStream;
outputStream = (__bridge NSOutputStream *)writeStream;
[inputStream setDelegate:(id)self];
[outputStream setDelegate:(id)self];
[inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[outputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
NSDictionary *settings = [[NSDictionary alloc] initWithObjectsAndKeys:
[NSNumber numberWithBool:b_ValidateCertificate], kCFStreamSSLValidatesCertificateChain,
[NSNumber numberWithBool:YES], kCFStreamPropertyShouldCloseNativeSocket,
kCFNull,kCFStreamSSLPeerName,
kCFStreamSocketSecurityLevelSSLv3, kCFStreamSSLLevel,
nil];
CFReadStreamSetProperty((CFReadStreamRef)inputStream, kCFStreamPropertySSLSettings, (CFTypeRef)settings);
CFWriteStreamSetProperty((CFWriteStreamRef)outputStream, kCFStreamPropertySSLSettings, (CFTypeRef)settings);
[inputStream open];
[outputStream open];
}
- (BOOL) sendData:(NSString*)ns_SendData :(NSUInteger)nui_bufferLength
{
NSUInteger nui_length = [ns_SendData length];
NSUInteger nui_chunkSize = nui_bufferLength;
NSUInteger nui_offset = 0;
long l_Result;
NSString *ns_response = [NSString stringWithFormat:#"%#", ns_SendData];
NSData *nd_data = [[NSData alloc] initWithData:[ns_response dataUsingEncoding:NSASCIIStringEncoding]];
// a none-ASCII-symbol exists
if ([nd_data length] == 0)
return FALSE;
// split the data
do {
NSUInteger nui_thisChunkSize = nui_length - nui_offset > nui_chunkSize ? nui_chunkSize : nui_length - nui_offset;
NSData* chunk = [NSData dataWithBytesNoCopy:(char *)[nd_data bytes] + nui_offset
length:nui_thisChunkSize
freeWhenDone:NO];
nui_offset += nui_thisChunkSize;
// send data to server
l_Result = [outputStream write:[chunk bytes] maxLength:[chunk length]];
NSLog(#"%s - %lu", __PRETTY_FUNCTION__, l_Result);
if (l_Result == -1) {
NSLog(#"error sending data");
return false;
}
} while (nui_offset < nui_length);
return true;
}
The problem is solved by using TLS 1.2. See the AppTechnical Note TN2287
const void* keys[] = { kCFStreamSSLValidatesCertificateChain, kCFStreamSSLLevel };
// New CFString values available only on iOS 5:
// (if these values are used on versions before iOS 5, then will
// default to max TLS 1.0, min SSLv3)
// kCFStreamSocketSecurityLevelTLSv1_0SSLv3 configures max TLS 1.0, min SSLv3
// (same as default behavior on versions before iOS 5).
// kCFStreamSocketSecurityLevelTLSv1_0 configures to use only TLS 1.0.
// kCFStreamSocketSecurityLevelTLSv1_1 configures to use only TLS 1.1.
// kCFStreamSocketSecurityLevelTLSv1_2 configures to use only TLS 1.2.
const void* values[] = { CFSTR("kCFBooleanTrue"), CFSTR("kCFStreamSocketSecurityLevelTLSv1_2") };
CFDictionaryRef sslSettingsDict = CFDictionaryCreate(kCFAllocatorDefault, keys, values, 2,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
CFReadStreamSetProperty(readStream, kCFStreamPropertySSLSettings, sslSettingsDict);
CFWriteStreamSetProperty(writeStream, kCFStreamPropertySSLSettings, sslSettingsDict);
CFRelease(sslSettingsDict);
Related
I'm trying to use a socket framework to connect to a socket I have. I am able to connect just fine with raw socket code like:
uint portNo = 9900;
CFReadStreamRef readStream;
CFWriteStreamRef writeStream;
CFStreamCreatePairWithSocketToHost(NULL, (CFStringRef)#"123.456.789.159", portNo, &readStream, &writeStream);
inputStream = (__bridge NSInputStream *)readStream;
outputStream = (__bridge NSOutputStream *)writeStream;
[inputStream setDelegate:self];
[outputStream setDelegate:self];
[inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[outputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[inputStream open];
[outputStream open];
but when I try with any framework I get an error and I'm completely stumped as to why. I've tried SocketRocket and Jetfire. Here's the code for connecting in SocketRocket that I've tried:
_webSocket.delegate = nil;
[_webSocket close];
_webSocket = [[SRWebSocket alloc] initWithURL:[NSURL URLWithString:#"wss://123.456.789.159"]];
_webSocket.delegate = self;
NSLog(#"Opening Connection...");
[_webSocket open];
I've also tried with the port number added.
_webSocket = [[SRWebSocket alloc] initWithURL:[NSURL URLWithString:#"wss://123.456.789.159:9900"]];
Also tried with http, https, ws. Still nothing.
Anybody have an idea as to why?
WebSocket is a protocol for bringing raw socket like functionality on top of HTTP. The socket that your server is exposing likely does not support the WebSocket protocol.
If you want a higher-level API for raw socket programming on iOS, CocoaAsyncSocket might be a good option for you.
I was working in Socket Communication in iOS. below is the code i have done.
- (void)TcpClientInitialise
{
NSLog(#"Tcp Client Initialise");
CFReadStreamRef readStream;
CFWriteStreamRef writeStream;
CFStreamCreatePairWithSocketToHost(NULL, (__bridge CFStringRef)_IPAddress.text,(int)_port.text.integerValue, &readStream, &writeStream);
[consoleLog appendString:[NSString stringWithFormat:#"IPAddress - %# and port %d \n", _IPAddress.text,(int)_port.text.integerValue]];
_console.text = consoleLog;
[InputStream setProperty:NSStreamSocketSecurityLevelNegotiatedSSL forKey: (__bridge NSString *)kCFStreamPropertySSLSettings];
InputStream = (__bridge NSInputStream *)readStream;
OutputStream = (__bridge NSOutputStream *)writeStream;
[InputStream setDelegate:self];
[OutputStream setDelegate:self];
[InputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[OutputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[InputStream open];
[OutputStream open];
}
but i not able initiate the connection. the problem is the Host is Enable with the SSL certificate. My Server team has said i have connect to the host with the certificate. and they have given the certificate. which is "digicert.crt"
Please help me how to connect to the host with using the certificate.
Im using input streams to communicate with a POS device.
When i send the first request the response is normal. The problem comes when i send the second request because the second response is shorter than first one and that content is still in the stream. So i get the following:
First response:
<response>A really big response with much more things inside</response>
Second response;
<response>A not so big response</response>more things inside</response>
I do the following:
1) Open the streams
CFStreamCreatePairWithSocketToHost(NULL, (__bridge CFStringRef)host, [port intValue], &readStream, &writeStream);
iStream = (NSInputStream *)readStream;
oStream = (NSOutputStream *)writeStream;
[iStream setDelegate:self];
[oStream setDelegate:self];
[iStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[oStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[iStream open];
[oStream open];
2) then i write my request:
[oStream write:(uint8_t *)[request UTF8String] maxLength:[request length]];
3) when the response arrives i capture it and send it to the parser:
uint8_t buffer[MAX_BUFFER_SIZE];
[iStream read:buffer maxLength:MAX_BUFFER_SIZE - 1];
NSString *response = [NSString stringWithUTF8String:(char *)buffer];
free(buffer);
NSError *error;
NSDictionary *ret =[SimpleXMLConverter dictionaryForXMLString:response error:&error];
After this, i tried to close the streams and open them again before sending the second request
Thanks and regards
PS: its not a blocking issue because i can get the correct substring, but i dont understand why its happening
That's because your buffer string is not \0(NULL) terminated.
Maybe uint8_t buffer[MAX_BUFFER_SIZE] = {0}; will solve your current problem.
However, you should consider one response could be longer than MAX_BUFFER_SIZE - 1.
Even if a response is shorter than it, read:maxLength: is not guaranteed to read all data at once. You must check the returned value and received data has entire response.
Moreover, DO NOT free(buffer), because free() is only for malloc()ed memory, not for auto variables.
Using this very helpful tutorial, I've been able to put together a test (video) of socket communication between the iOS Simulator and Maya. This is working very well with the simulator and localhost. However, when I test it on my device over the same wireless network, the iOS application just hangs with no connection with Maya (upon connection, there's a little "heads-up" message that displays).
I'm new to iOS programming and socket programming in particular, but I'm wondering if I'm missing anything. Here's the method I'm calling with the "connect" button press:
- (void) initNetworkCommunication {
//assign text inputs to variables
self.ipAddress = self.inputIPAddress.text;
self.portNumber = [self.inputPortNumber.text intValue];
//create streams and use variables to populate connection method
CFReadStreamRef readStream;
CFWriteStreamRef writeStream;
CFStreamCreatePairWithSocketToHost(NULL, (__bridge CFStringRef)(self.ipAddress), self.portNumber, &readStream, &writeStream);
inputStream = (__bridge NSInputStream *)readStream;
outputStream = (__bridge NSOutputStream *)writeStream;
[inputStream setDelegate:self];
[outputStream setDelegate:self];
[inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[outputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[inputStream open];
[outputStream open];
//send initial message to Maya
NSString *initResponse = [NSString stringWithFormat:#"cmds.headsUpMessage('connected to iPhone')"];
NSData *initData = [[NSData alloc] initWithData:[initResponse dataUsingEncoding:NSASCIIStringEncoding]];
[outputStream write:[initData bytes] maxLength:[initData length]];
//need some error checking or timeout mechanism
}
Like I said, works like a charm with localhost, but fails over the wireless network.
Okay, found my own answer by digging through someone else's python script for creating a commandPort in Maya. It actually isn't due to the iOS code at all. It turns out that in Maya I need to create a commandPort using not only a port number but also the ip address. My mistake was that I assumed the commandPort knew the local ip address or didn't care. Apparently it cares.
Before (simplified code):
cmds.commandPort (n=':6328', stp='python')
After:
cmds.commandPort (n='192.168.2.7:6328', stp='python')
I have been working on a NSStreamDelegate, I have implemented call back, I have initialized the input and output stream ilke this...
CFReadStreamRef readStream;
CFWriteStreamRef writeStream;
CFStringRef host = CFSTR("74.125.224.72");
UInt32 port = 2270;
CFStreamCreatePairWithSocketToHost(kCFAllocatorDefault, host, port, &inputStream, &writeStream);
if (writeStream && inputStream) {
inputStream = (__bridge NSInputStream *)readStream;
[inputStream setDelegate:self];
[inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[inputStream open];
outputStream = (__bridge NSOutputStream *)writeStream;
[outputStream setDelegate:self];
[outputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[outputStream open];
}
Even after opened both the stream callback(stream:(NSStream *)theStream handleEvent:) is not called with NSStreamEventOpenCompleted for both streams. Can anyone help me what am I doing wrong here. Or What is the possibilities NSStreamEventOpenCompleted won't be called, I have seen in documentation, if opening failed it will not call this, if so why opening of streams is failing. Any idea?
thanks for your help.
I use with very similar code and it works fine for me.
Try the code below.
NSString* host = #"192.168.2.105";
CFReadStreamRef readStream;
CFWriteStreamRef writeStream;
UInt32 port = 8008;
CFStreamCreatePairWithSocketToHost(kCFAllocatorDefault, (__bridge CFStringRef)(host), port, &readStream, &writeStream);
if (writeStream && readStream) {
self.InputStream = (__bridge NSInputStream *)readStream;
[self.InputStream setDelegate:self];
[self.InputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[self.InputStream open];
self.OutputStream = (__bridge NSOutputStream *)writeStream;
[self.OutputStream setDelegate:self];
[self.OutputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[self.OutputStream open];
}
If it doesn't work for you, I can to send you a small app that implement TCP Client and server for a example.
If it is running in a new NSThread, make sure the run loop of the thread is started after the stream setup, like CFRunLoopRun();