IOS基础网络:请求方法(上)

原创:知识点总结性文章
创作不易,请珍惜,之后会持续更新,不断完善
个人比较喜欢做笔记和写总结,毕竟好记性不如烂笔头哈哈,这些文章记录了我的IOS成长历程,希望能与大家一起进步
温馨提示:由于简书不支持目录跳转,大家可通过command + F 输入目录标题后迅速寻找到你所需要的内容

目录

  • 一、了解NSURLSession
  • 二、实现 GET 请求
  • 三、实现POST请求
  • 四、下载数据
  • 五、AFNetworking 框架

一、了解NSURLSession

优点
  • NSURLSession可以为每个用户会话 (session) 配置缓存
  • 支持安全证书策略
  • 支持 FTPHTTPHTTPS等网络通信协议
  • 支持实现后台下载和上传,以及断点续传功能
  • 还可以取消、恢复、挂起网络请求
设置HTTP请求白名单

访问http网页需要在Info.plist中添加NSAppTransportSecurity,并在其下添加NSAllowsArbitraryLoads,值设为YES

会话类型
  • 简单会话:不可配置会话,只能执行基本的网络请求,通过shared静态单例获得该对象
  • 默认会话 (default session) :可以配置
  • 短暂会话 (ephemeral session):不存储任何数据在磁盘中,所有的数据都是缓存的,当应用结束会话时,它们被自动释放
  • 后台会话 (background session):可以在后台进行上传下载操作,需要通过唯一的identity标示
任务类型

系统一共提供了5种任务类,继承关系如下图所示。其中NSURLSessionTask为抽象类,不能实现网络访问。NSURLSessionStreanTask以流的方式进行网络访问,使用的比较少,使用的多的是dataTaskdownloadTaskuploadTask,基本满足了网络访问的基本需求:获取数据(通常是JSONXML等)、文件上传、文件下载,这三个类都是NSURLSessionTask这个抽象类的子类。NSURLSessionTask支持任务的暂停、取消和恢复,并且默认任务运行在子线程中。

代理类型

根据图中代理协议的名字不难发现,每一个任务类都有相对应的代理协议,只有NSURLSessionUploadTask没有对应的代理协议,因为NSURLSessionUploadTask继承自NSURLSessionDataTask,因此NSURLSessionDataDelegate即为NSURLSessionUploadTask对应的代理协议。


二、实现 GET 请求

使用简单会话实现的网络请求不能进行任何配置,简单会话是在子队列中执行的,当遇到表视图刷新这种更新 UI界面的操作时,要切换回主队列执行。可以使用默认会话,然后进行配置为mainQueue,由于会话任务是在主线程中执行,不需要再使用并发队列方法dispatch_async回到主队列刷新UI。

1、构建网络请求任务

指定请求的 URL
  • 请求的参数全部暴露在 URL 后面,这是 GET 请求方法的典型特征
  • 协议:不同的协议代表着不同的资源查找方式、资源传输方式
  • 主机地址:存放资源的主机的IP地址(域名)
  • 路径:资源在主机中的具体位置
  • 参数:参数可有可无,也可以多个。如果带参数的话,在?号后面接参数,多个参数的话之间用&隔开
NSURL *url = [NSURL URLWithString:@"协议://主机地址/路径?参数&参数"];
// 指定请求的 URL
NSString *strURL = [[NSString alloc] initWithFormat:@"http://www.51work6.com/service/mynotes/WebServic.php?email=%@&type%@&action=%@",@"<你的51work6.com用户邮箱>","JSON",@"query"];
// URL字符串允许的字符集
strURL = [strURL stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLQueryAllowedCharacterSet]];
// 将字符串转换为 URL字符串
NSURL *url = [NSURL URLWithString:strURL];
构造网络请求对象 NSURLRequest
NSURLRequest *request = [[NSURLRequest alloc] initWithURL:url];
创建默认配置对象
NSURLSessionConfiguration *defaultConfig = [NSURLSessionConfiguration defaultSessionConfiguration];

//设置缓存策略
defaultConfig.requestCachePolicy = NSURLRequestUseProtocolCachePolicy;
//设置网络服务类型,决定了网络请求的形式
defaultConfig.networkServiceType = NSURLNetworkServiceTypeDefault;
//设置请求超时时间
defaultConfig.timeoutIntervalForRequest = 15;
//设置请求头
defaultConfig.HTTPAdditionalHeaders
//设置网络属性,是否使用移动流量
defaultConfig.allowsCellularAccess = YES;
创建默认会话对象

