zoukankan      html  css  js  c++  java
  • IOS

    #import "ViewController.h"

    @interface ViewController () <NSStreamDelegate, UITextFieldDelegate, UITableViewDataSource>
    {
        NSInputStream       *_inputStream;      // 输入流
        NSOutputStream      *_outputStream;     // 输出流
        
        NSMutableArray      *_dataList;
    }

    @property (weak, nonatomic) IBOutlet UITextField *hostName;
    @property (weak, nonatomic) IBOutlet UITextField *portText;
    @property (weak, nonatomic) IBOutlet UITextField *nickNameText;
    @property (weak, nonatomic) IBOutlet UITextField *messageText;

    @property (weak, nonatomic) IBOutlet UITableView *tableView;

    @end

    @implementation ViewController
    /**
     通过Scoket可以实现所有的网络功能:包括:GET、POST、PUT、DELETE
     
     最主要的应用场景是:自定义的协议,编写自由的网络应用!
     
     ==========================================================
     Socket 的难点:
     
     1. 因为所有的输入输出都是在一个代理方法中调用,随着自定义协议的复杂度的提高,
        程序编写难度势必要大幅度提升。
     
     2. 多线程的处理!
        示例代码中,输入流合输出流都添加到了主运行循环,如果应用过于复杂,将影响主线程程序的性能
        因此,需要使用另外一个运行循环,专门管理输入输出流。
     
        代理方法的工作是对数据的输入输出流进行“解析”,解析工作同样不需要影响到主线程的工作。
     
     多线程方面的处理,是Socket的一大难点!
     
     可以使用第三方框架!会在XMPP项目中隆重登场!
     
     ============================================================
     
     程序员写程序最主要的目的是用来阅读的,捎带着把程序的功能给实现了。
     
     SSL 安全漏洞,goto fail。
     
     */

    - (void)viewDidLoad
    {
        [super viewDidLoad];
        
        _dataList = [NSMutableArray array];
    }

    - (BOOL)textFieldShouldReturn:(UITextField *)textField
    {
        // 确认真的输入了文字,再发送消息给服务器
        if (textField.text.length > 0) {
            // 发送登录消息
            NSString *msg = [NSString stringWithFormat:@"msg:%@", textField.text];
            // 在网络上发送的是二进制数据
            NSData *data = [msg dataUsingEncoding:NSUTF8StringEncoding];
            
            // 发送数据,直接往输入流写数据
            [_outputStream write:data.bytes maxLength:data.length];
        }
        
        return YES;
    }

    - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
    {
        return _dataList.count;
    }

    - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
    {
        static NSString *ID = @"Cell";
        
        UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:ID];
        
        cell.textLabel.text = _dataList[indexPath.row];
        
        return cell;
    }

    #pragma mark - 私有方法
    #pragma mark 连接到服务器
    - (void)connectToServer:(NSString *)hostName port:(NSInteger)port
    {
        // 要进行Socket开发,以下代码都是固定的
        // 设置网络
        CFReadStreamRef readStream;
        CFWriteStreamRef writeStream;
        
        // CF框架是C语言的框架,在OC中的Socet方法,C语言部分的代码,总共就5行
        // 此方法可以连接到服务器,并分配输入流和输出流的内存空间
        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];
    }

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

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

    @end

  • 相关阅读:
    [ jquery 文档处理 replaceWith(content|fn) replaceAll(content) ] 此方法用于把所有匹配的元素替换成指定的HTML或DOM元素
    [ jquery 文档处理 wrapInner(htm|element|fnl) ] 此方法用于把所有匹配的元素的子元素(包括文本节点)使用指定的 HTML 元素来包裹
    [ jquery 文档处理 wrapAll(html|ele)) ] 此方法用于把所有匹配的元素使用指定的 HTML 元素来包裹
    [ jquery 文档处理 wrap(html|element|fn) ] 此方法用于把所有匹配的元素用其他元素的结构化标记包裹起来
    [ jquery 文档处理 insertBefore(content) before(content|fn) ] 此方法用于把所有匹配的元素插入到另一个、指定的元素元素集合的前面,实现外部插入
    [ jquery 文档处理 insertAfter(content) after(content|fn) ] 此方法用于把所有匹配的元素插入到另一个、指定的元素元素集合的后面,实现外部插入
    [ jquery 文档处理 prependTo(content) ] 此方法用于把所有匹配的元素前置到另一个、指定的元素元素集合中
    剑指 Offer 55
    剑指 Offer 54. 二叉搜索树的第k大节点 树的遍历
    剑指 Offer 52. 两个链表的第一个公共节点 哈希
  • 原文地址:https://www.cnblogs.com/mcj-coding/p/3579329.html
Copyright © 2011-2022 走看看