重点 (十二) : URLSession

URLSession

NSURLSession是iOS7中新的网络接口,与NSURLConnection是并列的。

当程序在前台时,NSURLSession与NSURLConnection大部分可以互相替代

NSURLSession支持后台网络操作,除非用户强行关闭

NSURLSession提供的功能:

通过URL将数据下载到内存

通过URL将数据下载到文件系统

将数据上传到指定URL

在后台完成上述功能

对于小型数据,例如用户登录、下载小图像、JSON
& XML仍然使用NSURLConnection的异步或同步方法即可

1.png

NSURLSession的使用

使用NSURLSessionConfiguration来配置NSURLSession对象

用NSURLSession对象来启动一个NSURLSessionTask对象

也可以使用系统全局的sharedSession单例来满足大多数的需求

注:相比较NSURLConnection的返回处理,NSURLSession提供了灵活的数据返回方式,可以使用简单的block方式来处理返回数据,也可以使用更强大的delegate

NSURLSessionConfiguration

作用:用于定义和配置NSURLSession对象

每一个NSURLSession对象都可以设置不同的NSURLSessionConfiguration,从而满足应用内不同类型的网络请求

NSURLSessionConfiguration的三种类型:

defaultSessionConfiguration默认session配置,类似NSURLConnection的标准配置,使用硬盘来存储缓存数据

ephemeralSessionConfiguration临时session配置,与默认配置相比,这个配置不会将缓存、cookie等存在本地,只会存在内存里,所以当程序退出时,所有的数据都会消失

backgroundSessionConfiguration后台session配置,与默认配置类似,不同的是会在后台开启另一个线程来处理网络数据

NSURLSessionTask

NSURLSession使用NSURLSessionTask来具体执行网络请求的任务

NSURLSessionTask支持网络请求的取消、暂停和恢复,比如下载文件暂停之后再恢复就能够自动从上次的进度继续下载

NSURLSessionTask还能获取数据的读取进度

NSURLSessionTask的三种类型:

NSURLSessionDataTask 处理一般的NSData数据对象,比如通过GET或POST方式从服务器获取JSON或XML返回等等,但不支持后台获取

NSURLSessionUploadTask 用于上传文件,支持后台上传

NSURLSessionDownloadTask 用于下载文件,支持后台下载

通过HTTP PUT方法实现文件上传的步骤

实例化NSMutableURLRequest并指定HTTPMethod为PUT

设置请求的授权

授权字符串格式:用户名:口令

授权模式:Basic base64编码的授权字符串

为HTTPHeaderField的Authorization赋值

文件上传的请求部分

  1. URLRequest

NSURL
*url = [NSURL URLWithString:@"http://localhost/uploads/xxx.png"];

NSMutableURLRequest
*requestM = [NSMutableURLRequest requestWithURL:url cachePolicy:0
timeoutInterval:2.0f];

requestM.HTTPMethod = @"PUT";

设置用户授权

1> 授权字符串(用户名+密码)

NSString
*authStr = @"admin:123456";

2> BASE 64编码

NSData *authData = [authStr
dataUsingEncoding:NSUTF8StringEncoding];

NSString *base64Str = [authData
base64EncodedStringWithOptions:0];