本例网络请求任务之后回调的是代码块,而非委托对象的方法,delegate参数被赋值为nildelegateQueue参数是设置会话任务执行所在的操作队列。由于会话任务是在主线程中执行不需要再使用并发队列方法dispatch_async回到主队列刷新UI。

NSURLSession *defaultSession = [NSURLSession sessionWithConfiguration:defaultConfig delegate:nil delegateQueue:[NSOperationQueue mainQueue]];
创建网络请求任务
  • 第一个参数是NSURLRequest请求对象
  • 第二个参数completionHandler是请求完成回调的代码块
  • data参数是从服务器返回的数据
  • response是从服务器返回的应答对象
  • error是错误对象,如errornil, 则说明请求过程没有错误发生
NSURLSessionDataTask *task = [defaultSession dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
    
    NSLog(@"请求完成...");
    ...
}];
开启网络请求任务

在会话任务对象上调用 resume 方法开始执行任务,新创建的任务默认情况下是暂停的。

[task resume];

2、请求完成使用返回数据更新UI

获取返回数据

将响应对象转化为NSHTTPURLResponse对象:

NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;

获取网络连接的状态码,200成功 404网页未找到:

NSLog(@"状态码:%li", httpResponse.statusCode);
if (httpResponse.statusCode == 200)
{
    NSLog(@"请求成功");
}

获取响应头:

NSLog(@"响应头:%@", httpResponse.allHeaderFields);

获取响应体。调用 reloadView:方法是在 GCD主队列中执行的,简单会话是在非主队列中执行的,当遇到表视图刷新这种更新 UI界面的操作时,要切换回主队列执行。在请求完成时调用 reloadView:方法,该方法用于重新加载表视图中的数据。

if (!error)
{
    NSDictionary *resDict = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingAllowFragments error:nil];
    dispatch_async(dispatch_get_main_queue(), ^{
        [self reloadView:resDict];
    });
}
else
{
    NSLog(@"error: %@", error.localizedDescription);
}
重新加载表视图

从服务器返回的JSON格式数据有两种情况,成功返回或者失败,ResultCode数据项用于说明该结果。当ResultCode大于等于0时,说明服务器端操作成功,取得从服务端返回的数据。为了减少网络传输,只传递消息代码,不传递消息内容,根据结果编码获得结果消息,显示错误消息。

- (void)reloadView:(NSDictionary *)res
{
    NSNumber *resultCode = res[@"ResultCode"];
    if ([resultCode integerValue] >= 0)
    {
        self.listData = res[@"Record"];
        [self.tableView reloadData];
    }
    else
    {
        NSString *errorStr = [resultCode errorMessage];
        UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"错误信息" message:errorStr preferredStyle:UIAlertControllerStyleAlert];
        UIAlertAction *okAction = [UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:nil];
        [alertController addAction:okAction];
        [self presentViewController:alertController animated:true completion:nil];
    }
}

3、实现网络请求代理方法

已经接收到响应头
  • NSURLSessionResponseCancel:取消接受
  • NSURLSessionResponseAllow:继续接受
  • NSURLSessionResponseBecomeDownload:将当前任务转化为一个下载任务
  • NSURLSessionResponseBecomeStream:将当前任务转化为流任务
- (void)URLSession:(NSURLSession *)session
          dataTask:(NSURLSessionDataTask *)dataTask
didReceiveResponse:(NSURLResponse *)response
 completionHandler:(void (^)(NSURLSessionResponseDisposition))completionHandler
{
    //通过状态码来判断石是否成功
    NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
    if (httpResponse.statusCode == 200)
    {
        NSLog(@"请求成功");
        NSLog(@"%@", httpResponse.allHeaderFields);

        //初始化接受数据的NSData变量
        _data = [[NSMutableData alloc] init];

        //执行completionHandler Block回调来继续接收响应体数据
        completionHandler(NSURLSessionResponseAllow);
    }
    else
    {
        NSLog(@"请求失败");
    }
}
接受到数据包时调用的代理方法
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data 
{
    NSLog(@"收到了一个数据包");

    //拼接完整数据
    [_data appendData:data];
    NSLog(@"接受到了%li字节的数据", data.length);
}
数据接收完毕时调用的代理方法
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error 
{
    NSLog(@"数据接收完成");

    if (error)
    {
        NSLog(@"数据接收出错!");
        //清空出错的数据
        _data = nil;
    }
    else
    {
        //数据传输成功无误,JSON解析数据
        NSDictionary *dic = [NSJSONSerialization JSONObjectWithData:_data options:NSJSONReadingMutableLeaves error:nil];
        NSLog(@"%@", dic);
    }
}

