• IOS Socket 04-利用框架CocoaAsyncSocket实现客户端/服务器端


      这篇文章,我们介绍CocoaAsyncSocket框架的使用,主要介绍实现客户端/服务器端代码,相信在网上已经很多这样的文章了,这里做一下自己的总结。这里介绍使用GCD方式

    一.客户端

    1.下载地址

    读者可以在github下载框架源码 https://github.com/robbiehanson/CocoaAsyncSocket

    下载后,可以看到在Examples下面可以看到很多例子,如果读者自学能力高,可以略过下面的文章。

    2.开始使用

    1)在 SourceGCD 目录下,我们可以看到GCDAsyncSocket的h与m文件,我们复制这两个文件到项目中。

    2)我们在代码里面导入头文件 #import "GCDAsyncSocket.h" 与 协议 GCDAsyncSocketDelegate。

    3.代码

    1)定义全局变量 GCDAsyncSocket *_socket;

    2)开始连接服务器

    - (void) connect2Server() {
        //1.主机与端口号
        NSString *host = @"127.0.0.1";
        int port = 12345;
        
        //初始化socket,这里有两种方式。分别为是主/子线程中运行socket。根据项目不同而定
        _socket = [[GCDAsyncSocket alloc] initWithDelegate:self delegateQueue:dispatch_get_main_queue()];//这种是在主线程中运行
        //_socket = [[GCDAsyncSocket alloc] initWithDelegate:self delegateQueue:dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)]; 这种是在子线程中运行
        
        //开始连接
        NSError *error = nil;
        [_socket connectToHost:host onPort port error:&error];
        if(error) {
            NSLog("error:%@", error);
        }
        
    }

    3)登录

    -(void)login() {
        //登录String
        NSString *loginStr = "iam:I am login!";
        NSData *loginData = [loginStr dataUsingEncoding: NSUTF8StringEncoding];
        //发送登录指令。-1表示不超时。tag200表示这个指令的标识,很大用处
        [_socket writeData: loginData withTimeout:-1 tag:200];
    }

    4)开始聊天

    //发送聊天数据
    -(void) sendMsg: (NSString*)msg{
        NSString *sendMsg = [@"msg:I send message to u!"];
        NSData *sendData = [sendMsg dataUsingEncoding: NSUTF8StringEncoding];
        [_socket writeData; sendData withTimeout:-1 tag:201];
    }

    5)实现协议delegate

    #pragma mark -socket的代理
    
    #pragma mark 连接成功
    
    -(void)socket:(GCDAsyncSocket *)sock didConnectToHost:(NSString *)host port:(uint16_t)port{   
        //连接成功
        NSLog(@"%s",__func__);
    }
    
    #pragma mark 断开连接
    -(void)socketDidDisconnect:(GCDAsyncSocket *)sock withError:(NSError *)err{
        if (err) { 
            NSLog(@"连接失败");
        }else{
            NSLog(@"正常断开");  
        }
    }
    
    #pragma mark 数据发送成功
    -(void)socket:(GCDAsyncSocket *)sock didWriteDataWithTag:(long)tag{  
        NSLog(@"%s",__func__); 
        //发送完数据手动读取 
        [sock readDataWithTimeout:-1 tag:tag];
    }
    
    #pragma mark 读取数据
    -(void)socket:(GCDAsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag{    
        //代理是在主/子线程调用
        NSLog(@"%@",[NSThread currentThread]);
        NSString *receiverStr = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
        
        if (tag == 200) {
        //登录指令
         }else if(tag == 201){
        //聊天数据
        }    
        NSLog(@"%s %@",__func__,receiverStr);
    }

    二.服务器端

    服务器端不可能在iphone/ipad上运行,所以我们新建一个单纯命令行的项目。

    1.新建一个类MyChatServer,并且开放公共方法startServer,头文件如下

    #import <Foundation/Foundation.h>
    
    @interface MyChatServer : NSObject
    /**
     *  开启聊天服务器
     */
    -(void)startServer;
    @end

    2.在main文件中,开启聊天服务器,并且开启主运行循环,让服务器接收到客户端请求

    //实现聊天
    MyChatServer *chatServer = [[MyChatServer alloc] init];
    [chatServer startServer];
    //开启主运行循环
    [[NSRunLoop currentRunLoop] run];

    3.在MychatServer.m文件中,我们开始实现GCDAsyncSocket的方法(下面代码都在MychatServer.m中)

    1)首先建一个成员变量array对象,把所有的socket(客户端)对象保存在里面

    @property(strong,nonatomic)NSMutableArray *clientSocket;

    私有变量serverSocket与golbal_queue

    GCDAsyncSocket *_serverSocket;
    dispatch_queue_t _golbalQueue;

    2)在类初始化的时候,我们初始化私有变量与成员变量

    -(instancetype)init{
        if (self = [super init]) {
          _clientSocket = [NSMutableArray array];    
          //创建全局queue
          _golbalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
          //创建服务端的socket,注意这里的是初始化的同时已经指定了delegate
          _serverSocket = [[GCDAsyncSocket alloc] initWithDelegate:self delegateQueue:_golbalQueue];
        } 
        return self;
    }

    3)开启服务器聊天startServer

    -(void)startChatServer{   
        //打开监听端口
        NSError *err;
        [_serverSocket acceptOnPort:12345 error:&err];
         if (!err) {
            NSLog(@"服务开启成功");
        }else{
            NSLog(@"服务开启失败");
        }
    }

    4)实现delegate

    #pragma mark 有客户端建立连接的时候调用
    -(void)socket:(GCDAsyncSocket *)sock didAcceptNewSocket:(GCDAsyncSocket *)newSocket{
        //sock为服务端的socket,服务端的socket只负责客户端的连接,不负责数据的读取。   newSocket为客户端的socket
        NSLog(@"服务端的socket %p 客户端的socket %p",sock,newSocket);
        //保存客户端的socket,如果不保存,服务器会自动断开与客户端的连接(客户端那边会报断开连接的log)
        NSLog(@"%s",__func__);
        [self.clientSocket addObject:newSocket];
        
        //newSocket为客户端的Socket。这里读取数据
        [newSocket readDataWithTimeout:-1 tag:100];
    }
    
    #pragma mark 服务器写数据给客户端
    -(void)socket:(GCDAsyncSocket *)sock didWriteDataWithTag:(long)tag{
        NSLog(@"%s",__func__);
        [sock readDataWithTimeout:-1 tag:100];
    }
    
    #pragma mark 接收客户端传递过来的数据
    -(void)socket:(GCDAsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag{
        //sock为客户端的socket 
        NSLog(@"客户端的socket %p",sock);
        //接收到数据
        NSString *receiverStr = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
        NSLog(@"length:%ld",receiverStr.length);
        // 把回车和换行字符去掉,接收到的字符串有时候包括这2个,导致判断quit指令的时候判断不相等
        receiverStr = [receiverStr stringByReplacingOccurrencesOfString:@"
    " withString:@""];
        receiverStr = [receiverStr stringByReplacingOccurrencesOfString:@"
    " withString:@""];
        
        //判断是登录指令还是发送聊天数据的指令。这些指令都是自定义的
        //登录指令
        if([receiverStr hasPrefix:@"iam:"]){
    // 获取用户名
            NSString *user = [receiverStr componentsSeparatedByString:@":"][1];
            // 响应给客户端的数据
            NSString *respStr = [user stringByAppendingString:@"has joined"];
          [sock writeData:[respStr dataUsingEncoding:NSUTF8StringEncoding] withTimeout:-1 tag:0];
        }
        //聊天指令
        if ([receiverStr hasPrefix:@"msg:"]) {
            //截取聊天消息
            NSString *msg = [receiverStr componentsSeparatedByString:@":"][1];
          [sock writeData:[msg dataUsingEncoding:NSUTF8StringEncoding] withTimeout:-1 tag:0];
        }
        //quit指令
        if ([receiverStr isEqualToString:@"quit"]) {
            //断开连接
            [sock disconnect];
            //移除socket
           [self.clientSocket removeObject:sock]; 
        }
        NSLog(@"%s",__func__);
    }

    查看本文章之前,可以看看

    IOS Socket 03-建立连接与登录

    可以关注本人的公众号,多年经验的原创文章共享给大家。

  • 相关阅读:
    XUL
    weblogic更新license步骤
    用Eclipse+ axis2_1.1.1+tomcat5.5 开发Web Services
    网管和黑客都必须知道的命令
    WebLogic Server实现双向SSL
    配置 WebLogic 9
    JSTL fmt:formatNumber 数字、货币格式化
    关闭myeclipse的Quick Update自动更新功能
    ibatis2.3+mysql5.1+resin3.15乱码感想
    配置MyEclipse 6 自带的tomcat6
  • 原文地址:https://www.cnblogs.com/alunchen/p/5501073.html
Copyright © 2020-2023  润新知