NSString *auth = [NSString stringWithFormat:@"BASIC
%@", base64Str];

[requestM
setValue:auth forHTTPHeaderField:@"Authorization"];

**************************笔记****************************


1.NSURLSession的基本使用

  • 1.1 涉及知识点

(1)使用步骤

使用NSURLSession创建task,然后执行task

(2)关于task

a.NSURLSessionTask是一个抽象类,本身不能使用,只能使用它的子类

b.NSURLSessionDataTask\NSURLSessionUploadTask\NSURLSessionDownloadTask

(3)发送get请求

1.创建NSURLSession对象(可以获取单例对象)

NSURLSession *session
= [NSURLSession sharedSession];

2.根据NSURLSession对象创建一个Task

NSURL *url = [NSURL URLWithString:@"http://120.25.226.186:32812/login?username=ss&pwd=ss&type=JSON"];

NSURLRequest *request
= [NSURLRequest requestWithURL:url];

方法参数说明

注意:该block是在子线程中调用的,如果拿到数据之后要做一些UI刷新操作,那么需要回到主线程刷新

第一个参数:需要发送的请求对象

block:当请求结束拿到服务器响应的数据时调用block

block-NSData:该请求的响应体

block-NSURLResponse:存放本次请求的响应信息,响应头,真实类型为NSHTTPURLResponse

block-NSErroe:请求错误信息

NSURLSessionDataTask *
dataTask = [session dataTaskWithRequest:request
completionHandler:^(NSData * __nullable
data, NSURLResponse * __nullable response,
NSError * __nullable error) {

拿到响应头信息

NSHTTPURLResponse *res = (NSHTTPURLResponse *)response;

4.解析拿到的响应数据

NSLog(@"%@\n%@",[[NSString

alloc]initWithData:data encoding:NSUTF8StringEncoding],res.allHeaderFields);

}];

3.执行Task

注意:刚创建出来的task默认是挂起状态的,需要调用该方法来启动任务(执行任务)

[dataTask resume];

(4)发送get请求的第二种方式

注意:该方法内部默认会把URL对象包装成一个NSURLRequest对象(默认是GET请求)

方法参数说明

第一个参数:发送请求的URL地址

block:当请求结束拿到服务器响应的数据时调用block

block-NSData:该请求的响应体

block-NSURLResponse:存放本次请求的响应信息,响应头,真实类型为NSHTTPURLResponse

block-NSErroe:请求错误信息

  • (nullable NSURLSessionDataTask
    *)dataTaskWithURL:(NSURL *)url completionHandler:(void
    (^)(NSData * __nullable data, NSURLResponse
  • __nullable response, NSError *__nullable error))completionHandler;

(5)发送POST请求

1.创建NSURLSession对象(可以获取单例对象)

NSURLSession *session
= [NSURLSession sharedSession];

2.根据NSURLSession对象创建一个Task

NSURL *url = [NSURL
URLWithString:@"http://120.25.226.186:32812/login"];

创建一个请求对象,并这是请求方法为POST,把参数放在请求体中传递

NSMutableURLRequest
*request = [NSMutableURLRequest requestWithURL:url];

request.HTTPMethod =@"POST";

request.HTTPBody = [@"username=520it&pwd=520it&type=JSON"
dataUsingEncoding:NSUTF8StringEncoding];

NSURLSessionDataTask
*dataTask = [session dataTaskWithRequest:request completionHandler:^(NSData *__nullable data, NSURLResponse *__nullable response, NSError *__nullable error) {

拿到响应头信息

NSHTTPURLResponse *res = (NSHTTPURLResponse *)response;

解析拿到的响应数据

NSLog(@"%@\n%@",[[NSString

alloc]initWithData:data encoding:NSUTF8StringEncoding],res.allHeaderFields);

}];

3.执行Task

注意:刚创建出来的task默认是挂起状态的,需要调用该方法来启动任务(执行任务)

[dataTask resume];

2.NSURLSession下载文件-代理

  • 2.1 涉及知识点

(1)创建NSURLSession对象,设置代理(默认配置)

1.创建NSURLSession,并设置代理

第一个参数:session对象的全局配置设置,一般使用默认配置就可以

第二个参数:谁成为session对象的代理

第三个参数:代理方法在哪个队列中执行(在哪个线程中调用),如果是主队列那么在主线程中执行,如果是非主队列,那么在子线程中执行

NSURLSession *session
= [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration
defaultSessionConfiguration] delegate:self
delegateQueue:[NSOperationQueue mainQueue]];

(2)根据Session对象创建一个NSURLSessionDataTask任务(post和get选择)

创建task

NSURL *url = [NSURL URLWithString:@"http://120.25.226.186:32812/resources/images/minion_01.png"];

注意:如果要发送POST请求,那么请使用dataTaskWithRequest,设置一些请求头信息