三、实现POST请求

❶ 在这个URL字符串后面没有参数(即没有?号后面的内容)

NSString *strURL = @"http://www.51work6.com/service/mynotes/WebService.php";
NSURL *url = [NSURL URLWithString:strURL];

❷ 参数字符串:请求参数放到请求体中。postData就是请求参数。将参数字符串转换成NSData类型,编码一定要采用UTF-8

NSString *post = [NSString stringWithFormat:@"email=%@&type=%@&action=%@", @"<用户邮箱>", @"JSON", @"query"];
NSData *postData = [post dataUsingEncoding:NSUTF8StringEncoding];

❸ 创建可变的请求对象NSMutableURLRequest,所以可以通过属性设置其内容。HTTPMethod属性用于设置HTTP请求方法为POSTHTTPBody属性用于设置请求数据。

NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
[request setHTTPMethod:@"POST"];
[request setHTTPBody:postData];

❹ 创建请求会话

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

❺ 创建并开启请求任务,将获取到的数据进行JSON解析。

NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
    NSDictionary *dic = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableLeaves error:nil];
    NSLog(@"%@", dic);
}];
[dataTask resume];

四、下载数据

1、创建下载任务

声明委托和变量
#define kResumeDataPath [NSHomeDirectory() stringByAppendingPathComponent:@"Documents/resumeData.plist"]
@interface NSURLSessionViewController ()<NSURLSessionDownloadDelegate>
@property(nonatomic, strong) UIProgressView *progressView;
@end
@implementation NSURLSessionViewController
{
    NSURLSession *_session;
    NSURLSessionDownloadTask *_downLoadTask;
}
创建会话,delegate为self
NSURLSessionConfiguration *defaultConfig = [NSURLSessionConfiguration defaultSessionConfiguration];
_session = [NSURLSession sessionWithConfiguration:defaultConfig delegate:self delegateQueue:[NSOperationQueue mainQueue]];
开始下载
- (void)startDownLoad
{
    //设置文件下载地址
    NSString *strURL = [[NSString alloc] initWithFormat:@"http://218.76.27.57:8080/海贼王.jpg"];
    NSURL *url = [NSURL URLWithString:strURL];
    
    //创建下载任务
    _downLoadTask = [_session downloadTaskWithURL:url];
    //开始执行任务
    [_downLoadTask resume];
}
暂停下载

判断当前的下载状态,正在下载就取消当前任务,并将任务的信息保存的文件中,即将数据写入到文件,方便下次读取。数据信息包括下载链接,已下载的数据大小,已下载的临时文件文件名。

- (void)pauseDownLoad
{
    if (_downLoadTask && _downLoadTask.state == NSURLSessionTaskStateRunning)
    {
        [_downLoadTask cancelByProducingResumeData:^(NSData * _Nullable resumeData) {
            [resumeData writeToFile:kResumeDataPath atomically:YES];
            NSLog(@"将数据写入到文件:%@", kResumeDataPath);
        }];
        _downLoadTask = nil;
    }
}
继续下载

获取已经保存的数据,再重建下载任务并继续执行任务,最后移除文件。

- (void)resumeDownLoad
{
    NSData *data = [NSData dataWithContentsOfFile:kResumeDataPath];
    if (data)
    {
        _downLoadTask = [_session downloadTaskWithResumeData:data];
        [_downLoadTask resume];
        [[NSFileManager defaultManager] removeItemAtPath:kResumeDataPath error:nil];
    }
}

2、实现委托协议

文件下载完成
  • session:网络会话
  • downloadTask:下载任务
  • location:下载完成的文件在本地磁盘中的位置,是个保存数据的本地临时文件
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location
{
    NSLog(@"下载完成");
    NSLog(@"临时文件: %@\n", location);
}

❶ 拼接文件的目标路径

NSString *downloadsDir = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, TRUE) objectAtIndex:0];
NSString *downloadStrPath = [downloadsDir stringByAppendingPathComponent:@"歌曲.mp3"];
NSURL *downloadURLPath = [NSURL fileURLWithPath:downloadStrPath];

❷ 将临时文件,移动到沙盒路径中。首先需要判断在沙箱 Documents 目录下是否存在 海贼王.jpg 文件。如果存在就删除海贼王.jpg文件,这可以防止最新下载的文件不能覆盖之前遗留海贼王.jpg文件。

