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

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
时间: 2024-10-14 06:27:42

socket网络间通信初识的相关文章

Linux下TCP网络编程与基于Windows下C#socket编程间通信

一.linux下TCP网络编程基础,需要了解相关函数 Socket():用于套接字初始化. Bind():将 socket 与本机上的一个端口绑定,就可以在该端口监听服务请求. Listen():使socket处于被动的监听模式,并为该  socket  建立一个输入数据队列,将到达的服务器, 请求保存在此队列中,直到程序处理他们. Accept():让服务器接收客户的连接请求. Connect():客户端使用connect函数来配置 socket并与远端服务器建立一个 TCP 连接. Clos

配置静态路由实现网络间通信

静态路由(英语:Static routing),一种路由的方式,路由项(routing entry)由手动配置,而非动态决定.与动态路由不同,静态路由是固定的,不会改变,即使网络状况已经改变或是重新被组态.一般来说,静态路由是由网络管理员逐项加入路由表. 操作环境:思科模拟器 PC1:192.168.10.10/24 vlan 10 PC2: 192.168.20.10/24 vlan 20 PC3:192.168.100.10 vlan 100 交换机配置 # 进入vlan模式,创建vlan

[OSI]网络间通信流程

PC 连接交换机A,组成内网.DNS Serv 和 Web Serv 连接交换机B 组成外网. 内网通信 PC1 到 PC2: PC1 发送的数据先到交换机A,交换机A没有ARP地址缓存表,进行广播. 到路由器时,数据被丢弃,到PC2时,PC2 响应返回其MAC地址信息,交换机A进行ARP缓存,响应PC1. PC1得到响应再发真实数据,此时交换机A通过ARP地址缓存表找到PC2单播发送数据,PC2响应数据. 外网通信PC1 通过浏览器访问 Web Serv: PC1 发送数据到交换机A,到网关时

中小型网络最全的VLAN技术(三)——实现不同网段间通信——三层交换(路由)原理

实现不同网段间通信 实验概况: 如上两图所示,多vlan间通信建立在三层交换的基础上,通过给虚拟vlan配置Ip网关,从而实现路由功能,实现不同VLAN间通信.如若跨多个VLAN或者路由器,则配置相应的静态路由.原理解释: 路由器的工作原理: 1.仅仅查看数据包中的IP地址中的目标IP地址: 2.将目标IP地址与 路由器的核心工作表 -- 路由表 中的条目进行匹配: #如果匹配成功,则在条目对应的端口中发送出去: #如果匹配失败,则直接丢弃: 路由条目的样子:前缀 / 掩码 协议/类型 优先级

C#通过Socket在网络间发送和接收图片的演示源码

将内容过程中常用的内容段备份一次,如下资料是关于C#通过Socket在网络间发送和接收图片的演示的内容,希望能对码农们有帮助. using System;using System.Collections.Generic;using System.Text;using System.Net.Sockets;using System.Net;using System.IO; namespace ConsoleApplication1{Class Program{static void Main (S

Python网络编程02/基于TCP协议的socket简单的通信

目录 Python网络编程02/基于TCP协议的socket简单的通信 1.昨日内容回顾 2.socket 2.1 socket套接字 2.2 基于TCP协议的socket简单通信 Python网络编程02/基于TCP协议的socket简单的通信 1.昨日内容回顾 1.单播:单独联系某一个人 2.广播:给所有人发送消息(群发) 3.比特流:bit就是0101跟水流一样的源源不断的发送01010101 4.以太网协议:将数据进行分组:一组称之为一帧,数据报 head|data head:18字节:

socket 网络编程快速入门(一)教你编写基于UDP/TCP的服务(客户端)通信

因为UNIX和Win的socket大同小异,为了方便和大众化,这里先介绍Winsock编程. socket 网络编程的难点在入门的时候就是对基本函数的了解和使用,因为这些函数的结构往往比较复杂,参数大部分都是结构体,令人难以记忆和理解. 但是一旦我们知道这些函数包括其参数的具体含义,socket网络编程也就变得不是那么复杂.这里不赘述 具体函数的详细含义,网络上有很多的文章,同时笔者建议大家参考 MSDN,对返回值,参数等会有更好的理解. 以下均为单线程的简单实例,多线程的请关注下一篇文章. (

socket 网络编程高速入门(一)教你编写基于UDP/TCP的服务(client)通信

由于UNIX和Win的socket大同小异,为了方便和大众化,这里先介绍Winsock编程. socket 网络编程的难点在入门的时候就是对基本函数的了解和使用,由于这些函数的结构往往比較复杂,參数大部分都是结构体,令人难以记忆和理解. 可是一旦我们知道这些函数包含其參数的详细含义,socket网络编程也就变得不是那么复杂. 这里不赘述 详细函数的详细含义.网络上有非常多的文章.同一时候笔者建议大家參考 MSDN.对返回值,參数等会有更好的理解. 下面均为单线程的简单实例,多线程的请关注下一篇文

docker的网络、端口映射和容器间通信

docker作为服务器内部的一个容器单位,对外的通信也就有了困难.这里提供了一些几个解决方案. 1.Docker独立IP 简单说就是配置独立的网桥,分配给docker IP cd /etc/sysconfig/network-scripts/ vi ifcfg-eth0 DEVICE=eth0 BOOTPROTO=none ONBOOT=yes TYPE=Ethernet BRIDGE="br0" BOOTPROTO=static vi ifcfg-br0 DEVICE="b