NSURLSessionDataTask
*dataTask = [session dataTaskWithURL:url];

(3)执行任务(其它方法,如暂停、取消等)

启动task

[dataTask
resume];

其它方法,如取消任务,暂停任务等

[dataTask
cancel];

[dataTask
suspend];

(4)遵守代理协议,实现代理方法(3个相关的代理方法)

1.当接收到服务器响应的时候调用

session:发送请求的session对象

dataTask:根据NSURLSession创建的task任务

response:服务器响应信息(响应头)

completionHandler:通过该block回调,告诉服务器端是否接收返回的数据

-(void)URLSession:(nonnull
NSURLSession *)session dataTask:(nonnull
NSURLSessionDataTask *)dataTask didReceiveResponse:(nonnull
NSURLResponse *)response completionHandler:(nonnull
void (^)(NSURLSessionResponseDisposition))completionHandler

2.当接收到服务器返回的数据时调用

该方法可能会被调用多次

-(void)URLSession:(nonnull
NSURLSession *)session dataTask:(nonnull
NSURLSessionDataTask *)dataTask didReceiveData:(nonnull
NSData *)data

3.当请求完成之后调用该方法

不论是请求成功还是请求失败都调用该方法,如果请求失败,那么error对象有值,否则那么error对象为空

-(void)URLSession:(nonnull
NSURLSession *)session task:(nonnull
NSURLSessionTask *)task didCompleteWithError:(nullable
NSError *)error

(5)当接收到服务器响应的时候,告诉服务器接收数据(调用block)

默认情况下,当接收到服务器响应之后,服务器认为客户端不需要接收数据,所以后面的代理方法不会调用

如果需要继续接收服务器返回的数据,那么需要调用block,并传入对应的策略

NSURLSessionResponseCancel = 0, 取消任务

NSURLSessionResponseAllow = 1, 接收任务

NSURLSessionResponseBecomeDownload = 2, 转变成下载

NSURLSessionResponseBecomeStream NS_ENUM_AVAILABLE(10_11, 9_0) =
3, 转变成流

completionHandler(NSURLSessionResponseAllow);

3.NSURLSessionDownloadTask实现大文件下载

  • 3.1 涉及知识点

(1)使用NSURLSession和NSURLSessionDownload可以很方便的实现文件下载操作

第一个参数:要下载文件的url路径

第二个参数:当接收完服务器返回的数据之后调用该block

location:下载的文件的保存地址(默认是存储在沙盒中tmp文件夹下面,随时会被删除)

response:服务器响应信息,响应头

error:该请求的错误信息

说明:downloadTaskWithURL方法已经实现了在下载文件数据的过程中边下载文件数据,边写入到沙盒文件的操作