NSFileManager *fileManager = [NSFileManager defaultManager];
NSError *error = nil;
if ([fileManager fileExistsAtPath:downloadStrPath])
{
    [fileManager removeItemAtPath:downloadStrPath error:&error];
    if (error)
    {
        NSLog(@"删除文件失败: %@", error.localizedDescription);
    }
}

❸ 将下载时保存数据的本地临时文件移动到沙箱 Documents 目录下,并命名为 海贼王.jpg文件。沙箱Documents目录下不能有海贼王.jpg名字的文件,否则无法完成移动操作。文件下载并移动成功后,构建UIImage对象,然后再把UIImage对象赋值给图片视图。在界面中我 们就可以看到下载的图片了。

if ([fileManager moveItemAtURL:location toURL:downloadURLPath error:&error])
{
    NSLog(@"文件保存地址: %@", downloadStrPath);
    UIImage *image = [UIImage imageWithContentsOfFile:downloadStrPath];
    self.imageView.image = image;
}
else
{
    NSLog(@"保存文件失败: %@", error.localizedDescription);
}

接受到一部分数据后调用的方法
  • session:网络会话对象
  • downloadTask:下载任务对象
  • bytesWritten:当前数据包写入的数据字节数
  • totalBytesWritten:当前总共写入的数据字节数
  • totalBytesExpectedToWrite:完整的文章总大小字节数

计算当前下载任务进度。更新进度条的进度,属于更新UI操作,需要在主队列(主线程所在队列)中执行。由于配置会话时设置的是主队列(主线程所在队列),所以更新UI的操作不必放在dispatch_async中执行。

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

    NSLog(@"获取到一个数据包:%lli", bytesWritten);
    NSLog(@"已经写入的数据包大小:%lli", totalBytesWritten);
    NSLog(@"总文件大小:%lli", totalBytesExpectedToWrite);

    CGFloat progress = (CGFloat)totalBytesWritten / totalBytesExpectedToWrite;
    _progressView.progress = progress;
    _progressLabel.text = [NSString stringWithFormat:@"%.2f%%", progress * 100];
}

五、AFNetworking 框架

由于上传数据时需要模拟HTML表单上传数据,数据采用multipart/form-data格式, 即将数据分割成小段进行上传,具体实现非常复杂 。 NSURLSessionUploadTask任务没有屏蔽这些复杂性,不推荐使用 NSURLSessionUploadTask任务,而推荐采用AFNetworking网络请求框架。

NSURLSession仍然不是很理想,易用性不够。AFNetworking框架是Alamofire基金会支持的项目,因此能够获得比较稳定的技术支持。AFNetworking 框架支持断点续传、图片缓存到内存、后台下载、获取下载进度和上传进度等。

实现GET请求
NSString *urlString = @"http://piao.163.com/m/cinema/list.html?app_id=1&mobileType=iPhone&ver=2.6&channel=appstore&deviceId=9E89CB6D-A62F-438C-8010-19278D46A8A6&apiVer=6&city=110000";
❶ 创建manager
AFHTTPSessionManager *manger = [AFHTTPSessionManager manager];
❷ 设置发送和接收的数据类型
  • AFHTTPRequestSerializer:使用 key1=value1&key2=value2的形式来上传数据
  • AFJSONRequestSerializer:使用json格式上传数据
[AFJSONRequestSerializer serializerWithWritingOptions:NSJSONWritingPrettyPrinted];
manger.requestSerializer = [AFHTTPRequestSerializer serializer];
❸ 接受数据格式
  • AFHTTPResponseSerializer:不做解析操作
  • AFJSONResponseSerializer:自动进行json解析
  • AFXMLParserResponseSerializer:接受XML数据
manger.responseSerializer = [AFJSONResponseSerializer serializerWithReadingOptions:NSJSONReadingMutableLeaves];
❹ 进行网络请求

responseObject是从服务器返回的JSON对象,可以是字典或数组类型。

[manger GET:urlString parameters:nil headers:nil progress:^(NSProgress * _Nonnull downloadProgress) {
    NSLog(@"%lli/%lli", downloadProgress.completedUnitCount, downloadProgress.totalUnitCount);
} success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
    NSLog(@"请求成功");
    NSLog(@"responseObject%@", responseObject);
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
    NSLog(@"请求失败");
    NSLog(@"error : %@", error.localizedDescription);
}];

