Socket

*********************************************

简单理解Socket 就是网络连接,可以实现两个点之间的数据通讯。

 

Socket允许使用长连接,允许应用程序运行在异步模式(提高效率),只有在需要的时候才接收数据

 

使用Socket,可以只传送数据本身而不用进行XML封装,大大降低数据传输的开销      在(JSON)之前出现的

iOS中常用的两种Socket类型

 

Ø流式Socket(SOCK_STREAM):流式是一种面向连接的Socket,针对于面向连接的TCP服务应用
Ø数据报式Socket(SOCK_DGRAM):数据报式Socket是一种无连接的Socket,对应于无连接的UDP服务应用
 
•在iOS中以NSStream(流)来发送和接收数据
开发步骤
 
{
网络连接设置
  1.设置网络连接,绑定到主机和端口
  2.设置输入流和输出流的代理,监听数据流的状态
  3.将输入输出流添加至运行循环
  4.打开输入流和输出流
发送消息给服务器
有可读取字节时,读取服务器返回的内容
到达流末尾时,关闭流,同时并从主运行循环中删除
}
 

 通过Scoket可以实现所有的网络功能:包括:GET、POST、PUT、DELETE

文件读取、写入(I/O)方式是以(二进制)流的方式读取的
 最主要的应用场景是:自定义的协议,编写自由的网络应用!
 
 Socket 的难点
 1. 因为所有的(I/O)输入输出都是在一个代理方法中调用,随着自定义协议的复杂度的提高,
    程序编写难度势必要大幅度提升。
 2. 多线程的处理!
   输入流和输出流都添加到了主运行循环,如果应用过于复杂,将影响主线程程序的性能
    因此,需要使用另外一个运行循环,专门管理输入输出流。
   代理方法的工作是对数据的输入输出流进行“解析”,解析工作同样不需要影响到主线程的工作。
 
 3.多线程方面的处理,是Socket的一大难点!可以使用第三方框架GCDAsyncSocket来解决多线程问题。

连接服务器

#pragma mark 连接到服务器
- (void)connectToServer:(NSString *)hostName port:(NSInteger)port
{
    // 设置网络
    CFReadStreamRef readStream;
    CFWriteStreamRef writeStream;
  
    // CF框架是C语言的框架
    // 此方法可以连接到服务器,并分配输入流和输出流的内存空间
    CFStreamCreatePairWithSocketToHost(NULL, (__bridge CFStringRef)hostName, port, &readStream, &writeStream);
    
    // 记录已经分配的输入流和输出流
    _inputStream = (__bridge NSInputStream *)readStream;
    _outputStream = (__bridge NSOutputStream *)writeStream;
    
    // 设置代理,监听输入流和输出流中的变化
    _inputStream.delegate = self;
    _outputStream.delegate = self;
    
    // Scoket是建立的长连接,需要将输入输出流添加到主运行循环
    // 如果不将流加入主运行循环,delegate拒绝工作
    [_inputStream scheduleInRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
    [_outputStream scheduleInRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
    
    // 打开输入流和输出流,准备开始文件读写操作
    [_inputStream open];
    [_outputStream open];
}

 登录到聊天室

 1 #pragma mark 登录到聊天室
 2 - (IBAction)login
 3 {
 4     NSString *hostName = _hostName.text;
 5     NSInteger port = [_portText.text integerValue];
 6     
 7     [self connectToServer:hostName port:port];
 8     
 9     // 发送登录消息
10     NSString *msg = [NSString stringWithFormat:@"iam:%@",      _nickNameText.text];
11     // 在网络上发送的是二进制数据
12     NSData *data = [msg dataUsingEncoding:NSUTF8StringEncoding];
13     
14     // 发送数据,直接往输入流写数据
15     [_outputStream write:data.bytes maxLength:data.length];
16 }

 NSStream的代理方法

/**
 NSStreamEventNone = 0,                         // 无事件
 NSStreamEventOpenCompleted = 1UL << 0,         // 建立连接完成
 NSStreamEventHasBytesAvailable = 1UL << 1,     // 有可读的字节,接收到了数据,可以读了
 NSStreamEventHasSpaceAvailable = 1UL << 2,     // 可以使用输出流的空间,此时可以发送数据给服务器
 NSStreamEventErrorOccurred = 1UL << 3,         // 发生错误
 NSStreamEventEndEncountered = 1UL << 4         // 流结束事件,在此事件中负责做销毁工作
 */
#pragma mark NSStream的代理方法

- (void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)eventCode
{
    NSLog(@"%d", eventCode);
    
    switch (eventCode) {
        case NSStreamEventOpenCompleted:
            NSLog(@"连接完成");
            break;
        case NSStreamEventHasBytesAvailable:
            NSLog(@"有可读字节");
            // 读从服务器接收到得数据,从输入流中读取
            // 先开辟一段缓冲区以读取数据,用空间来换取程序的简单
            uint8_t buffer[1024];
            
            // read返回的是输入流缓冲区中实际存储的字节数
            NSInteger len = [_inputStream read:buffer maxLength:sizeof(buffer)];
            
            if (len > 0) { // 读到数据
                // 将buffer中的数据,转换成字符串,输出
                NSString *str = [[NSString alloc] initWithBytes:buffer length:len encoding:NSUTF8StringEncoding];
                
                // 将接收到的内容添加到数组
                [_dataList addObject:str];
                
                // 刷新表格
                [_tableView reloadData];
            }
            
            break;
        case NSStreamEventHasSpaceAvailable:
            NSLog(@"可以写入数据");
            break;
        case NSStreamEventErrorOccurred:
            NSLog(@"发生错误");
            break;
        case NSStreamEventEndEncountered:
            NSLog(@"流结束");
            // 做善后工作
            // 关闭流的同时,将流从主运行循环中删除
            [aStream close];
            [aStream removeFromRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
        default:
            break;
    }
}

 GCDAsyncSocket(异步Socket)

需要导入Security.framework & CFNetwork.framework框架

// 1. Socket通讯的第一件事情——先创建一个长连接
    _socket = [[GCDAsyncSocket alloc] initWithDelegate:self delegateQueue:dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)];
创建一个长连接

相关文章:

  • 2021-06-01
  • 2022-12-23
  • 2022-12-23
  • 2022-01-09
  • 2021-12-14
  • 2021-06-08
  • 2022-12-23
猜你喜欢
  • 2022-12-23
  • 2021-05-30
  • 2022-01-18
  • 2021-04-04
  • 2022-02-01
相关资源
相似解决方案