一、网络基础知识介绍
1、为什么要学习网络编程?
在移动互联网时代,移动应用的特征有
几乎所有应用l都需要用到网络,比如QQ、微博、网易新闻、优酷、百度地图
只有通过网络跟外界进行数据交互、数据更新,应用才能保持新鲜、活力
如果没有了网络,也就缺少了数据变化,无论外观多么华丽,终将变成一潭死水
移动网络应用 = 良好的UI + 良好的用户体验 + 实时更新的数据
新闻:网易新闻、新浪新闻、搜狐新闻、腾讯新闻
视频:优酷、百度视频、搜狐视频、爱奇艺视频
音乐:QQ音乐、百度音乐、酷狗音乐、酷我音乐
LBS:百度地图、高德地图、大众点评、墨迹天气、滴滴打车
电商:淘宝、京东商城、天猫、蘑菇街、凡客诚品、美丽说
社交:QQ、微信、微博、陌陌、比邻
为什么要学习网络编程
网络编程是一种实时更新应用数据的常用手段
网络编程是开发优秀网络应用的前提和基础
2、网络编程示例
PC互联网和移动互联网
移动互联网,就是将移动通信和互联网二者结合起来,成为一体。是指互联网的技术、平台、商业模式和应用与移动通信技术结合并实践的活动的总称。
PC互联网:即传统的互联网,将计算机网络互相联接在一起的方法可称作“网络互联”,在这基础上发展出覆盖全世界的全球性互联网络称互联网。
PC互联网和移动互联网的区别:
字面理解:一个是移动的互联网,一个是固定的互联网,这是相对而言的。
终端设备的区别:移动互联网主要有智能手机,而PC互联网主要是计算机。
形态:移动互联网则釆用客户端APP结构加通信录为主,PC互联网采用B/S结构为主,也就是浏览器、服务器结构。
3、基本概念
- 在网络编程中,有几个必须掌握的基本概念
- 客户端(Client):移动应用(iOS、android等应用)
- 服务器(Server):为客户端提供服务、提供数据、提供资源的机器
- 请求(Request):客户端向服务器索取数据的一种行为
- 响应(Response):服务器对客户端的请求做出的反应,一般指返回数据给客户端
作为移动开发工程师,主要的精力都是放在前端开发
4、服务器分类
-
按照软件开发阶段来分,服务器可以大致分为2种
-
远程服务器
别名:外网服务器、正式服务器
使用阶段:应用上线后使用的服务器
使用人群:供全体用户使用
速度:取决于服务器的性能、用户的网速 -
本地服务器
别名:内网服务器、测试服务器
使用阶段:应用处于开发、测试阶段使用的服务器
使用人群:仅供公司内部的开发人员、测试人员使用
速度:由于是局域网,所以速度飞快,有助于提高开发测试效率
-
-
如果处于学习、开发阶段,自己搭建一个本地服务器即可
选择怎样的本地服务器
在公司开发阶段,已经有专门的后端人员开发服务器程序,不由得你选择
在学习阶段,选择什么本地服务器都可以,能用就行
在我们课程中,我们选择Apache服务器
二、HTTP 介绍
1、URL和常见协议
- 什么是URL
- URL的全称是Uniform Resource Locator(统一资源定位符)
- 通过1个URL,能找到互联网上唯一的1个资源
- URL就是资源的地址、位置,互联网上的每个资源都有一个唯一的URL
- URL中常见的协议头
-
HTTP
超文本传输协议,访问的是远程的网络资源,格式是http://
http协议是在网络开发中最常用的协议 -
file
访问的是本地计算机上的资源,格式是file:///(不用加主机地址) -
mailto
访问的是电子邮件地址,格式是mailto:// -
FTP
访问的是共享主机的文件资源,格式是ftp://
-
2、HTTP协议简介
-
HTTP 简介
-
HTTP协议,中文名:超文本传输协议(HyperText Transfer Protocol)是互联网上应用最为广泛的一种网络协议。
-
HTTP最开始是专门用来传输 HTML。
HMTL中文名:超文本标记语言。随着互联网时代的发展,HTTP可以用来传输任何数据类型。 -
HTTP 是一种请求/响应式的协议。一个客户端与服务器建立连接后,发送一个请求给服务器;服务器接到请求后,给予相应的响应信息。
-
-
思考?
客户端该传什么格式的数据给服务器?服务器才能看懂
服务器该返回什么格式的数据给客户端?客户端才能看懂
两边要怎样传输数据才能有效沟通?
3、HTTP协议的作用
- 规定客户端和服务器之间的数据传输格式
- 让客户端和服务器能有效地进行数据沟通
4、HTTP协议的特点
-
简单方便快速
因为HTTP协议比较简单,所以HTTP服务器的程序规模相对比较小,从而使得通信的速度非常快。 -
灵活
Http协议允许客户端和服务器端传输任意类型任意格式的数据对象。 -
HTTP 0.9和1.0使用非持续连接
非持续连接的含义是每次建立的连接只处理一个客户端请求。
当服务器处理完客户端的请求之后,并且收到客户的反馈应答后,服务器端立即断开连接。采用这种通信方式可以大大的节省传输时间。 -
HTTP 1.1使用持续连接
不必为每个web对象创建一个新的连接,一个连接可以传送多个对象 -
面向无状态
Http是无状态的协议。所谓的无状态是指协议对于请求的处理没有记忆功能。无状态意味着如果要再次处理先前的信息,则这些先前的信息必须要重传,这就导致了数据量传输的增加。 -
非持续连接和持续连接的区别?
-
非持续连接是指启动一次TCP连接服务器就向客户端传送一个对象。
-
持续连接是指服务器可在相同的TCP连接上向客户端发送多个对象。HTTP/1.0的默认设置是非持续连接,而HTTP/1.1的默认设置是持续连接。
-
举个例子
在使用HTTP/1.0的情况下,如果打开一个包含一个HTML文件和10个内联图象对象的网页时,HTTP就要建立11次TCP连接才能把文件从服务器传送到客户端。而使用HTTP/1.1的情况下,如果打开同样的文件时,HTTP建立一次TCP连接就可把文件从服务器传送到客户端。
-
5、HTTP的基本通信过程
三、网络开发解决方案
1、苹果原生方案
-
1、NSURLConnection
- iOS2.0出现,用法简单,最古老最经典最直接的一种方案。
- 在iOS5.0之前,使用起来相对繁琐,一大堆的代理方法。
- 在iOS5.0之后又作了一层改进,提供了两个非常方便的方法,使发送网络请求 * 变得更简单。基本一句代码就能解决简单的网络处理了。
-
2、NSURLSession
- iOS7新出的技术,与 NSURLConnection 是并列的。
- 功能比 NSURLConnection 更加强大,性能也比 NSURLConnection 好很多。
- 当程序在前台时,NSURLSession和NSURLConnection大部分可以互相替代,但NSURLSession支持后台网络操作,除非用户强行关闭。
- 苹果官方推荐使用 NSURLSession 代替 NSURLConnection。
- NSURLSession提供的功能:
- 通过 URL 将数据下载到内存
- 通过 URL 将数据下载到文件系统
- 将数据上传到指定的URL
- 在后台完成上述功能
- 支持下载,断点续传,后台上传/下载,后台上传/下载任务跟进
- 3、CFNetwork:NSURL*的底层,纯C语言,开发中几乎不会用到。
2、第三方框架
-
1、ASIHttpRequest
- 外号"HTTP终结者",功能极其强大,几乎没有它考虑不到的问题。
- 可惜2012年10月就停止更新,且是MRC环境。
- 底层是CFNetwork。
-
2、AFNetworking
- 简单易用,提供了基本够用的常用功能,维护和使用者多。
- 使用它AFNetworking,程序员可以不用知道什么URL,什么是线程,什么是线程间的通信。
- 底层是 NSURLSession 和 NSURLConnection。
-
3、MKNetworkKit
- 简单易用,产自三哥的故乡印度,维护和使用者少。
- 底层是NSURLConnection
建议:为了提高开发效率,企业开发用的基本是第三方框架
- 示例:
// ViewController.m
// 01-NSURLConnection访问百度
#import "ViewController.h"
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
// URL : 确定要访问的资源路径
// m:mobile 一般大公司会提供专门给手机上使用的网址
NSURL *url = [NSURL URLWithString:@"http://m.baidu.com"];
// NSURLRequest: 根据URL创建请求对象
NSURLRequest *request = [NSURLRequest requestWithURL:url];
//NSURLConnection:发送请求 向服务器索要数据
[NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse * _Nullable response, NSData * _Nullable data, NSError * _Nullable connectionError) {
// 将二进制数据转换成字符串
// 一般在实际开发中,如果没有特殊要求,编码方式统一使用UTF8
// UNICODE一种编码方式 UTF8使用1到4个字节表示一个文字. 一个汉字占3个字节来表示. 大约43亿 能够表示完全世界的文字.
// GBK:国标 1980年颁发的汉字库 6700+个文字 中国现在大约有100000+
NSString *result = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSLog(@"%zd--%@",data.length,result);
}];
}
@end
用 webView显示
// ViewController.m
// 02-NSURLConnection显示百度-(掌握)
#import "ViewController.h"
@interface ViewController ()
@property (weak, nonatomic) IBOutlet UIWebView *webView;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
[self loadData];
}
- (void)loadData {
// URL : 确定要访问的资源路径
// m:mobile 一般大公司会提供专门给手机上使用的网址
NSURL *url = [NSURL URLWithString:@"http://m.baidu.com"];
// NSURLRequest: 根据URL创建请求对象
NSURLRequest *request = [NSURLRequest requestWithURL:url];
//NSURLConnection:发送请求 向服务器索要数据
[NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse * _Nullable response, NSData * _Nullable data, NSError * _Nullable connectionError) {
// 将二进制数据转换成字符串
// 一般在实际开发中,如果没有特殊要求,编码方式统一使用UTF8
// UNICODE一种编码方式 UTF8使用1到4个字节表示一个文字. 一个汉字占3个字节来表示. 大约43亿 能够表示完全世界的文字.
// GBK:国标 1980年颁发的汉字库 6700+个文字 中国现在大约有100000+
NSString *result = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
// 显示界面
//baseURL:加载资源的参照路径
[self.webView loadHTMLString:result baseURL:url];
NSLog(@"%zd--%@",data.length,result);
}];
}
@end
// ViewController.m
// 03-请求参数详解和缓存策略以及超时时长-(掌握)
#import "ViewController.h"
@interface ViewController ()
@property (weak, nonatomic) IBOutlet UIWebView *webView;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
[self loadData];
}
- (void)loadData {
// URL : 确定要访问的资源路径
// m:mobile 一般大公司会提供专门给手机上使用的网址
NSURL *url = [NSURL URLWithString:@"http://m.baidu.com/"];
// NSURLRequest: 根据URL创建请求对象
// User-Agent 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
/*
cachePolicy 缓存策略
NSURLRequestUseProtocolCachePolicy = 0, 默认的缓存策略
一般对实时性要求比较高的应用,比如12306,彩票,股票...
NSURLRequestReloadIgnoringLocalCacheData = 1, 忽略本地缓存数据,直接从服务器上拿数据.
一般使用在做离线开发
本地数据库,sqlite3数据库.
NSURLRequestReturnCacheDataElseLoad = 2, 有缓存就返回缓存数据,没有就加载.
NSURLRequestReturnCacheDataDontLoad = 3, 有缓存就返回缓存数据,否则不加载.
timeoutInterval:请求超时时长. 在指定的时间内,如果还没有获得服务器响应.则认为本次是失败的.
- 默认超时时长是60s 建议不要太快,也不要太慢 一般建议到15s-30s
*/
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url cachePolicy:NSURLRequestReloadIgnoringLocalCacheData timeoutInterval:15];
// 通过request对象告诉服务器一些额外的信息
// 通过请求头告诉服务器我是xxx
// [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"];
// 告诉服务器我是iphone 并且支持苹果的网页套件
[request setValue:@"iPhoneWebKit" forHTTPHeaderField:@"User-Agent"];
//NSURLConnection:发送请求 向服务器索要数据
/**
sendAsynchronousRequest:方法内部会自动创建一个线程来做异步请求.
completionHandler:当接收到服务器响应时调用.
queue:决定completionHandler在主线程还是在子线程执行.
如何选择队列?
- 如果获得服务器响应之后直接更新UI,则选择主队列.
- 如果获得响应后,要做耗时操作,则选择创建队列.比如下载zip文件,解压缩
重定向:
response: 本质是NSHTTPURLResponse对象
- URL: 响应的URL,一般使用在有重定向的界面,如果没有重定向,则请求URL和响应URL是一样的
- MIMEType: 服务器告诉客户端返回的数据类型是什么.
- Content-Type: 等价于MIMEType
- textEncodingName: 服务器告诉客户端返回内容编码方式
- allHeaderFields: 响应头信息.服务器返回给客户端的额外信息
// 下面三个必须记住
--- 下面两个属性一般使用在开发下载功能
- expectedContentLength: 服务器告诉客户端返回数据的长度.
- suggestedFilename: 服务器建议客户端保存文件使用的文件名
- statusCode : 状态码
*/
[NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse * _Nullable response, NSData * _Nullable data, NSError * _Nullable connectionError) {
//将父类response的强转为子类型才能点得出allHeaderFields
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
NSLog(@"%@--",httpResponse.allHeaderFields);
// 在实际开发中一定要对错误进行处理,因为所有的网络请求都有可能出错. 比如没有流量,没有信号,手机没钱
// connectionError如果不为nil,则表示有错了,提示用户出错了,但不要提示得太专业.
// 404表示请求的资源不存在
// data:服务器返回的二进制数据
// 客户端和服务器之间传输数据都是二进制数据.
if(connectionError != nil || data.length == 0) {
NSLog(@"亲.你的网络不给力喔");
return;
}
[self.webView loadRequest:[NSURLRequest requestWithURL:response.URL]];
// 将二进制数据转换成字符串
// 一般在实际开发中,如果没有特殊要求,编码方式统一使用UTF8
// UNICODE一种编码方式 UTF8使用1到4个字节表示一个文字. 一个汉字占3个字节来表示. 大约43亿 能够表示完全世界的文字.
// GBK:国标 1980年颁发的汉字库 6700+个文字 中国现在大约有100000+
// NSString *result = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
// // 显示界面
// //baseURL:加载资源的参照路径
// [self.webView loadHTMLString:result baseURL:url];
// NSLog(@"%zd--%@",data.length,result);
}];
}
@end
重定向—用苹果浏览器打开到mac系统页面是由于浏览器的用户代理-User-Agent会告诉服务器是用什么类型的系统
四、NSURLConnection常用方法
使用步骤
- NSURL:确定要访问的资源
- NSURLRequest:根据URL 建立请求,向服务器索要数据
- NSURLConnection:建立网络连接,将请求异步发送给服务器
1、代码演示--访问百度
- 1、代码实现
// 1、NSURL :确定要访问的资源路径
// m:mobile 代表手机上访问的路径,一般大公司都会提供一个专门手机上访问的路径
NSURL *url = [NSURL URLWithString:@"http://m.baidu.com/"];
// 2、NSURLRequest:根据 url 创建请求对象,向服务器索要数据
NSURLRequest *request = [NSURLRequest requestWithURL:url];
// 3、建立网络链接,将请求(同步或异步)发送给服务器
[NSURLConnection sendAsynchronousRequest:reuqest queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
/*
编码:在日常开发中,如果没有特殊原因,编码统一使用‘UTF8’
- UTF8 :是 UNICODE的一种编码方式,使用 1到4个字节表述一个字符,能够表述全世界的所有的字符
- GB2312:国标,1980年颁布的汉字库,6700+汉字,只有一些老的网站还在使用
现在中文汉字大约:100000+
*/
NSString *html = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSLog(@"%@",html);
}];
-
2、sendAsynchronousRequest方法和参数解释:
- sendAsynchronousRequest:是异步的,NSURLConnection会开启一条线程,发送网络请求。
- queue:队列-->负责调度/执行completionHandler的队列。
- completionHandler:完成请求的回调block。表示收到服务器的响应数据。
如何选择队列?
如果接收到数据后,需要直接更新UI,选择主队列。
如果要做耗时操作,比如下载一个zip文件,下载完要进行解压缩操作,选择新建一个队列。 -
3、总结步骤
- 创建NSURL对象,确定要访问的资源路径。
- 根据url创建请求对象NSURLRequest,向服务器索要数据。
- 建立网络链接,将请求对象(同步或异步)发送给服务器。
2、UIWebView显示百度首页
开发中服务器返回的都是字符串数据,下面利用UIWebView加载请求百度返回的字符串---显示百度首页。
- 1、代码实现如下:
// 1、NSURL :确定要访问的资源路径
NSURL *url = [NSURL URLWithString:@"http://m.baidu.com/"];
// 2、NSURLRequest:根据url创建请求对象,向服务器索要数据
NSMutableURLRequest *reuqest = [NSMutableURLRequest requestWithURL:url];
// 2.1、告诉服务器额外的信息
// iPhone: 告诉服务器我是 iPhone
// iPhone AppleWebKit":告诉服务器我是 iPhone,并且要支持苹果的网页套件
[reuqest setValue:@"iPhone AppleWebKit" forHTTPHeaderField:@"User-Agent"];
// 3、建立网络链接,将请求(同步或异步)发送给服务器
[NSURLConnection sendAsynchronousRequest:reuqest queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
// 将二进制数据转换成字符串
NSString *html = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
// baseURL:加载资源的参照路径。
[self.webView loadHTMLString:html baseURL:url];
}];
- 2、NSMutableURLRequest对象的作用
- 根据url创建请求对象,向服务器索要数据。
- 告诉服务器额外的信息。在网络访问过程中,绝大多数的变化都是通过request对象设置。
*设置请求头和请求体
*身份验证,Cookie,浏览器类型,超时时长等。
3、服务器响应演练
- 1、加载数据
- (void)loadData{
// 1. 创建 url
NSURL *url = [NSURL URLWithString:@"http://pinyin.sogou.com/"];
// 2. 创建请求对象
// NSURLRequest *request = [NSURLRequest requestWithURL:url];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
[request setValue:@"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2272.118 Safari/537.36" forHTTPHeaderField:@"User-Agent"];
// 3. 发送异步请求到服务器(所有的网络请求都是耗时操作,因此绝大多数都是异步请求)
[NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
NSLog(@"%@---%@--%@---%@----%@",response,response.MIMEType,response.URL,response.textEncodingName,response.suggestedFilename);
[self.webView loadData:data MIMEType:response.MIMEType textEncodingName:response.textEncodingName baseURL:url];
}];
}
参数解释:
[NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse * _Nullable response, NSData * _Nullable data, NSError * _Nullable connectionError) {
* response:本质上就是NSHTTPURLResponse从服务器上获得的响应。
* NSURLResponse相关属性:
* URL:响应url,一般用来重定向使用。浏览器演示什么是重定向,百度搜索 搜狗输入法。(没有重定向的情况下,响应url和请求url是相同)
* MIMEType:请求数据的数据类型,服务器告诉客户端的返回的数据是什么类型。
* Content-Type等价MIMEType,客户端根据MIMEType决定用什么来打开或显示返回的数据。
* (典型的例子就是浏览器装flash插件的例子。当在浏览器中看视频的时候,浏览器都会提示要安装flash插件,否则无法观看,浏览器就是通过MIMEType来判断观看视频需要flash插件的支持。)
* textEncodingName:返回内容的编码方式
* 以下两个属性通常用于开发下载
* expectedContentLength:请求二进制数据的长度,下载文件的大小。
* suggestedFilename:建议保存的文件名,服务器建议下载文件保存时使用的文件名,一般mac下下载文件不需要填写保存的文件名,就是因为浏览器客户端根据服务器返回的该字段作为文件名了。
- 2、错误处理
在实际开发中,一定要处理错误!任何的网络请求都有可能出错!比如流量没了,网络断了,没信号了 。
// 有时候没有错误,但也没有数据
if (connectionError != nil || data == nil) {
//提示用户最好友善点,不要太专业了。比如提示出现404错误,或500错误了。用户不会理解这些专业术语的。
NSLog(@"你的网络不给力哦!");
return ;
}
- 3、超时时长和缓存策略
-
创建可变请求对象
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url cachePolicy:0 timeoutInterval:15]; -
cachePolicy:表示缓存策略
- NSURLRequestUseProtocolCachePolicy = 0, 默认的缓存策略
- NSURLRequestReloadIgnoringLocalCacheData = 1, 忽略本地缓存数据,一般应用于对数据‘实时性要求高’的应用 每次都从服务器加载数据。应用场景:12306,股票,彩票等
- NSURLRequestReloadIgnoringLocalAndRemoteCacheData = 4,没有实现的缓存策略,可忽略
- NSURLRequestReloadIgnoringCacheData = NSURLRequestReloadIgnoringLocalCacheData,
-
// 以下两项做离线应用
// 在实际开发中,要尽量让用户回到网络上!- NSURLRequestReturnCacheDataElseLoad = 2, 有缓存,就返回缓存数据,否则从服务器中加载。
- NSURLRequestReturnCacheDataDontLoad = 3, 有缓存,就返回缓存数据, 否则不加载。
- NSURLRequestReloadRevalidatingCacheData = 5, 没有实现的缓存策略,可忽略。
-
timeoutInterval:请求的超时时长,在指定的时间内,如果没有收到服务器的反馈,认为请求失败。
- 系统默认60s(SDWebImage 超时时长 15s,AFN 超时时长 60s)
- 建议:不要太短,也不要太长。在15s~30s之间最合适
-
友情提示
如果应用需要通过广告,展现率,点击率,成交率来获得收入的,建议不要做离线缓存,原因主要有两点:- 开发困难且周期长,需要设计两个数据库,一个本地数据库,用来缓存数据,一个远程数据库。
- 广告,展现率,点击率,成交率等操作都是基于网络的,离线不使用网络就无法产生收入。
-
在菜单栏中显示“开发”菜单
五、Socket 编程
1、简介
- Socket 就是为网络服务提供的一种机制。
- 通信的两端都是 Socket。
- 网络通信其实就是 Socket 间的通信。
- 数据在两个 Socket 间通过 IO 传输。
- Socket 是纯 C语言的,是跨平台的。
2、辅助工具---NetCat
- 是终端下用于调试和检查网络的工具包
- 可用于创建 TCP/IP 连接
- 进入终端,输入以下指令
- nc -lk 12345 // 相当于在本机上启动了一个服务器,ip是本机地址,端口号是12345。
1、网络通讯三要素
-
1)IP地址[主机名]
-
英文全称:Internet Protocol Address,又译为网际协议地址。
-
网络中设备的标识,用来唯一标识每一台计算机。现在常用的IP地址是 IPV4 地址。
- IPV4 就是有4段数字,格式是xxx.xxx.xxx.xxx。每一段数字由8位二进制做成,取值范围是0~255。
- IPV4 采用32位地址长度,只有大约43亿个地址。IPv4定义的有限地址空间将被耗尽。
- 为了扩大地址空间,拟通过IPV6重新定义地址空间。IPv6采用128位地址长度。几乎可以不受限制地提供地址。但IPV6现在还没有正式普及。
- 为了解决IPV4有限地址空间的问题,IP地址又分内网地址和外网地址。(比如校园网,每一个学生都会有一个内网地址,学校会有一个路由器,路由器会有个外网地址,学生想要上外网都必须通过路由器出去,只要通过同一个路由器出去的,他们对应的外网地址都是一样的)
-
本质上所有的网络访问是通过ip地址访问的。域名是一个速记符号,不用记住IP地址复杂的数字。
-
本地回环地址:127.0.0.1 主机名:localhost
- 每台计算机都有一个127.0.0.1
- 如果127.0.0.1 ping不通,说明网卡不工作(比如装黑苹果,检测网卡驱动有没装好,可以ping下回环地址)
- 如果本机地址 ping不通,说明网线坏了。
TIP:通过ip138.com可以速查某个域名对应的IP地址。
-
-
2)端口号
-
通过打电话例子说明端口号的作用
- 很多网络概念来源于电话
- 电话号码类似IP
- 分机号类似于端口
-
端口号的作用
- 用来标识进程的逻辑地址,不同进程的标识。
- 有效的端口:0~65535。
- 其中0~1024由系统使用或保留端口。开发中不要使用1024以下的端口。
- 端口有什么用?我们知道,一台拥有IP地址的主机可以提供许多服务,比如Web服务、FTP服务、SMTP服务等,这些服务完全可以通过1个IP地址来实现。那么,主机是怎样区分不同的网络服务呢?显然不能只靠IP地址,因为IP地址与网络服务的关系是一对多的关系。实际上是通过“IP地址+端口号”来区 分不同的服务的。
-
-
3)传输协议(通信规则)
-
UDP(用户数据报协议)
- 面向无连接的协议。
- 只管发送,不确认对方是否接收到。
- 将数据及源和目的封装成数据包中,不需要建立连接。
- 每个数据包的大小限制在64K之内。
- 因为无需连接,因此是不可靠协议。
- 不需要建立连接,速度快。
- 理解发电报的特点就理解了UDP协议的特点。
-
TCP(Transmission Control Protocol,传输控制协议)
- 面向连接的协议。
- 建立连接,形成传输数据的通道。
- 连接中进行大数据传输(数据大小不受限制)。
- 通过三次握手完成连接,是可靠协议,安全送达。
- 必须建立连接,效率会稍低。
- 简单的描述下三次握手的过程:
- 主机A向主机B发出连接请求数据包:“我想给你发数据,可以吗?”,这是第一次对话;主机B向主机A发送同意连接和要求同步(同步就是两台主机一个在发送,一个在接收,协调工作)的数据包:“可以,你什么时候发?”,这是第二次对话;主机A再发出一个数据包确认主机B的要求同步:“我现在就发,你接着吧!”,这是第三次对话。三次“对话”的目的是使数据包的发送和接收同步,经过三次“对话”之后,主机A才向主机B正式发送数据。
-
网络通讯三要素归纳一句话:通过 ip 找机器,通过 端口 找程序,通过 协议 确定如何传输数据。
-
TCP和UDP使用场合
-
tcp一般用于:
- 文件传输(ftp http对数据准确性要求高,速度可以相对慢)。
- 发送或接收邮件(pop imap smtp 对数据准确性要求高,非紧急应用)。
- 远程登录(telnet ssh 对数据准确性有一定要求,有连接的概念)。
-
UDP一般用于
- 即时通信(qq聊天 对数据准确性和丢包要求比较低,但速度必须快)。
- 在线视频(rtsp 速度一定要快,保证视频连续,但是偶尔花了一个图像帧,人们还是能接受的)。
- 网络语音电话(VoIP 语音数据包一般比较小,需要高速发送,偶尔断音或串音也没有问题)。
-
-
1.1、电话图
2、Socket的建立和连接
- 1)导入三个头文件
#import <sys/socket.h>
#import <netinet/in.h>
#import <arpa/inet.h>
- 2)创建socket
/**
参数
domain: 协议域/协议族,AF_INET(IPV4的网络开发)
type: Socket 类型,SOCK_STREAM(TCP)/SOCK_DGRAM(UDP,报文)
protocol: IPPROTO_TCP,协议,如果输入0,可以根据第二个参数自动选择协议
返回值
socket,如果>0 就表示成功
*/
int clientSocket = socket(AF_INET, SOCK_STREAM, 0);
- 3)连接到服务器
/**
参数
1> 客户端socket
2> 指向数据结构sockaddr的指针,其中包括目的端口和IP地址。即服务器的“结构体”地址
3> 结构体数据长度
返回值
0 成功/其他 错误代号,非0即真
*/
struct sockaddr_in serverAddress;
// 协议族
serverAddress.sin_family = AF_INET;
// ip 找机器
serverAddress.sin_addr.s_addr = inet_addr("127.0.0.1");
// 端口号 找程序
serverAddress.sin_port = htons(12345);
// 连接服务器
int result = connect(clientSocket, (const struct sockaddr *)&serverAddress, sizeof(serverAddress));
if (result == 0) {
NSLog(@"成功");
} else {
NSLog(@"失败");
}
友情提示
在C语言开发时,如果要传递结构体的地址,通常会一起传递结构的长度。因为 C 语言中取数据是通过指针寻址的,告诉长度的目的是防止取错数据。
在终端输入:nc -lk 12345 相当于在本机上启动了一个服务器,ip是本机地址,端口号是12345。
3、发送和接收数据
- 1、发送数据
/**
参数
1> 客户端socket
2> 发送内容地址 void * == id
3> 发送内容长度,是指字节的长度。
4> 发送方式标志,一般为0
返s回值
如果成功,则返回发送的字节数,失败则返回SOCKET_ERROR
*/
NSString *msg = @"约?";
ssize_t sendLenght = send(clientSocket, msg.UTF8String, strlen(msg.UTF8String), 0);
NSLog(@"发送了%ld长度的字节,字符串长度%zd",sendLenght,msg.length);
- 2、接收数据
/**
参数
1> 客户端socket
2> 接收内容地址
3> 长度,表示一次最多接收服务器返回的多少字节内容。
4> 接收标志,一般填0,表示阻塞式的,一直等待服务器返回数据
返回值
接收数据的长度
*/
// 缓冲区,准备接受来自服务器的数据
// C语言中,数组的名字,就是指向数组第一个元素的指针。
uint8_t buffer[1024];
ssize_t recvLen = recv(clientSocket, buffer, sizeof(buffer), 0);
NSLog(@"接收 %ld 字节",recvLen);
// 获得服务器返回的二进制数据
NSData *data = [NSData dataWithBytes:buffer length:recvLen];
// 将二进制数据转化成字符串
NSString *resultStr = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSLog(@"接收到的内容是:%@",resultStr);
4、关闭连接
断开连接
close(clientSocket);
5、htons函数的作用
六、Socket 聊天示例
#import "ViewController.h"
#import <sys/socket.h>
#import <netinet/in.h>
#import <arpa/inet.h>
@interface ViewController ()
/**
* 主机
*/
@property (weak, nonatomic) IBOutlet UITextField *hostField;
/**
* 端口
*/
@property (weak, nonatomic) IBOutlet UITextField *portField;
/**
* 消息
*/
@property (weak, nonatomic) IBOutlet UITextField *msgField;
/**
* 内容
*/
@property (weak, nonatomic) IBOutlet UILabel *recvLabel;
/**
* 客户端 socket
*/
@property(nonatomic,assign) int clientSocket;
@end
@implementation ViewController
/**
* 监听连接按钮
*/
- (IBAction)connection {
BOOL result = [self connectionToHost:self.hostField.text port:self.portField.text.integerValue];
self.recvLabel.text = result ? @"成功":@"失败";
}
/**
* 监听发送按钮
*/
- (IBAction)send{
self.recvLabel.text = [self sendAndRecv:self.msgField.text];
[self disconnection];
}
- (void)viewDidLoad {
[super viewDidLoad];
}
/**
* 连接服务器
*/
- (BOOL)connectionToHost:(NSString *)host port:(NSInteger)port{
// 1.创建 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);
// 2.连接
/**
参数
1> 客户端socket
2> 指向数据结构sockaddr的指针,其中包括目的端口和IP地址。即服务器的“结构体”地址
3> 结构体数据长度
返回值
0 成功/其他 错误代号,非0即真
*/
struct sockaddr_in serverAddress;
// 协议族
serverAddress.sin_family = AF_INET;
// ip 找机器 inet_addr内部会对地址做字节翻转
serverAddress.sin_addr.s_addr = inet_addr(host.UTF8String);
//
// 端口号 找程序
serverAddress.sin_port = htons(port);
// 连接服务器
return (connect(self.clientSocket, (const struct sockaddr *)&serverAddress, sizeof(serverAddress)) == 0);
}
/**
* 发送和接收
*/
- (NSString *)sendAndRecv:(NSString *)msg{
// 发送数据
/**
参数
1> 客户端socket
2> 发送内容地址 void * == id
3> 发送内容长度,是指字节的长度。
4> 发送方式标志,一般为0
返回值
如果成功,则返回发送的字节数,失败则返回SOCKET_ERROR
*/
ssize_t sendLenght = send(self.clientSocket, msg.UTF8String, strlen(msg.UTF8String), 0);
NSLog(@"发送了%ld长度的字节,字符串长度%zd",sendLenght,msg.length);
// 接收数据
/**
参数
1> 客户端socket
2> 接收内容地址
3> 长度,表示一次最多接收服务器返回的多少字节内容。
4> 接收标志,一般填0,标示阻塞式的,一直等待服务器服务器返回数据
返回值
接收数据的长度
*/
// 缓冲区,准备接受来自服务器的数据
// C语言中,数组的名字,就是指向数组第一个元素的指针。
uint8_t buffer[1024];
ssize_t recvLen = recv(self.clientSocket, buffer, sizeof(buffer), 0);
NSLog(@"接收 %ld 字节",recvLen);
// 获得服务器返回的二进制数据
NSData *data = [NSData dataWithBytes:buffer length:recvLen];
// 将二进制数据转化成字符串
return [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
}
/**
* 断开连接
*/
- (void)disconnection{
close(self.clientSocket);
}
@end
七、通信过程
1、Socket发送HTTP请求本地apache服务器
-
1、准备工作
启动Mac自带的Apache服务器服务器。
相关命令
sudo apachectl -v, 显示apache版本。
sudo apachectl start, 启动apache。 -
2、代码实现
// 建立连接
if (![self connectionToHost:@"127.0.0.1" port:80]) {
NSLog(@"失败");
return;
}
NSLog(@"成功");
// 向服务器发送请求数据
NSString *result = [self sendAndRecv:@"我要成为牛逼的程序员"];
NSLog(@"%@",result);
提问:给服务器发送的数据,服务器能接收到吗?会有响应结果吗?
答:能接收到数据,但是不会有任何响应结果。原因是服务器不知道客户端发送的内容是什么。
思考
1)、客户端该传什么格式的数据给服务器?服务器才能看懂。
2)、服务器该返回什么格式的数据给客户端?客户端才能看懂。
// 向服务器发送请求数据
// 请求字符串,指定请求头和请求行
NSString *request = @"GET /abcd.txt HTTP/1.1\r\n""Host:localhost\r\n\r\n";
/**
URL与请求的对应关系:
比如URL是:[http://localhost/abcd.txt](http://localhost/abcd.txt) 其中协议头 http是由HTTP/1.1指定的,主机地址是由Host:localhost指定的,路径是由/abcd.txt指定的
*/
NSString *result = [self sendAndRecv:request];
NSLog(@"%@",result);
- 3、获得本地apache服务器的根目录
- 在终端输入命令 more /etc/apache2/httpd.conf 回车
- 搜索 DocumentRoot 得到/Library/WebServer/Documents
2、Socket发送HTTP请求远程服务器(百度,京东,起点)
/*** 连接远程服务器(百度,京东,起点) */
- (void)connectionToRometionServer{
// 百度:域名 [m.baidu.com](http://m.baidu.com/) ip:61.135.185.17
// 起点:域名 [m.qidian.com](http://m.qidian.com/) ip:36.250.76.204
// 京东:域名 [m.jd.com](http://m.jd.com/) ip:111.206.227.150
// 建立连接
if (![self connectionToHost:@"111.206.227.150" port:80]) {
NSLog(@"失败");
return;
}
NSLog(@"成功");
// 向服务器发送请求数据
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];
// 在返回结果中查找\r\n\r\n,目的是取出响应状态行和响应头的内容
NSRange range = [result rangeOfString:@"\r\n\r\n"];
if (range.location != NSNotFound) {
NSString *html = [result substringFromIndex:range.location];
NSLog(@"=======> %@",html);
// 加载网页
[self.webView loadHTMLString:html baseURL:[NSURL URLWithString:@"[http://m.jd.com](http://m.jd.com/)"]];
}
NSLog(@"--------%@",result);
}
友情提示
ping 域名; 就能得到对应网站的ip地址。
3、HTTP通信过程
- 1、建立请求
HTTP协议规定:一个完整的 由客户端发给服务器的HTTP请求 需要包含以下内容:
1)、请求行:指定 请求方法、请求资源路径 以及 HTTP协议版本。
# / 表示访问根目录
GET / HTTP/1.1
2)、请求头:对客户端的环境描述、客户端请求的主机地址等信息。
*请求头中 至少包含以下信息
# 客户端要访问的服务器主机地址:Host:[m.baidu.com](http://m.baidu.com/)
* 请求头中 还可以包含以下信息
# 客户端的类型,客户端的软件环境:User-Agent:iPhone AppleWebKit
# 客户端所能接收的数据类型:Accept:text/html
# 客户端的语言环境:Accept-Language:zh-cn
# 客户端支持的数据压缩格式:Accept-Encoding:gzip
# 访问结束后,是否断开连接:Connection:Close
3)、请求体(可选):客户端发给服务器的具体数据,例如要上传的文件数据。
请求格式:
○ 每一项请求信息末尾使用 \r\n
○ 最后一个请求项末尾使用 \r\n\r\n 表示请求结束
#warming 每一项请求信息和\r\n之间不能有空格。
- 2、接收响应
客户端向服务器发送请求,服务器应当做出响应,即返回给客户端的数据。
HTTP协议规定:1个完整的HTTP响应中包含以下内容
1)、状态行:包含了HTTP协议版本、状态码、状态英文名称
#HTTP/1.1 200 OK
2)、响应头:包含了对服务器的描述、对返回数据的描述
#Server: Apache-Coyote/1.1 // 服务器的类型
#Content-Type: image/jpeg // 返回数据的类型
#Content-Length: 56811 // 返回数据的长度
#Date: Mon, 23 Jun 2014 12:54:52 GMT // 响应的时间
3)、实体内容:服务器返回给客户端的具体数据,比如文件数据
八、Xcode网络适配
Xcode7 使用NSURLSession和 NSURLConnection发送HTTP请求报错
控制台打印:Application Transport Security has blocked a cleartext HTTP (http://) resource load since it is insecure. Temporary exceptions can be configured via your app's Info.plist file.
解决方法:
编辑 info.plist,加入如下设置:
<plist>
<dict>
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
</dict>
</plist>
九、单例模式
单例模式的意思就是只有一个实例。单例模式确保某一个类只有一个实例,而且自行实例化并向整个应用提供这个实例。
这个类称为单例类。
- 单例模式的要点:
必须只能有一个实例。
必须自行创建这个实例。
必须自向整个应用提供这个实例。
1、ARC
(一般不用写)// 定义一个静态变量,在程序运行过程中只有1份(只加载一次)
static id instance;
- (instancetype)init {
static id obj = nil;
static dispatch_once_t onceToken;
// 确保只执行一次初始化代码
dispatch_once(&onceToken, ^{
if ((obj = [super init])) {
// 加载资源
NSLog(@"加载资源");
}
});
return obj;
}
// 重写这个方法控制内存只分配一次
+ (instancetype)allocWithZone:(struct _NSZone *)zone{
static dispatch_once_t onceToken;
// 里面的代码只会执行1次
dispatch_once(&onceToken, ^{
instance = [super allocWithZone:zone];
});
// 返回对象
return instance;
}
// 确保复制的对象还是同一个
- (id)copyWithZone:(NSZone *)zone {
return self;
}
// 全局访问点
+ (instancetype)sharedAudioTool {
return [[self alloc] init];
}
2、非ARC
// 定义一个静态变量,在程序运行过程中只有1份
static id instance;
- (instancetype)init {
static id obj = nil;
static dispatch_once_t onceToken;
// 确保只执行一次初始化代码
dispatch_once(&onceToken, ^{
if ((obj = [super init])) {
// 加载资源
NSLog(@"加载资源");
}
});
return obj;
}
// 重写这个方法控制内存只分配一次
+ (instancetype)allocWithZone:(struct _NSZone *)zone{
static dispatch_once_t onceToken;
// 里面的代码只会执行1次
dispatch_once(&onceToken, ^{
instance = [super allocWithZone:zone];
});
// 返回对象
return instance;
}
// 确保复制的对象还是同一个
- (id)copyWithZone:(NSZone *)zone {
return self;
}
// 全局访问点
+ (instancetype)sharedAudioTool {
return [[self alloc] init];
}
#pragma mark - 在非 ARC 环境下要重写下面内存管理的方法
//oneway:是分布式对象的API,它允许不同的线程或应用程序之间使用OC 对象
- (oneway void)release {
}
不放在自动释放池(返回self重写了release方法)
- (instancetype)autorelease {
return self;
}
增加引用计数
- (instancetype)retain {
return self;
}
单例引用计数永远是1
- (NSUInteger)retainCount {
return 1;
}
3、抽取宏
// Singleton.h
// 单例模式
// ## 拼接前后两个字符串
// .h文件用 Singleton_h
#define Singleton_h(name) + (instancetype)shared##name
// .m文件用 Singleton_m
// \表示\后面的内容都是#define这个宏的内容
#if __has_feature(objc_arc) // arc环境
#define Singleton_m(name) \
+ (instancetype)shared##name { \
return [[self alloc] init]; \
}\
\
+ (instancetype)allocWithZone:(struct _NSZone *)zone {\
static id instance;\
static dispatch_once_t onceToken;\
dispatch_once(&onceToken, ^{\
instance = [super allocWithZone:zone];\
});\
return instance;\
}\
\
- (id)copyWithZone:(nullable NSZone *)zone {\
return self;\
}
#else // 非arc环境
#define Singleton_m(name) \
+ (instancetype)shared##name { \
return [[self alloc] init]; \
}\
\
+ (instancetype)allocWithZone:(struct _NSZone *)zone {\
static id instance;\
static dispatch_once_t onceToken;\
dispatch_once(&onceToken, ^{\
instance = [super allocWithZone:zone];\
});\
return instance;\
}\
\
- (id)copyWithZone:(nullable NSZone *)zone {\
return self;\
}\
- (oneway void)release {}\
\
- (instancetype)autorelease {\
return nil;\
}\
\
- (instancetype)retain {\
return self;\
}\
\
- (NSUInteger)retainCount {\
return 1;\
}
#endif
网友评论