• socket网络间通信初识



    NSOperation:
    1. 指定同一时间最大执行的操作数
    queue.max……
    2. 设定队列中的任务时间的依赖关系
    task1 依赖于 task2: task2 —> task1
    3. 回到主线程(找到如何获取主队列的方式)[NSOperation mainQueue]:

    keyword: iOS main queue

    Socket: 网络中的两个程序通过双向的通讯,达到交换数据的目的。

    发送(客户端):终端控制台
    接收(服务器端):  终端控制台(sever.py)

    资源:网页(www.sina.com.cn)/图片/软件/.......

    1. 服务器监听状态 standby (开机+应用程序启动)
    2. 客户端发送请求Request给服务器端
    3. 连接确认, 客户端接收服务器返回的数据

    客户端:iOS应用程序

    服务器端:启动server.py

    #coding:utf-8
    
    from twisted.internet.protocol import Factory, Protocol
    from twisted.internet import reactor
    
    class IphoneChat(Protocol):
        def connectionMade(self):
            self.factory.clients.append(self)
            print "当前连线的客户端有", self.factory.clients
    
        def connectionLost(self, reason):
            self.factory.clients.remove(self)
    
        def dataReceived(self, data):
            a = data.split(':')
            if len(a) > 1:
                command = a[0].strip()
                content = a[1].strip()
    
                msg = ""
                if command == "iam":
                    self.name = content
                    msg = self.name + " 加入了聊天室"
    
                elif command == "msg":
                    msg = self.name + ": " + content
    
                print msg
    
                for c in self.factory.clients:
                    c.message(msg)
    
        def message(self, message):
            self.transport.write(message + '\n')
    
    
    factory = Factory()
    factory.clients = []
    factory.protocol = IphoneChat
    reactor.listenTCP(1025, factory)
    print "Iphone Chat server started"
    reactor.run()
    脚本代码


    NSInputStream:    读取数据
    NSOutputStream:写数据


    发送消息:
    功能:
    存放接收消息 —> NSArray —> UITableView

    准备工作:
    UITableView准备(datasource, delegate)
    两个stream的delegate (NSStreamDelegate)

    具体实现:

    @interface ViewController () <UITableViewDataSource, UITableViewDelegate, NSStreamDelegate>
    @property (weak, nonatomic) IBOutlet UITableView *tableView;
    @property (weak, nonatomic) IBOutlet UITextField *messageTextField;
    
    //输入流(读)
    @property (nonatomic, strong) NSInputStream *inputStream;
    
    //输出流(写)
    @property (nonatomic, strong) NSOutputStream *outputStream;
    
    //存放服务器返回消息的可变数组
    @property (nonatomic, strong) NSMutableArray *messageArray;
    
    @end
    
    @implementation ViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        //初始化可变数组对象
        self.messageArray = [NSMutableArray array];
        
        //设置delegate
        self.tableView.dataSource = self;
        self.tableView.delegate = self;
        
        //准备工作:和服务器端进行连接
        [self createConnectionToServer];
    
    }
    
    - (void)createConnectionToServer {
        //telnet 196.112.122.11 1025
        
        //创建两个stream相关的类
        CFReadStreamRef readStream;
        CFWriteStreamRef writeStream;
        
        //NSStream不具有连接服务器的功能
        //创建和服务器的连接
        /**
         第二个参数:连接服务器的ip地址 (localhost)
         第三个参数:指定端口
         */
        CFStreamCreatePairWithSocketToHost(NULL, (CFStringRef)@"localhost", 1025, &readStream, &writeStream);
        
        //将底层的CFStream转换成NSStream相关的类
        self.inputStream = (__bridge NSInputStream *)readStream;
        self.outputStream = (__bridge NSOutputStream *)writeStream;
        
        //设置两个stream的delegate
        self.inputStream.delegate = self;
        self.outputStream.delegate = self;
        
        //把这两个stream添加到runloop中(原因:才可以响应对应的代理方法)
        [self.inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
        [self.outputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
        
        //将两个stream打开
        [self.inputStream open];
        [self.outputStream open];
    }
    
    - (IBAction)enterChatRoom:(id)sender {
        //iam:xxxxx
        NSString *text = @"iam:Maggie";
        
        //NSString --> NSData
        NSData *data = [text dataUsingEncoding:NSUTF8StringEncoding];
        
        //写数据(OutputSteam)
        [self.outputStream write:[data bytes] maxLength:[data length]];
    }
    
    - (IBAction)sendMessage:(id)sender {
        //发送消息到服务器端(msg:xxxxx)
        NSString *messageStr = [NSString stringWithFormat:@"msg:%@", self.messageTextField.text];
        
        //往outputStream中写数据
        NSData *data = [messageStr dataUsingEncoding:NSUTF8StringEncoding];
        [self.outputStream write:[data bytes] maxLength:[data length]];
        
        //清空textField文本
        self.messageTextField.text = nil;
    }
    
    
    #pragma mark -- NSStreamDelegate
    //针对两个管道Stream, 处理不同的事件
    - (void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)eventCode {
        switch (eventCode) {
            case NSStreamEventOpenCompleted:
                NSLog(@"Stream打开");
                break;
            case NSStreamEventHasSpaceAvailable:
                NSLog(@"Stream还有空间可以放数据");
                break;
            case NSStreamEventHasBytesAvailable:
                NSLog(@"此时Stream有数据");
                [self readBytes:aStream];
                break;
            case NSStreamEventErrorOccurred:
                NSLog(@"有错误出现");
                //把stream关掉
                [self.inputStream close];
                [self.outputStream close];
                //从runloop中移除
                [self.inputStream removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
                [self.outputStream removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
                break;
                
            default:
                break;
        }
        
    }
    
    - (void)readBytes:(NSStream *)aStream {
        //将数据显示在table view上
        //首先判定是服务器返回的(将服务器消息显示在table view上)
        if (aStream == self.inputStream) {
            unsigned char readData[1024];
            //读取输入流的数据(服务器返回来的数据)
            while ([self.inputStream hasBytesAvailable]) {
                //获取服务器返回的数据
                /**
                 第一个参数:读取的数据存放对象
                 第二个参数:读取的最大bytes数量
                 */
                NSInteger length = [self.inputStream read:readData maxLength:sizeof(readData)];
                if (length > 0) {
                    //读取到数据
                    NSString *messageStr = [[NSString alloc] initWithBytes:readData length:length encoding:NSUTF8StringEncoding];
                    //将获取到的字符串添加到可变数组中
                    [self.messageArray addObject:messageStr];
                    //显示在table view上
                    [self.tableView reloadData];
                    
                }
            }
        }
    }
    
    #pragma mark -- table view datasouce/delegate
    - (NSInteger) tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
        return self.messageArray.count;
    }
    
    //设置cell
    - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
        //创建identifier
        static NSString *cellID = @"messageCell";
        
        //从缓存池中获取cell(Reuse可复用性)
        UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellID];
        
        //如果缓存池中没有,再重新创建
        if (cell == nil) {
            cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellID];
        }
        
        //设置cell文本属性
        cell.textLabel.text = self.messageArray[indexPath.row];
        
        return cell;
    }
    
    @end

    练习:
    1. 点中UITextFiled,键盘弹出,table view和其他控件上移
    2. 让table view最后一行,始终显示用户发送的最新的消息



    官方socket实例代码链接:
    https://developer.apple.com/library/prerelease/ios/documentation/Cocoa/Conceptual/Streams/Articles/NetworkStreams.html#//apple_ref/doc/uid/20002277-BCIDFCDI


    URL: (Uniform Resource Locator) 统⼀资源定位符
    最完整格式:
    协议://IP地址:端口/路径/文件名字
    一般格式:
    IP地址(域名)/路径/文件名字

    协议的通俗说法:发送数据方和接收数据方规定的传送数据的规则

    File协议:从本地查找相应的文件
    URL —>
    file:///Users/tarena/Downloads/testImages/aa.jpg

    HTTP协议内部数据

    小例子:
    把本地mac机器改装成提供web服务(Apache软件)的服务器步骤:
    1. 查看Apache软件是否安装
    sudo apachectl -v
    输入mac系统的密码
    2. 启动apache软件
    sudo apachectl start
    3. 在浏览器中输入localhost,显示It works!就表示mac机器成为了可以提供网页服务的机器

    http://IP地址:端口/路径/文件名字


    MIME: 客户端(浏览器)指定服务器返回的类型(xxx.html)
    文本类型:
    text/html:不仅包含文本,也包含.jpg/.gif/.....
    text/xml
    text/plain: 只能包含文本

    http协议的规则:
    1. 指定MIME类型: text/html
    2. 方法:
        a. GET (获取资源:html文件)
        b. DELETE (删除资源:图片)
        c. POST: 登录(用户名/密码)
        d. PUT: 上传文件/上传信息
    3. status code: 服务返回的状态码
         a. 成功:200/OK
         c. 失败:
         404 -> 未找到资源(html文件)
         400 -> 客户端的请求,服务器端无法解析
         501 -> 服务器错误
    4.响应Response (服务器返回):
    content-length: 总长度多少
    content-range (****): 默认服务器会返回客户端请求的资源的总数据



    iOS中发送网络请求的方案:
    方案一:
    NSURLConnection (相对麻烦/更加理解http原理):
    方案二
    NSURLSession (简单/封装性高):

    此案例主要以方案一来实现:
    样例:界面上输入任意一个网址(URL), 将返回的网页显示到界面上(xxx.html) (NSURLConnection)
    UI界面:UITextField; UIButton; UIWebView(内嵌浏览器)

    具体实现:

    @interface ViewController ()
    @property (weak, nonatomic) IBOutlet UIWebView *webView;
    @property (weak, nonatomic) IBOutlet UITextField *urlTextField;
    
    //界面输入的URL
    @property (nonatomic, strong) NSURL *url;
    
    @end
    
    @implementation ViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
    
    }
    //发送请求Request ---> 接收响应Response
    - (IBAction)sendSyncRequest:(id)sender {
        //NSURLConnection
        //1.创建一个客户端发送请求对象
        self.url = [NSURL URLWithString:self.urlTextField.text];
        NSURLRequest *urlRequest = [NSURLRequest requestWithURL:self.url];
        //2.开始执行发送的同步请求
        //3.获取服务器返回的html文件
        NSError *error = nil;
        NSData *data = [NSURLConnection sendSynchronousRequest:urlRequest returningResponse:nil error:&error];
        if (error) {
            NSLog(@"发送请求失败:%@", error.userInfo);
            return;
        }
        
        //4.并显示在UIWebView上
        [self.webView loadData:data
                      MIMEType:@"text/html"
              textEncodingName:@"utf-8"
                       baseURL:nil];
        
        NSLog(@"请求执行完毕%@; 数据的大小%lu", [NSThread currentThread], (unsigned long)data.length);
    }
    
    
    - (IBAction)sendAsyncRequest:(id)sender {
        
        //1.创建客户端发送请求对象
        self.url = [NSURL URLWithString:self.urlTextField.text];
        NSURLRequest *request = [NSURLRequest requestWithURL:self.url];
        
        //2.异步执行请求
        //创建非主队列
        NSOperationQueue *queue = [[NSOperationQueue alloc] init];
        [NSURLConnection sendAsynchronousRequest:request queue:queue completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
            //获取response的状态码(判定服务器是否返回成功200)
            //data:服务器返回的数据
            //connectionError:具体的错误
            NSLog(@"返回%@", [NSThread currentThread]);
            NSInteger retStatusCode = [(NSHTTPURLResponse *)response statusCode];
            if (retStatusCode == 200) {
                //回到主线程更新界面UIWebView
                dispatch_async(dispatch_get_main_queue(), ^{
                    //3.显示服务器返回的数据到UIWebView
                    [self.webView loadData:data MIMEType:@"text/html" textEncodingName:@"utf-8" baseURL:nil];
                });
            } else {
                NSLog(@"失败%@",connectionError.userInfo);
            }
        }];    
    }
    
    @end
  • 相关阅读:
    jQuery实现仿微博发布框字数提示
    jQuery实现滚动公告练习
    jQuery实现页面搜索
    jQuery某网站品牌列表效果
    [转]windows中断与共享的连接(samba)
    rpm --rebuilddb
    【转】一个 Linux 上分析死锁的简单方法
    取消脚本进程之——后台进程
    whoami与who am i
    linux启动执行某个脚本
  • 原文地址:https://www.cnblogs.com/YKiOS/p/4802366.html
Copyright © 2020-2023  润新知