实现 POST请求
❶ 创建manager
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
❷ 请求参数
NSString *urlString = @"http://piao.163.com/m/cinema/schedule.html?app_id=1&mobileType=iPhone&ver=2.6&channel=appstore&deviceId=9E89CB6D-A62F-438C-8010-19278D46A8A6&apiVer=6&city=110000";
NSDictionary *parameters = @{@"cinema_id" : @"1533"};
❸ 发起POST请求
[manager POST:urlString parameters:parameters headers:nil progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
    NSLog(@"请求成功:%@", responseObject);
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
    NSLog(@"请求失败");
}];

下载数据

下载地址

NSString *urlString = @"http://218.76.27.57:8080/chinaschool_rs02/135275/153903/160861/160867/1370744550357.mp3";

创建manager

AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];

创建请求对象

NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:urlString]];

创建下载任务。创建一个沙盒路径下的子路径,设定保存的文件夹位置。

NSURLSessionDownloadTask *downloadTask = [manager downloadTaskWithRequest:request progress:^(NSProgress * _Nonnull downloadProgress) {
    NSLog(@"%lli/%lli", downloadProgress.completedUnitCount, downloadProgress.totalUnitCount);
} destination:^NSURL * _Nonnull(NSURL * _Nonnull targetPath, NSURLResponse * _Nonnull response) {
    NSLog(@"状态码:%li", ((NSHTTPURLResponse *)response).statusCode);
    
    //指定的保存路径
    NSString *filePath = [NSHomeDirectory() stringByAppendingPathComponent:@"Documents/歌曲.mp3"];
    NSLog(@"%@", filePath);
    return [NSURL fileURLWithPath:filePath];
} completionHandler:^(NSURLResponse * _Nonnull response, NSURL * _Nullable filePath, NSError * _Nullable error) {
    NSLog(@"下载完成");
}];

[downloadTask resume];

上传数据
❶ 构造参数字典

设置请求网址

NSString *urlString = @"https://api.weibo.com/2/statuses/upload.json";
NSString *token = @"2.00hd363CtKpsnBedca9b3f35tBYiP";

获取输入的文字和图片

UIImage *image = _imageView.image;
NSString *text = _textField.text;
if (image == nil || text.length == 0)
{
    return;
}

构造参数字典

NSDictionary *dic = @{@"access_token" : token, @"status" : text};
❷ 创建HTTP请求序列化对象

封装了HTTP请求参数(放在 URL 问号之后的部分)和表单数据

AFHTTPRequestSerializer *serializer = [AFHTTPRequestSerializer serializer];
❸ 创建请求对象
  • 该方法可以发送 multipart/form-data 格式的表单数据
  • method参数的请求方法一般是 POST
  • URLString 参数是上传时的服务器地址
  • parameters是请求参数,它是字典结构
  • constructingBodyWithBlock是请求体代码块
  • error参数是错误对象
NSMutableURLRequest *request = [serializer multipartFormRequestWithMethod:@"POST" URLString:urlString parameters:dic constructingBodyWithBlock:^(id<AFMultipartFormData>  _Nonnull formData) {
    ...
} error:nil];
❹ 图片数据的拼接
  • 添加 multipart/form-data 格式的表单数据,这种格式的数据被分割成小段进行上传
  • 该方法的第一个参数是要上传的文件数据
  • 第二个参数name是与数据相关的名称,一般是 file
  • 第三个参数fileName是文件名,是放在请求头中的文件名,不能为空
  • 第四个参数mimeType是数据相关的MIME类型
NSData *imageData = UIImageJPEGRepresentation(image, 1);
[formData appendPartWithFileData:imageData name:@"pic" fileName:@"image.png" mimeType:@"image/png"];
❺ 创建manager
AFURLSessionManager *manager = [[AFURLSessionManager alloc] initWithSessionConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]];
❻ 用于创建NSURLSessionUploadTask上传会话任务
NSURLSessionUploadTask *uploadTask = [manager uploadTaskWithStreamedRequest:request progress:^(NSProgress * _Nonnull uploadProgress) {
    //回到主线程中 刷新界面
    [_progressView performSelectorOnMainThread:@selector(setProgress:) withObject:@(uploadProgress.fractionCompleted) waitUntilDone:YES];
    
    dispatch_async(dispatch_get_main_queue(), ^{
        //上传进度
        [_progressView setProgress:uploadProgress.fractionCompleted];
    });
    
} completionHandler:^(NSURLResponse * _Nonnull response, id  _Nullable responseObject, NSError * _Nullable error) {
    NSLog(@"上传结束,状态码:%li", ((NSHTTPURLResponse *)response).statusCode);
}];

[uploadTask resume];

Demo

Demo在我的Github上,欢迎下载。
BasicsDemo

参考文献

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

推荐阅读更多精彩内容