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"];
    }
}
View Code
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];
}
View Code
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];
    }
}
View Code

同样地,当服务端写完数据给客户端后,又继续(等待)读取客户端发送过来的内容。

好的,继续看下去。

- (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];
}
View Code

这是服务端读取到客户端发来的信息,然后因为需要更新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];
        }
    }
}
View Code

 

 

ok,至此为止,服务端的代码也差不多看完,应该也略懂七八成了吧。下一篇,我会继续看客户端的实现。

 

 

 

posted @ 2013-08-13 11:13  卓毅  阅读(1425)  评论(0编辑  收藏  举报