美文网首页iOS
iOS-Socket开发学习笔记-2

iOS-Socket开发学习笔记-2

作者: 806349745123 | 来源:发表于2017-05-15 18:18 被阅读29次

    前提

    一般流程图:

    image.png

    一、简单客户端实现(苹果自带的框架)

    • 1、与服务器进行连接
    - (IBAction)connectToServer {
        //ios里实现sokcet的连接,使用C语言
        
        // 1.绑定地址和端口,与服务器通过三次握手建立连接
        NSString *host = @"127.0.0.1"; // 服务器ip
        int port = 12345; // 连接端口,用来区分 软件/进程
        
        // 2.定义输入输出流
        CFReadStreamRef readStream;  // 输入流,读数据
        CFWriteStreamRef writeStream; // 输出流,写数据
        
        // 3.分配输入输出流的内存空间
        CFStreamCreatePairWithSocketToHost(NULL, (__bridge CFStringRef)host, port, &readStream, &writeStream);
        
        // 4.把C语言的输入输出流转成OC对象,方便面向对象编程
        _inputStream = (__bridge NSInputStream *)readStream;
        _outputSteam = (__bridge NSOutputStream *)(writeStream);
        
        // 5.设置代理,监听数据接收的状态
        _outputSteam.delegate = self;
        _inputStream.delegate = self;
        
        // 把输入输入流添加到主运行循环(RunLoop),通过运行循环不断检查端口的数据状态
        // 若加到子线程中,delegate回调也是在子线程中执行
        [_outputSteam scheduleInRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
        [_inputStream scheduleInRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
        
        // 6.打开输入输出流
        [_inputStream open];
        [_outputSteam open];
    }
    
    • 2、输入/输出流代理回调
    - (void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)eventCode {
    //    NSStreamEventNone
    //    NSStreamEventOpenCompleted = 1UL << 0,
    //    NSStreamEventHasBytesAvailable = 1UL << 1,
    //    NSStreamEventHasSpaceAvailable = 1UL << 2,
    //    NSStreamEventErrorOccurred = 1UL << 3,
    //    NSStreamEventEndEncountered = 1UL << 4
        
        switch (eventCode) {
            case NSStreamEventOpenCompleted:
                NSLog(@"%@",aStream);
                NSLog(@"成功连接建立,形成输入输出流的传输通道");
                break;
                
            case NSStreamEventHasBytesAvailable:
                NSLog(@"有数据可读");
                [self readData];
                break;
                
            case NSStreamEventHasSpaceAvailable:
                NSLog(@"可以发送数据");
                break;
                
             case NSStreamEventErrorOccurred:
                NSLog(@"有错误发生,连接失败");
                break;
                
             case NSStreamEventEndEncountered:
                NSLog(@"正常的断开连接");
                break;
            default:
                break;
        }
    }
    
    • 3、读数据
    - (void)readData {
        
        //定义缓冲区 这个缓冲区只能存储1024字节
        uint8_t buf[1024];
        
        // 读取数据
        // len为服务器读取到的实际字节数,实际数据可能不满1024字节
        NSInteger len = [_inputStream read:buf maxLength:sizeof(buf)];
        
        // 把缓冲区里的实现字节数转成字符串
        NSString *receiverStr = [[NSString alloc] initWithBytes:buf length:len encoding:NSUTF8StringEncoding];
        NSLog(@"%@",receiverStr);
    }
    
    • 4、发数据
    - (void)sendData:(NSData *)data {
        [_outputSteam write:data.bytes maxLength:data.length];
    }
    

    一、简单服务端实现(GCDAsyncSocket)

    • 1、创建socket
    - (instancetype)init {
        if (self = [super init]) {
             // 回调线程
            _globalQ = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
            //1.创建服务端的socket
            _serverSocket = [[GCDAsyncSocket alloc] initWithDelegate:self delegateQueue:_globalQ];
        }
        return self;
    }
    
    • 2、监听端口
    -(void)startChatServer{
        //2.打开监听端口
        NSError *err;
        [_serverSocket acceptOnPort:54321 error:&err];
        if (!err) {
            NSLog(@"服务开启成功");
        }else{
            NSLog(@"服务开启失败");
        }
    }
    
    • 3、与客户端建立连接
    - (void)socket:(GCDAsyncSocket *)sock didAcceptNewSocket:(GCDAsyncSocket *)newSocket {
        NSLog(@"服务端的socket %p 客户端的socket %p",sock,newSocket);
        //保存与客户端之间的socket套接字,以后和客户端交换信息需要用到这个newSocket
        [self.clientSocket addObject:newSocket];
        
        //sock 服务端的socket 服务端的socket只负责客户端的连接,不负责数据的读取
        //先读取数据
        [newSocket readDataWithTimeout:-1 tag:100];
    }
    
    • 4、服务器写数据给客户端回调
    - (void)socket:(GCDAsyncSocket *)sock didWriteDataWithTag:(long)tag {
        NSLog(@"%s",__func__);
    }
    
    • 5、服务器收到客户端数据的回调
    - (void)socket:(GCDAsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag {
        // sock为与客户端连接的套接字
    
        //接收到数据
        NSString *receiverStr = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
        NSLog(@"length:%ld",receiverStr.length);
    
        // 以下数据处理的流程可以忽略
        // 把回车和换行字符去掉
        receiverStr = [receiverStr stringByReplacingOccurrencesOfString:@"\r" withString:@""];
        receiverStr = [receiverStr stringByReplacingOccurrencesOfString:@"\n" withString:@""];
        
        //判断是登录指令还是发送聊天数据的指令
        //登录指令
        if([receiverStr hasPrefix:@"iam:"]){//登录指令
            // 返回 xxx has joined
            // 获取用户名
            NSString *user = [receiverStr componentsSeparatedByString:@":"][1];
            
            // 响应给客户端的数据
            NSString *respStr = [user stringByAppendingString:@"has joined"];
            //sock
            [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];
            return;
        }
    
        // 维持输入流的监听
        [sock readDataWithTimeout:-1 tag:100];
    }
    

    相关文章

      网友评论

        本文标题:iOS-Socket开发学习笔记-2

        本文链接:https://www.haomeiwen.com/subject/okyhxxtx.html