NSURLSessionDownloadTask
*downloadTask = [session downloadTaskWithURL:url completionHandler:^(NSURL *__nullable location, NSURLResponse *__nullable response, NSError *__nullable error)

          ```

(2)downloadTaskWithURL内部默认已经实现了变下载边写入操作,所以不用开发人员担心内存问题

(3)文件下载后默认保存在tmp文件目录,需要开发人员手动的剪切到合适的沙盒目录

(4)缺点:没有办法监控下载进度

4.使用NSURLSessionDownloadTask实现大文件下载-监听下载进度

(1)创建NSURLSession并设置代理,通过NSURLSessionDownloadTask并以代理的方式来完成大文件的下载

1.创建NSULRSession,设置代理

self.session = [NSURLSession
sessionWithConfiguration:[NSURLSessionConfiguration
defaultSessionConfiguration] delegate:self
delegateQueue:[NSOperationQueue mainQueue]];

2.创建task

NSURL *url = [NSURL URLWithString:@"http://120.25.226.186:32812/resources/videos/minion_

01.mp4"];

self.downloadTask = [self.session
downloadTaskWithURL:url];

3.执行task

[self.downloadTask resume];

(2)常用代理方法的说明

1.当接收到下载数据的时候调用,可以在该方法中监听文件下载的进度

该方法会被调用多次

totalBytesWritten:已经写入到文件中的数据大小

totalBytesExpectedToWrite:目前文件的总大小

bytesWritten:本次下载的文件数据大小

-(void)URLSession:(nonnull NSURLSession
*)session downloadTask:(nonnull NSURLSessionDownloadTask
*)downloadTask didWriteData:(int64_t)bytesWritten
totalBytesWritten:(int64_t)totalBytesWritten
totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite

2.恢复下载的时候调用该方法

fileOffset:恢复之后,要从文件的什么地方开发下载

expectedTotalBytes:该文件数据的总大小

-(void)URLSession:(nonnull
NSURLSession *)session downloadTask:(nonnull
NSURLSessionDownloadTask *)downloadTask didResumeAtOffset:(int64_t)fileOffset
expectedTotalBytes:(int64_t)expectedTotalBytes

3.下载完成之后调用该方法

-(void)URLSession:(nonnull

NSURLSession *)session downloadTask:(nonnull
NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(nonnull NSURL *)location

4.请求完成之后调用

如果请求失败,那么error有值

-(void)URLSession:(nonnull

NSURLSession *)session task:(nonnull
NSURLSessionTask *)task didCompleteWithError:(nullable
NSError *)error

          ```

(3)实现断点下载相关代码

如果任务,取消了那么以后就不能恢复了

[self.downloadTask cancel];

如果采取这种方式来取消任务,那么该方法会通过resumeData保存当前文件的下载信息

只要有了这份信息,以后就可以通过这些信息来恢复下载

  [self.downloadTask

cancelByProducingResumeData:^(NSData * __nullable
resumeData) {

 self.resumeData = resumeData;

}];

继续下载

首先通过之前保存的resumeData信息,创建一个下载任务

self.downloadTask = [self.session
downloadTaskWithResumeData:self.resumeData];

[self.downloadTask resume];

(4)计算当前下载进度

获取文件下载进度

self.progress.progress = 1.0 *
totalBytesWritten/totalBytesExpectedToWrite;

(5)局限性

01 如果用户点击暂停之后退出程序,那么需要把恢复下载的数据写一份到沙盒,代码复杂度更

02 如果用户在下载中途未保存恢复下载数据即退出程序,则不具备可操作性

5.使用NSURLSessionDataTask实现大文件离线断点下载(完整)

  - 5.1 涉及知识点





(1)关于NSOutputStream的使用

 

  1. 创建一个输入流,数据追加到文件的屁股上

  把数据写入到指定的文件地址,如果当前文件不存在,则会自动创建

NSOutputStream *stream = [[NSOutputStream alloc]initWithURL:[NSURL
fileURLWithPath:[self fullPath]] append:YES];

  2. 打开流

[stream open];

  3. 写入流数据

[stream write:data.bytes maxLength:data.length];

  4.当不需要的时候应该关闭流

[stream close];

(2)关于网络请求请求头的设置(可以设置请求下载文件的某一部分)

 

  1. 设置请求对象

  1.1 创建请求路径

  NSURL *url = [NSURL URLWithString:@"http://120.25.226.186:32812/resources/videos/minion_01.mp4"];




  1.2 创建可变请求对象

NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];

  1.3 拿到当前文件的残留数据大小

 self.currentContentLength = [self FileSize];




  1.4 告诉服务器从哪个地方开始下载文件数据

NSString *range = [NSString stringWithFormat:@"bytes=%zd-",self.currentContentLength];

NSLog(@"%@",range);

  1.5 设置请求头

[request setValue:range forHTTPHeaderField:@"Range"];

(3)NSURLSession对象的释放

-(void)dealloc

{

在最后的时候应该把session释放,以免造成内存泄露

NSURLSession设置过代理后,需要在最后(比如控制器销毁的时候)调用session的invalidateAndCancel或者    

resetWithCompletionHandler,才不会有内存泄露

[self.session invalidateAndCancel];

[self.session resetWithCompletionHandler:^{





  NSLog(@"释放---");

}];

}

(4)优化部分





01 关于文件下载进度的实时更新


02 方法的独立与抽取

