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