CocoaAsyncSocket--mac Or iOS下封装好的Socket编程库(二)
接下来的两篇,我就使用两个例子来说明下CocoaAsyncSocket的基本使用方法。
首先,在这里,先看看服务端的例子。这里使用的是EchoServer,例子在上一篇的github地址里面可以下载到。
由于这个服务端是mac下的应用,对于只做iOS客户端的我来说,这还真是比较新鲜的内容,不过看着看着,感觉还能明白,也算是略懂而已。
先看下初始化时候,做了哪些处理:
- (id)init { if((self = [super init])) { // Setup our logging framework. [DDLog addLogger:[DDTTYLogger sharedInstance]]; // Setup our socket. // The socket will invoke our delegate methods using the usual delegate paradigm. // However, it will invoke the delegate methods on a specified GCD delegate dispatch queue. // // Now we can configure the delegate dispatch queues however we want. // We could simply use the main dispatc queue, so the delegate methods are invoked on the main thread. // Or we could use a dedicated dispatch queue, which could be helpful if we were doing a lot of processing. // // The best approach for your application will depend upon convenience, requirements and performance. socketQueue = dispatch_queue_create("socketQueue", NULL); listenSocket = [[GCDAsyncSocket alloc] initWithDelegate:self delegateQueue:socketQueue]; // Setup an array to store all accepted client connections connectedSockets = [[NSMutableArray alloc] initWithCapacity:1]; isRunning = NO; } return self; }
DDLog不用管,是作者写好的一个打印各种信息的类,不过有时间去了解下也不错,可以挪用到其他应用里面。
好,现在看看socketQueue,是自己设定用来响应delegate methods的线程。很有意思,GCDAsyncSocket对于这一点,设置得比较巧妙,因为他不同于其他网络库,可以允许你设置在哪个线程里面执行delegate methods。而其他网络库,说是异步执行,但最后响应delegate methods时候还是用了主线程,虽然从主线程去响应也是应该,因为很多时候需要更新UI界面,只不过有些时候我们需要在网络信息返回后做一系列的操作处理,那这时候可能往往就需要在其他线程里面进行处理了。
看看这个开关监听的方法。
- (IBAction)startStop:(id)sender { if(!isRunning) { int port = [portField intValue]; if (port < 0 || port > 65535) { [portField setStringValue:@""]; port = 0; } NSError *error = nil; if(![listenSocket acceptOnPort:port error:&error]) { [self logError:FORMAT(@"Error starting server: %@", error)]; return; } [self logInfo:FORMAT(@"Echo server started on port %hu", [listenSocket localPort])]; isRunning = YES; [portField setEnabled:NO]; [startStopButton setTitle:@"Stop"]; } else { // Stop accepting connections [listenSocket disconnect]; // Stop any client connections @synchronized(connectedSockets) { NSUInteger i; for (i = 0; i < [connectedSockets count]; i++) { // Call disconnect on the socket, // which will invoke the socketDidDisconnect: method, // which will remove the socket from the list. [[connectedSockets objectAtIndex:i] disconnect]; } } [self logInfo:@"Stopped Echo server"]; isRunning = false; [portField setEnabled:YES]; [startStopButton setTitle:@"Start"]; } }
if(![listenSocket acceptOnPort:port error:&error])
这一句话就是开启了服务端的监听。
ok,那当一个客户端连接上该服务端的时候,我们服务端应该作出一个回应,表示欢迎。以下说的都是delegate methods。
- (void)socket:(GCDAsyncSocket *)sock didAcceptNewSocket:(GCDAsyncSocket *)newSocket { // This method is executed on the socketQueue (not the main thread) @synchronized(connectedSockets) { [connectedSockets addObject:newSocket]; } NSString *host = [newSocket connectedHost]; UInt16 port = [newSocket connectedPort]; dispatch_async(dispatch_get_main_queue(), ^{ @autoreleasepool { [self logInfo:FORMAT(@"Accepted client %@:%hu", host, port)]; } }); NSString *welcomeMsg = @"Welcome to the AsyncSocket Echo Server\r\n"; NSData *welcomeData = [welcomeMsg dataUsingEncoding:NSUTF8StringEncoding]; [newSocket writeData:welcomeData withTimeout:-1 tag:WELCOME_MSG]; [newSocket readDataToData:[GCDAsyncSocket CRLFData] withTimeout:READ_TIMEOUT tag:0]; }
NSString *welcomeMsg = @"Welcome to the AsyncSocket Echo Server\r\n"; NSData *welcomeData = [welcomeMsg dataUsingEncoding:NSUTF8StringEncoding]; [newSocket writeData:welcomeData withTimeout:-1 tag:WELCOME_MSG];
服务端通过newsocket返回欢迎信息给客户端。
[newSocket readDataToData:[GCDAsyncSocket CRLFData] withTimeout:READ_TIMEOUT tag:0];
服务端继续(等待)读取客户端发送过来的内容。
下一个delegate method
- (void)socket:(GCDAsyncSocket *)sock didWriteDataWithTag:(long)tag { // This method is executed on the socketQueue (not the main thread) if (tag == ECHO_MSG) { [sock readDataToData:[GCDAsyncSocket CRLFData] withTimeout:READ_TIMEOUT tag:0]; } }
同样地,当服务端写完数据给客户端后,又继续(等待)读取客户端发送过来的内容。
好的,继续看下去。
- (void)socket:(GCDAsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag { // This method is executed on the socketQueue (not the main thread) dispatch_async(dispatch_get_main_queue(), ^{ @autoreleasepool { NSData *strData = [data subdataWithRange:NSMakeRange(0, [data length] - 2)]; NSString *msg = [[NSString alloc] initWithData:strData encoding:NSUTF8StringEncoding]; if (msg) { [self logMessage:msg]; } else { [self logError:@"Error converting received data into UTF-8 String"]; } } }); // Echo message back to client [sock writeData:data withTimeout:-1 tag:ECHO_MSG]; }
这是服务端读取到客户端发来的信息,然后因为需要更新UI,所以在这里,就调用了主线程来进行更新。而最后一句,是服务端返回客户端,说这边已经收到客户端发过来的信息了。
后面两个方法,是用来响应连接超时和断开连接时候的。也不算很难,就此略过了。
- (NSTimeInterval)socket:(GCDAsyncSocket *)sock shouldTimeoutReadWithTag:(long)tag elapsed:(NSTimeInterval)elapsed bytesDone:(NSUInteger)length { if (elapsed <= READ_TIMEOUT) { NSString *warningMsg = @"Are you still there?\r\n"; NSData *warningData = [warningMsg dataUsingEncoding:NSUTF8StringEncoding]; [sock writeData:warningData withTimeout:-1 tag:WARNING_MSG]; return READ_TIMEOUT_EXTENSION; } return 0.0; } - (void)socketDidDisconnect:(GCDAsyncSocket *)sock withError:(NSError *)err { if (sock != listenSocket) { dispatch_async(dispatch_get_main_queue(), ^{ @autoreleasepool { [self logInfo:FORMAT(@"Client Disconnected")]; } }); @synchronized(connectedSockets) { [connectedSockets removeObject:sock]; } } }
ok,至此为止,服务端的代码也差不多看完,应该也略懂七八成了吧。下一篇,我会继续看客户端的实现。