01-网络

一、网络基础知识介绍

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提供的功能:
      1. 通过 URL 将数据下载到内存
      2. 通过 URL 将数据下载到文件系统
      3. 将数据上传到指定的URL
      4. 在后台完成上述功能
      5. 支持下载,断点续传,后台上传/下载,后台上传/下载任务跟进
  • 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
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 151,829评论 1 331
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 64,603评论 1 273
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 101,846评论 0 226
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 42,600评论 0 191
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 50,780评论 3 272
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 39,695评论 1 192
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,136评论 2 293
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 29,862评论 0 182
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 33,453评论 0 229
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 29,942评论 2 233
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,347评论 1 242
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 27,790评论 2 236
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,293评论 3 221
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 25,839评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,448评论 0 181
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 34,564评论 2 249
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 34,623评论 2 249

推荐阅读更多精彩内容