NSURLConnection 实现webView显示HTTPS页面

最后更新于:2022-04-01 14:25:28

我们在浏览器访问https页面的时候的,会弹出: ![信任证书](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-04-07_570611039930e.jpg "") 我们接下来信任证书以及显示出来 遵循协议 ~~~ @interface ViewController ()<NSURLConnectionDataDelegate> ~~~ interface: ~~~ @interface ViewController ()<NSURLConnectionDataDelegate> /** - 存储data数据 */ @property(nonatomic,strong)NSMutableData *dataM; /** - 访问url链接 */ @property(nonatomic,strong)NSURL *url; @property(nonatomic,weak)IBOutlet UIWebView *webView; @end ~~~ viewDidLoad:创建url以及发送请求 ~~~ - (void)viewDidLoad { [super viewDidLoad]; self.url = [NSURL URLWithString:@"https://mail.com"]; NSURLRequest *request = [NSURLRequest requestWithURL:self.url]; //发送请求 [NSURLConnection connectionWithRequest:request delegate:self]; } ~~~ 实现代理方法: ~~~ #pragma mark - NSURLSessionDataDelegate代理方法 - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential * __nullable credential))completionHandler { NSLog(@"challenge = %@",challenge.protectionSpace.serverTrust); //判断是否是信任服务器证书 if (challenge.protectionSpace.authenticationMethod ==NSURLAuthenticationMethodServerTrust) { //创建一个凭据对象 NSURLCredential *credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]; //告诉服务器客户端信任证书 [challenge.sender useCredential:credential forAuthenticationChallenge:challenge]; } } /** * 接收到服务器返回的数据时调用 */ - (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data { NSLog(@"接收到的数据%zd",data.length); [self.dataM appendData:data]; } /** * 请求完毕 */ - (void)connectionDidFinishLoading:(NSURLConnection *)connection { NSString *html = [[NSString alloc]initWithData:self.dataM encoding:NSUTF8StringEncoding]; NSLog(@"请求完毕"); [self.webView loadHTMLString:html baseURL:self.url]; } ~~~ 懒加载: ~~~ - (NSMutableData *)dataM { if (_dataM == nil) { _dataM = [[NSMutableData alloc]init]; } return _dataM; } ~~~ 至此,我们已经实现了https数据的展示: **注意:** NSURLSession/NSURLConnection HTTP load failed (kCFStreamErrorDomainSSL, -9843) 原因:没有信任服务器证书 在下面这个方法中: ~~~ - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential * __nullable credential))completionHandler() ~~~ 我们通过protectionSpace.authenticationMethod判断是否信任服务器证书 - NSURLSessionAuthChallengeUseCredential = 0, 使用凭据 ,信任服务器证书 - NSURLSessionAuthChallengePerformDefaultHandling = 1, 默认处理,忽略服务器证书 - NSURLSessionAuthChallengeCancelAuthenticationChallenge = 2, 整个请求被取消 凭据被忽略 - NSURLSessionAuthChallengeRejectProtectionSpace = 3, 本次拒绝,下次重试
';

一步一步 搞定RSA(公钥、私钥)

最后更新于:2022-04-01 14:25:26

首先我们要会生成RSA密钥文件,现在一步步的来给大家展示一下,如何生成我们所需的公钥和私钥文件: **RSA密钥生成过程** 生成私钥文件 $ openssl genrsa -out private.pem 1024 openssl:是一个自由的软件组织,专注做加密和解密的框架。 genrsa:指定了生成了算法使用RSA out:后面的参数表示生成的key的输入文件 1024:表示的是生成key的长度,单位字节(bits) 创建证书请求 $ openssl req -new -key private.pem -out rsacert.csr 可以拿着这个文件去数字证书颁发机构(即CA)申请一个数字证书。CA会给你一个新的文件cacert.pem,那才是你的数字证书。(要收费的) 生成证书并签名,有效期10年 $ openssl x509 -req -days 3650 -in rsacert.csr -signkey private.pem -out rsacert.crt 509是一种非常通用的证书格式。 将用上面生成的密钥privkey.pem和rsacert.csr证书请求文件生成一个数字证书rsacert.crt。这个就是公钥 转换格式 将 PEM 格式文件 转换成 DER 格式 $ openssl x509 -outform der -in rsacert.crt -out rsacert.der 在 iOS开发中,公钥是不能使用base64编码的,上面的命令是将公钥的base64编码字符串转换成二进制数据 导出 P12 文件 在iOS使用私钥不能直接使用,需要导出一个p12文件。下面命令就是将私钥文件导出为p12文件。 $ openssl pkcs12 -export -out p.p12 -inkey private.pem -in rsacert.crt 执行完上面的这些,我们现在就得到了四个文件 ![RSA](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-04-07_570611036d796.jpg "") 那么接下来,我们用这两个文件来使用一下(小点点的两个文件即可)。 ~~~ - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event { //创建加密对象 CryptorTools *tool = [[CryptorTools alloc]init]; //要加密的内容 NSString *msg = @"i love you"; //加载公钥 NSString *pubPath = [[NSBundle mainBundle] pathForResource:@"rsacert.der" ofType:nil]; [tool loadPublicKeyWithFilePath:pubPath]; //使用公钥加密 NSString *result = [tool RSAEncryptString:msg]; NSLog(@"加密 = %@",result); //解密 //加载私钥 //密码是导出p12密码 NSString *privatePath = [[NSBundle mainBundle] pathForResource:@"p.p12" ofType:nil]; [tool loadPrivateKey:privatePath password:@"123456 "]; //使用私钥解密 NSString *result2 = [tool RSADecryptString:result]; NSLog(@"解密 = %@",result2); } ~~~ 看一下结果: ![RSA加密结果](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-04-07_570611037dfe9.jpg "") 搞定。
';

谈谈:服务器返回的数据,该怎么接收(int,NSNumber)

最后更新于:2022-04-01 14:25:23

其实很多时候,当服务器返回的是这种数据的时候: ![int NSNumbser](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-04-07_57061102ccbf2.jpg "") 那我们用int的来接收一下,看有没有问题: ![int](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-04-07_57061102e0b77.jpg "") 是不是一点问题都没有,那么用NSNumber 呢: ![NSNumber](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-04-07_570611030cc51.jpg "") 一样没有问题。但是关键的来了,若返回的是null .看一下: ![null](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-04-07_5706110327c2e.jpg "") 服务器返回什么,我们是决定不了的对吧,不能说,哎,你不能给我返回null。你看有人理你吗? 看一下int接收 有没有问题: ![崩溃](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-04-07_570611033849b.jpg "") 噢NO。崩了。 我们立马来看看NSNumber: ![NSNumber](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-04-07_5706110352c12.jpg "") 奇迹出来了。没有问题。所以呀,我们在处理这些问题的时候,要注意这点了。 这个控制器里面的代码,我们在[你可能不知道的事(服务器返回 id)](http://blog.csdn.net/yi_zz32/article/details/50065797) 这里有写。我就不写在这里了。
';

你可能不知道的事(服务器返回 id)

最后更新于:2022-04-01 14:25:21

首先说一下id的问题 加入服务器就是给我们反馈了一个id:如下: ![id](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-04-07_57061102a4860.jpg "") 既然返回的是id,有些人就乱了阵脚,“这个是关键字,怎么来接?” 其实没事,他返回什么给我们,我们就拿什么来接,就行。看一下如何解决: 既然返回的是字典,那么久字典转模型,写个模型先: 模型.h ~~~ @interface ZYDemo : NSObject @property(nonatomic,assign)int id; @property(nonatomic,copy)NSString *message; - (instancetype)initWithDict:(NSDictionary *)dict; + (instancetype)demoWithDict:(NSDictionary *)dict; @end ~~~ 模型.m ~~~ @implementation ZYDemo - (instancetype)initWithDict:(NSDictionary *)dict { if (self = [super init]) { [self setValuesForKeysWithDictionary:dict]; } return self; } + (instancetype)demoWithDict:(NSDictionary *)dict { return [[self alloc]initWithDict:dict]; } @end ~~~ 在viewControllder进行数据解析 ~~~ - (void)viewDidLoad { [super viewDidLoad]; NSURL *url = [NSURL URLWithString:@"http://localhost/demo.json"]; NSURLRequest *request1 = [NSURLRequest requestWithURL:url]; NSURLRequest *request = [NSURLRequest requestWithURL:url cachePolicy:1 timeoutInterval:10.0]; [NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse * _Nullable response, NSData * _Nullable data, NSError * _Nullable connectionError) { NSDictionary *result = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil]; ZYDemo *demo = [ZYDemo demoWithDict:result]; NSLog(@"%@",demo); }]; } ~~~ 看一下我们的模型是否出来了: ![id](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-04-07_57061102b4fde.jpg "") 看,我们一样能解决,不要去为难服务器的人
';

解析XML

最后更新于:2022-04-01 14:25:19

由于我们搭建好了Apache服务器,那么我们接下来看一下如何解析服务器返回的xml文件 虽然开发中,服务器返回的xml格式的数据较少,但是偶尔还是会有的。 由于解析xml没有比较好的第三方框架,所以我们还是乖乖的苦逼的写代码,虽然说,代码没有难度 xml数据: ~~~ <videos> <video videoId="1"> <name>张三</name> <teacher>张老师</teacher> </video> </videos> ~~~ 首先从服务器获取回来数据先 ~~~ NSURL *url = [NSURL URLWithString:@"http://localhost/videos.xml"]; NSURLRequest *request = [NSURLRequest requestWithURL:url cachePolicy:1 timeoutInterval:10.0]; [NSURLConnection sendAsynchronousRequest:request queue:[[NSOperationQueue alloc]init] completionHandler:^(NSURLResponse * _Nullable response, NSData * _Nullable data, NSError * _Nullable connectionError) { //创建xml解析器 NSXMLParser *parser = [[NSXMLParser alloc]initWithData:data]; //设置代理 parser.delegate = self; //开始解析 [parser parse]; }]; ~~~ 我们依次的看一下我们要用到的代理方法 **开始解析调用(只会调用一次)** ~~~ - (void)parserDidStartDocument:(NSXMLParser *)parser { NSLog(@"1.开始文档"); } ~~~ **每发现一个开始节点就调用** ~~~ /** * 每发现一个节点就调用 * * @param parser 解析器 * @param elementName 节点名字 * @param attributeDict 属性字典 */ * (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(nullable NSString *)namespaceURI qualifiedName:(nullable NSString *)qName attributes:(NSDictionary<NSString *, NSString *> *)attributeDict { NSLog(@"2.发现节点:%@",elementName); if ([elementName isEqualToString:@"video"]) { //创建模型对象 self.video = [[ZYVideo alloc]init]; } [self.elementNameString setString:@""]; } ~~~ **发现节点内容** ~~~ - (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string { NSLog(@"3.发现节点内容:%@",string); //把发现的内容进行拼接 [self.elementNameString appendString:string]; } ~~~ **发现结束节点** ~~~ - (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(nullable NSString *)namespaceURI qualifiedName:(nullable NSString *)qName { NSLog(@"3.发现结束节点 %@",elementName); // NSLog(@"拼接的内容%@",self.elementNameString); if ([elementName isEqualToString:@"name"]) { self.video.name = self.elementNameString; }else if ([elementName isEqualToString:@"teacher"]) { self.video.teacher = self.elementNameString; } } ~~~ **解析完毕调用** ~~~ - (void)parserDidEndDocument:(NSXMLParser *)parser { NSLog(@"解析完毕---------"); NSLog(@"%@",self.video); } ~~~ 提前做好的懒加载: ~~~ #pragma mark - 懒加载 - (NSMutableString *)elementNameString { if (_elementNameString == nil) { _elementNameString = [[NSMutableString alloc]init]; } return _elementNameString; } ~~~ 搞定:看结果: ![解析XML](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-04-07_5706110286e08.jpg "")
';

图文讲解如何搭建Apache服务器

最后更新于:2022-04-01 14:25:17

如果我们能在本地搭建开发用的网络测试环境,能更有优势 步骤1:手动创建1个文件夹(文件夹名不限) ![步骤1](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-04-07_570611023bbe5.jpg "") 步骤2:切换工作目录 cd /etc/apache2 ![步骤2](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-04-07_570611024be0d.jpg "") 步骤3: 备份文件,以防不测,只需要执行一次就可以了 命令行代码:sudo cp httpd.conf httpd.conf.bak 步骤4:提示:如果后续操作出现错误!可以使用以下命令,恢复备份过的 httpd.conf 文件 命令行代码:sudo cp httpd.conf.bak httpd.conf 步骤5:vim里面只能用键盘,不能用鼠标 用vim编辑httpd.conf 命令行代码:sudo vim httpd.conf 步骤6:查找DocumentRoot 命令行代码:/DocumentRoot 将光标移动到首行 (按0)即可 进入编辑模式:(按 i)即可 修改下面的两个目录: ![步骤6](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-04-07_570611026219a.jpg "") 再修改这个地方: ![http](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-04-07_570611027192e.jpg "") 步骤7:ESC退出编辑 步骤8:查找php 命令行代码:/php 步骤9:删除行首注释,(按0到行首,按x 删除) 步骤10:wq 保存退出 (:q!)不保存退出 步骤11:切换工作目录 命令行:cd /etc 步骤12:拷贝php.ini文件 命令行: sudo cp php.ini.default php.ini 步骤13:重新启动apache服务器 命令行:sudo apachectl -k restart 大功告成:在浏览器输入:localhost 会有意想不到的结果。 然后我们把需要的文件 放到我们创建好的Sites,然后刷新浏览器 搞定。
';

解决Xcode 不能 访问http的问题

最后更新于:2022-04-01 14:25:14

由于Xcode默认不支持http的直接访问,那么我们就需要配置一下,我们先看一下没配置之前的Xcode返回的错误信息 ![http不能访问](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-04-07_57061101c5965.jpg "") 发现错误信息,首先,不要慌张,现在出现错误,比到客户手上再出错,好得多。废话不多说,搞定他 步骤1: ![步骤1](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-04-07_57061101dba45.jpg "") 步骤2: ![步骤2](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-04-07_57061102053ee.jpg "") 步骤3: ~~~ <key>NSAppTransportSecurity</key> <dict> <key>NSAllowsArbitraryLoads</key> <true/> </dict> ~~~ 插入这么一段代码,搞定。 可能这段代码,不任意记住但是没有必要记住,封装成一段代码,需要用的适合,输入关键字就出来了。看一下效果: ![效果](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-04-07_570611021e607.jpg "")
';

NSURLConnection 详解

最后更新于:2022-04-01 14:25:12

首先我们来创建一个URL ~~~ NSURL *url = [NSURL URLWithString:@"http://m.baidu.com"]; ~~~ m:mobile专门给手机提供访问的连接 **创建请求对象,根据url向服务器索要数据** ~~~ NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url cachePolicy:NSURLRequestReloadIgnoringLocalCacheData timeoutInterval:15]; ~~~ - 这个方法里面,我们要做的事情: - 通过NSMutableURLRequest告诉服务器一些额外的信息 - timeoutInterval:请求超时时长,在指定的时间内,如果没有得到服务器的响应,则认为请求是失败的 - 默认是60s 但是建议在15~30s之间 - cachePolicy 缓存策略 - NSURLRequestUseProtocolCachePolicy = 0, 默认的策略 - NSURLRequestReloadIgnoringLocalCacheData = 1,每次从服务器加载,忽略本地缓存。 - 一般使用在实时性要求很高的应用,股票/12306/ - 下面两个一般使用在开发离线版应用。 - 离线版应用一般需要两个数据库,一个是本地数据库Sqlite3,一个服务器数据库。 - NSURLRequestReturnCacheDataElseLoad = 2, 有缓存,就返回缓存数据,没有就从服务器加载。 - NSURLRequestReturnCacheDataDontLoad = 3, 有缓存,就返回缓存数据,没有就不加载 - 告诉服务器,我是iPhone 并且支持Apple的网页套件 ~~~ [request setValue:@"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_1) AppleWebKit/601.2.7 (KHTML, like Gecko) Version/9.0.1 Safari/601.2.7" forHTTPHeaderField:@"User-Agent"]; ~~~ - 将请求对象发送给服务器–(网络访问都是耗时操作,使用异步) - sendAsynchronousRequest:本身是异步,NSURLConnection内部会开启一条线程进行网络访问 - queue:决定了completionHandler回调所在的线程 - **如何选择队列** - 如果获得服务器响应的时候,要做耗时操作,则选择自己创建队列,比如下载一个zip包,解压缩 - 如果获得响应后直接更新UI,则选择主队列。 completionHandler:服务器响应客户端的回调。 **response** 本质是NSHTTPURLResponse - statusCode:状态码,可以根据这个值判断是否请求出错。 - allHeaderFields:获得响应体 - URL:一般使用在重定向,如果不需要重定向,响应的url和请求的url是一样的。 - MIMEType:服务器告诉客户端返回的数据类型,并决定客户端使用什么软件查看内容 - textEncodingName : 服务器告诉客户端返回内容的编码格式 **下面两个属性一般使用在开发下载功能** - expectedContentLength:服务器返回数据的长度,客户端可以通过该属性获得文件大 - suggestedFilename:服务器建议客户端保存文件使用的名字 如下: ~~~ [NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse * _Nullable response, NSData * _Nullable data, NSError * _Nullable connectionError) { if(connectionError != nil || data.length == 0) { NSLog(@"你的网络不给力哦"); return; } NSString *html = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; ~~~ **加载html** ~~~ [self.webView loadData:data MIMEType:response.MIMEType textEncodingName:response.textEncodingName baseURL:url]; ~~~
';

Socket的创建和连接

最后更新于:2022-04-01 14:25:10

从socket的创建开始说起(连接到京东。。。) 我们只要理解了各个参数,那么就学会了创建和连接 ~~~ //连接到京东 - (void)connection{ if ([self connectionToHost:@"111.13.28.23" port:80]) { // 发送数据 NSString *request = @"GET / HTTP/1.1\r\n" "Host:m.jd.com\r\n" "User-Agent:iPhone AppleWebKit\r\n" "Connection:Close\r\n\r\n"; NSString *result = [self sendAndRecv:request]; NSLog(@"接收到的内容result = %@",result); // 查找\r\n\r\n NSRange range = [result rangeOfString:@"\r\n\r\n"]; if (range.location != NSNotFound) { // 找到\r\n\r\n NSString *html = [result substringFromIndex:range.location]; NSLog(@"接收到的内容html = %@",html); [self.webView loadHTMLString:html baseURL:[NSURL URLWithString:@"http://m.jd.com"]]; } } } ~~~ **连接到服务器** ~~~ - (BOOL)connectionToHost:(NSString *)host port:(int)port{ // 创建客户端socket /** 参数 domain: 协议域/协议族,AF_INET(IPV4的网络开发) type: Socket 类型,SOCK_STREAM(TCP)/SOCK_DGRAM(UDP,报文) protocol: IPPROTO_TCP,协议,如果输入0,可以根据第二个参数自动选择协议 返回值 socket,如果>0 就表示成功 */ self.clientSocket = socket(AF_INET, SOCK_STREAM, 0); // 建立连接 /** 参数 1> 客户端socket 2> 指向数据结构sockaddr的指针,其中包括目的端口和IP地址。即服务器的“结构体”地址 3> 结构体数据长度 返回值 0 成功/其他 错误代号,非0即真 */ struct sockaddr_in socketServer; // 1.协议---> 确定如何传输数据 socketServer.sin_family = AF_INET; // 2.ip --> 通过ip找主机 inet_addr内部对字符串也进行了字节翻转(高低位互换) socketServer.sin_addr.s_addr = inet_addr(host.UTF8String); // 3.端口 ---> 通过端口找程序 htons:对整数进行高低位换号 socketServer.sin_port = htons(port); return (connect(self.clientSocket, (const struct sockaddr *)&socketServer, sizeof(socketServer)) == 0); } ~~~ **发送和接收** ~~~ - (NSString *)sendAndRecv:(NSString *)msg{ // 发送数据 /** 参数 1> 客户端socket 2> 发送内容地址 void * == id 3> 发送内容长度,是指字节的长度。 4> 发送方式标志,一般为0 返回值 如果成功,则返回发送的字节数,失败则返回SOCKET_ERROR */ ssize_t sendLength = send(self.clientSocket, msg.UTF8String, strlen(msg.UTF8String), 0); NSLog(@"发送了%ld字节,%zd--%zd",sendLength,msg.length,strlen(msg.UTF8String)); // 读取数据 /* 参数1:客户端的socket 参数2:接收内容的地址 参数3:接收内容的长度,告诉服务器一次只能接收多少字节的内容 参数4:接收方式标志,一般为0 表示阻塞式等待服务器响应 返回 — 接收的字节数 */ uint8_t buffer[1024]; NSMutableData *dataM = [NSMutableData data]; ssize_t recvLength = -1; while (recvLength != 0) { recvLength = recv(self.clientSocket, buffer, sizeof(buffer), 0); // 2000 80 NSLog(@"接收了%ld 字节",recvLength); [dataM appendBytes:buffer length:recvLength]; } NSString *resultStr = [[NSString alloc] initWithData:dataM encoding:NSUTF8StringEncoding]; return resultStr; } ~~~ **断开连接** ~~~ - (void)disconnection{ // 断开连接 close(self.clientSocket); } ~~~ 其实也不难 对吧 **效果:** ![Socket](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-04-07_570611019701c.jpg "")
';

前言

最后更新于:2022-04-01 14:25:08

> 原文出处:[IOS 网络编程](http://blog.csdn.net/column/details/ios-data.html) 作者:[yi_zz32](http://blog.csdn.net/yi_zz32) **本系列文章经作者授权在看云整理发布,未经作者允许,请勿转载!** # IOS 网络编程 > IOS 网络编程 详细的讲解各种数据的解析
';