6.NSURLSession实现文件上传

  - 6.1 涉及知识点





(1)实现文件上传的方法

 

 

   第一个参数:请求对象

   第二个参数:请求体(要上传的文件数据)

   block回调:

   NSData:响应体

   NSURLResponse:响应头

   NSError:请求的错误信息

NSURLSessionUploadTask *uploadTask = [session
uploadTaskWithRequest:request fromData:data completionHandler:^(NSData *__nullable data, NSURLResponse *__nullable response, NSError *__nullable error)

(2)设置代理,在代理方法中监听文件上传进度

调用该方法上传文件数据

如果文件数据很大,那么该方法会被调用多次

参数说明:

totalBytesSent:已经上传的文件数据的大小

totalBytesExpectedToSend:文件的总大小

 -(void)URLSession:(nonnull

NSURLSession *)session task:(nonnull
NSURLSessionTask *)task didSendBodyData:(int64_t)bytesSent
totalBytesSent:(int64_t)totalBytesSent
totalBytesExpectedToSend:(int64_t)totalBytesExpectedToSend

{

NSLog(@"%.2f",1.0 *

totalBytesSent/totalBytesExpectedToSend);

}

(3)关于NSURLSessionConfiguration相关

01 作用:可以统一配置NSURLSession,如请求超时等

02 创建的方式和使用

 创建配置的三种方式

 +

(NSURLSessionConfiguration *)defaultSessionConfiguration;

 +

(NSURLSessionConfiguration *)ephemeralSessionConfiguration;

  • (NSURLSessionConfiguration
    )backgroundSessionConfigurationWithIdentifier:(NSString

)identifier
NS_AVAILABLE(10_10, 8_0);

 统一配置NSURLSession

-(NSURLSession *)session

{

if (_session == nil)
{

    创建NSURLSessionConfiguration

NSURLSessionConfiguration *config = [NSURLSessionConfiguration
defaultSessionConfiguration];

    设置请求超时为10秒钟

config.timeoutIntervalForRequest = 10;

    在蜂窝网络情况下是否继续请求(上传或下载)

config.allowsCellularAccess = NO;

_session = [NSURLSession sessionWithConfiguration:config delegate:self delegateQueue:[NSOperationQueue mainQueue]];

}

return _session;

}


NSURLSessionConfiguration笔记

NSURLSessionConfiguration对象用于初始化NSURLSession对象。

展开请求级别中与NSMutableURLRequest相关的可供选择的方案,我们可以看到NSURLSessionConfiguration对于会话如何产生请求,提供了相当多的控制和灵活性。从网络访问性能,到cookie,安全性,缓存策略,自定义协议,启动事件设置,以及用于移动设备优化的几个新属性,你会发现你一直在寻找的,正是NSURLSessionConfiguration。

会话在初始化时复制它们的配置,NSURLSession有一个只读的配置属性,使得该配置对象上的变化对这个会话的政策无效。配置在初始化时被读取一次,之后都是不会变化的。

-构造方法

NSURLSessionConfiguration有三个类构造函数,这很好地说明了NSURLSession是为不同的用例而设计的。

  • "defaultSessionConfiguration"返回标准配置,这实际上与NSURLConnection的网络协议栈是一样的,具有相同的共享NSHTTPCookieStorage,共享NSURLCache和共享NSURLCredentialStorage。

  • "ephemeralSessionConfiguration"返回一个预设配置,没有持久性存储的缓存,Cookie或证书。这对于实现像"秘密浏览"功能的功能来说,是很理想的。

  • "backgroundSessionConfiguration":独特之处在于,它会创建一个后台会话。后台会话不同于常规的,普通的会话,它甚至可以在应用程序挂起,退出,崩溃的情况下运行上传和下载任务。初始化时指定的标识符,被用于向任何可能在进程外恢复后台传输的守护进程提供上下文。

想要查看更多关于后台会话的信息,可以查看WWDC Session 204: “What’s New with Multitasking”

-NSURLSessionConfiguration的属性

NSURLSessionConfiguration拥有20个属性。熟练掌握这些属性的用处,将使应用程序充分利用其网络环境。

最重要的属性:

替代 request 中的
forHTTPHeaderField 告诉服务器有关客户端的附加信息

"HTTPAdditionalHeaders"指定了一组默认的可以设置出站请求的数据头。这对于跨会话共享信息,如内容类型,语言,用户代理,身份认证,是很有用的。

WebDav的身份验证

NSString
*userPasswordString = [NSString stringWithFormat:@"%@:%@",
user, password];

NSData *
userPasswordData = [userPasswordString dataUsingEncoding:NSUTF8StringEncoding];

NSString
*base64EncodedCredential = [userPasswordData base64EncodedStringWithOptions:0];

NSString *authString =
[NSString stringWithFormat:@"Basic: %@",
base64EncodedCredential];

设置客户端类型

NSString
*userAgentString = @"iPhone AppleWebKit";

configuration.HTTPAdditionalHeaders
= @{@"Accept":@"application/json",

        @"Accept-Language":

@"en",

        @"Authorization":

authString,

        @"User-Agent":

userAgentString};

"networkServiceType(网络服务类型)"对标准的网络流量,网络电话,语音,视频,以及由一个后台进程使用的流量进行了区分。大多数应用程序都不需要设置这个

"allowsCellularAccess(允许蜂窝访问)"和"discretionary(自行决定)"被用于节省通过蜂窝连接的带宽。建议在使用后台传输的时候,使用discretionary属性,而不是allowsCellularAccess属性,因为它会把WiFi和电源可用性考虑在内

"timeoutIntervalForRequest"和"timeoutIntervalForResource"指定了请求以及该资源的超时时间间隔。许多开发人员试图使用timeoutInterval去限制发送请求的总时间,但这误会了timeoutInterval的意思:报文之间的时间。timeoutIntervalForResource实际上提供了整体超时的特性,这应该只用于后台传输,而不是用户实际上可能想要等待的任何东西

"HTTPMaximumConnectionsPerHost"是
Foundation 框架中URL加载系统的一个新的配置选项。它曾经被用于NSURLConnection管理私人连接池。现在有了NSURLSession,开发者可以在需要时限制连接到特定主机的数量

"HTTPShouldUsePipelining"也出现在NSMutableURLRequest,它可以被用于开启HTTP管道,这可以显着降低请求的加载时间,但是由于没有被服务器广泛支持,默认是禁用的

"sessionSendsLaunchEvents" 是另一个新的属性,该属性指定该会话是否应该从后台启动

"connectionProxyDictionary"指定了会话连接中的代理服务器。同样地,大多数面向消费者的应用程序都不需要代理,所以基本上不需要配置这个属性

关于连接代理的更多信息可以在CFProxySupport Reference 找到。

"Cookie
Policies"

-"HTTPCookieStorage" 是被会话使用的cookie存储。默认情况下,NSHTTPCookieShorage的+ sharedHTTPCookieStorage会被使用,这与NSURLConnection是相同的

-"HTTPCookieAcceptPolicy" 决定了该会话应该接受从服务器发出的cookie的条件

-"HTTPShouldSetCookies" 指定了请求是否应该使用会话HTTPCookieStorage的cookie

"Security
Policies"

URLCredentialStorage
是会话使用的证书存储。默认情况下,NSURLCredentialStorage 的+
sharedCredentialStorage 会被使用使用,这与NSURLConnection是相同的

"TLSMaximumSupportedProtocol" 和 "TLSMinimumSupportedProtocol" 确定是否支持SSLProtocol版本的会话

"Caching
Policies"

URLCache
是会话使用的缓存。默认情况下,NSURLCache 的+ sharedURLCache 会被使用,这与NSURLConnection是相同的

requestCachePolicy
指定了一个请求的缓存响应应该在什么时候返回。这相当于NSURLRequest 的-cachePolicy方法

"Custom
Protocols"

protocolClasses是注册NSURLProtocol类的特定会话数组

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

推荐阅读更多精彩内容