iOS - NSURLConnection&&NSURLSession

作者:Mitchell 

一、NSURLConnection

  • iOS7之后不建议使用
  • GET请求
    • 发送同步请求
    // 1.创建一个URL
    NSURL *url = [NSURL URLWithString:@"httpAddress"];
    // 2.根据URL创建NSURLRequest对象
    // 默认情况下NSURLRequest会自动给我们设置好请求头
    // request默认情况下就是GET请求
    NSURLRequest *request = [NSURLRequest requestWithURL:url];
    
    // 3.利用NSURLConnection对象发送请求
    /*
     第一个参数: 需要请求的对象
     第二个参数: 服务返回给我们的响应头信息
     第三个参数: 错误信息
     返回值: 服务器返回给我们的响应体
     */
    //    NSURLResponse *response = nil;
    NSHTTPURLResponse *response = nil; // 真实类型
    // 注意点: 如果是调用NSURLConnection的同步方法, 会阻塞当前线程
    NSData *data = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:nil];
  • 发送异步请求
    // 1.创建一个URL
    NSURL *url = [NSURL URLWithString:@"httpAddress"];
    // 2.根据URL创建NSURLRequest对象
    // 默认情况下NSURLRequest会自动给我们设置好请求头
    // request默认情况下就是GET请求
    NSURLRequest *request = [NSURLRequest requestWithURL:url];
    // 3.利用NSURLConnection对象发送请求
    /*
     第一个参数: 需要请求的对象
     第二个参数: 回调block的队列, 决定了block在哪个线程中执行
     第三个参数: 回调block
     */
    // 注意点: 如果是调用NSURLConnection的同步方法, 会阻塞当前线程
    [NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
        /*
         response: 响应头
         data : 响应体
         connectionError : 错误信息
         */
        //        NSLog(@"%@", [NSThread currentThread]);
        NSLog(@"%@", [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);
    }];
  • POST请求:
    // 1.创建一个URL
    NSURL *url = [NSURL URLWithString:@"http://admin/login"];
    // 2.根据URL创建NSURLRequest对象
//    NSURLRequest *request = [NSURLRequest requestWithURL:url];
    /*
     NSMutableURLRequest中保存了请求的地址/请求头/请求体
     */
    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
    // 2.1设置请求方式
    // 注意: POST一定要大写
    request.HTTPMethod = @"POST";
    // 2.2设置请求体
    // 注意: 如果是给POST请求传递参数: 那么不需要写?号
    request.HTTPBody = [@"username=Mitchell&pwd=123456&type=JSON" dataUsingEncoding:NSUTF8StringEncoding];
    
    // 3.利用NSURLConnection对象发送请求
    [NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
        NSLog(@"%@", [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);
    }];
  • NSURLConnection 与 NSRunLoop 的关联使用
    • 主要是区分 NSURLConnection 在主线程和子线程发送网络请求的区别
    • 主线程
      • 1、 直接发送网络请求,发送是异步的,但是代理方法是在主线程中执行的:
    NSURL*url = [NSURL URLWithString:@"http://mvvideo1.meitudata.com/55d99e5939342913.mp4"];
    NSURLRequest*request = [NSURLRequest requestWithURL:url];
    //这里分两种方式发送请求
    //2.1 直接发送网络请求是异步的,但是回调方法是在主线程中执行的
    //[[NSURLConnection alloc]initWithRequest:request delegate:self];
    * 2、如果按照如下设置,那么回调的代理方法也会运行在子线程中:
NSURL*url = [NSURL URLWithString:@"http://mvvideo1.meitudata.com/55d99e5939342913.mp4"];
    NSURLRequest*request = [NSURLRequest requestWithURL:url];
    //2.2 设置回调方法也在子线程中运行
    NSURLConnection*conn = [[NSURLConnection alloc]initWithRequest:request delegate:self startImmediately:NO];
    [conn setDelegateQueue:[[NSOperationQueue alloc] init]];
    [conn start];
- 子线程
    * `因为 NSURLConnection 是局部变量`,当我们创建的时候其实是会默认`添加到当前的 RunLoop 中`,如果是在主线程添加,主线程的 RunLoop 是默认有的,无须我们创建,`然而如果再子线程中,是默认没有 RunLoop 和输入源的,所以需要给子线程手动添加 RunLoop` 。
    * 为什么使用 `start` 方法就可以呢?

因为 start 方法如果没有 RunLoop ,会默认添加一个 RunLoop 到当前的线程中来。

    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    dispatch_async(queue, ^{
        NSURL*url = [NSURL URLWithString:@"http://mvvideo1.meitudata.com/55d99e5939342913.mp4"];
        NSURLRequest*request = [NSURLRequest requestWithURL:url];
        
        //2.1 这么设置还是可以的
//NSURLConnection*conn = [[NSURLConnection alloc]initWithRequest:request delegate:self startImmediately:NO];
//[conn setDelegateQueue:[[NSOperationQueue alloc] init]];
//[conn start];
        
        //2.2 但是这样设置就不行了
//[NSURLConnection connectionWithRequest:request delegate:self];
        /*问题一:为什么?
         NSURLConnection 是临时变量,当运行的时候被默认添加到当前线程的 RunLoop 中,由于主线程的RunLoop是默认存在的,所以可以运行,这里不行能的原因就是当前线程并没有 RunLoop,如果想让其运行,比需要创建RunLoop。
         问题二:为什么2.1就可以?
         因为start方法:如果没有一个runloop,它会自动给当前线程添加runLoop,然后将connection加到runLoop中。
         If you don’t schedule the connection in a run loop or an operation queue before calling this method, the connection is scheduled in the current run loop in the default mode.
         */
        //2.2解决方式
       NSRunLoop*loop =  [NSRunLoop currentRunLoop];
        [NSURLConnection connectionWithRequest:request delegate:self];
        [loop run];
    });

二、NSURLSession

  • 推荐使用
  • 简单使用
    • Get
    //注意:如果需要改请求头 需要使用这种方法
    NSURL*url = [NSURL URLWithString:@"httpAddress"];
    NSURLRequest*request = [NSURLRequest requestWithURL:url];
    //1、创建NSURLSession
    NSURLSession*session = [NSURLSession sharedSession];
    //2、利用NSURLSession创建Tash
    NSURLSessionDataTask *task = [session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
        /*
         data:服务器返回高i的数据
         response:响应头
         error:错误信息
         */
        NSLog(@"%@",[[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding]);
    }];
    //3、执行Task
    [task resume];
    * 
    NSURL*url = [NSURL URLWithString:@"httpAddress"];
    //1、创建NSURLSession
    NSURLSession*session = [NSURLSession sharedSession];
    //2、利用NSURLSession创建Tash
    // 如果是通过url的方法创建Task,方法内部会自动根据URL创建一个request
    // 如果是发送Get请求,或者不需要设置请求头信息,那么建议使用当前方法发送请求
    NSURLSessionDataTask*task = [session dataTaskWithURL:url completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
        NSLog(@"%@",[[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding]);
    }];
    //3、执行Task
    [task resume];
- POST
    NSURL*url = [NSURL URLWithString:@"httpAddress"];
    NSMutableURLRequest*request = [NSMutableURLRequest requestWithURL:url];
    request.HTTPMethod = @"POST";
    request.HTTPBody =@"需要发送的信息";
    //1、创建NSURLSession
    NSURLSession*session = [NSURLSession sharedSession];
    //2、利用NSURLSession创建Tash
    NSURLSessionDataTask*task = [session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
        NSLog(@"%@",[[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding]);
    }];
    //3、执行Task
    [task resume];
  • 断点下载
#import "ViewController.h"
#import "NSString+Mitchell.h"
@interface ViewController () <NSURLSessionDataDelegate>
@property (weak, nonatomic) IBOutlet UIProgressView *pro;
@property (weak, nonatomic) IBOutlet UIButton *startBtn;
@property (weak, nonatomic) IBOutlet UIButton *cancelBtn;
@property (nonatomic,strong) NSURLSession             * session;
@property(nonatomic,assign)CGFloat totalLength;
@property(nonatomic,assign)CGFloat currentLength;
@property(nonatomic,strong)NSURLSessionDataTask*task;
@property(nonatomic,strong)NSOutputStream * stream;
@property(nonatomic,strong)NSString * path;
@end
@implementation ViewController
  - (void)viewDidLoad {
    [super viewDidLoad];
    //初始化操作
    //1、初始化文件路径
    self.path = [@"abc.mp4" cacheDir];
    //2、初始化当前下载进度
    self.currentLength = [self getFileSizeWithPath:self.path];
 }
 #pragma mark ------------------ delegate ------------------
 //注意:NSURLSessionDataTask的代理方法中,默认情况下是不接受服务器返回的数据的,所以didCompleteWithError、didReceiveData默认不会被调用
 //如果想接受服务器返回的数据,必须手动告诉系统,我们需要接受数据
  - (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSURLResponse *)response completionHandler:(void (^)(NSURLSessionResponseDisposition))completionHandler{
    NSLog(@"didReceiveResponse");
    self.totalLength = response.expectedContentLength;
    //告诉服务器接受,才能调用didCompleteWithError、didReceiveData两个方法
    NSString*path = [@"abc.mp4" cacheDir];
    NSLog(@"%@",path);
    _stream = [NSOutputStream outputStreamToFileAtPath:path append:YES];
    [_stream open];
    completionHandler(NSURLSessionResponseAllow);
}
  -(void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data{
    self.currentLength +=data.length;
    self.pro.progress = 1.0*_currentLength/_totalLength;
    [_stream write:data.bytes maxLength:data.length];
    NSLog(@"didReceiveData");
}
  -(void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error{
    NSLog(@"didCompleteWithError");
    [_stream close];
    _stream = nil;
}
  - (NSURLSession *)session{
    if (!_session) {
        //1、创建NSURLSession
        _session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:[NSOperationQueue mainQueue]];
    }
    return _session;
}
  - (NSURLSessionDataTask *)task{
    if (!_task) {
        //图片:http://img1.imgtn.bdimg.com/it/u=298400068,822827541&fm=21&gp=0.jpg%2F2008-09-08%2F200898163242920_2.jpg&bdtype=0&fr=ala&ala=1&alatpl=others&pos=1
        //MP4:http://mvvideo1.meitudata.com/55d99e5939342913.mp4
        NSURL*url = [NSURL URLWithString:@"http://mvvideo1.meitudata.com/55d99e5939342913.mp4"];
        NSMutableURLRequest*request = [NSMutableURLRequest requestWithURL:url];
        NSString*range = [NSString stringWithFormat:@"bytes:%zd-",[self getFileSizeWithPath:self.path]];
        [request setValue:range forHTTPHeaderField:@"Range"];
        //2、利用NSURLSession创建Tash
        _task = [self.session dataTaskWithURL:url];
    }
    return _task;
}
  - (NSUInteger)getFileSizeWithPath:(NSString*)path{
    NSUInteger currentSize = [[[NSFileManager defaultManager]attributesOfItemAtPath:path error:nil][NSFileSize] integerValue];
    return currentSize;
}
 #pragma mark ------------------ 开始 ------------------
  - (IBAction)start:(id)sender {
    //3、执行Task
    [self.task resume];

}
 #pragma mark ------------------ 暂停 ------------------
  - (IBAction)pause:(id)sender {
    NSLog(@"暂停");
    [self.task suspend];
}
 #pragma mark ------------------ 继续 ------------------
  - (IBAction)resume:(id)sender {
    NSLog(@"继续");
    [self.task resume];
}
 #pragma mark ------------------ 取消 ------------------
  - (IBAction)cancel:(id)sender {
}
@end
  • 下载进度
#import "ViewController.h"
#import "NSString+Mitchell.h"
@interface ViewController ()<NSURLSessionDownloadDelegate>
@property (weak, nonatomic ) IBOutlet UIProgressView           *pro;
@property (weak, nonatomic ) IBOutlet UIButton                 *startBtn;
@property (weak, nonatomic ) IBOutlet UIButton                 *cancelBtn;
@property (nonatomic,strong) NSURLSessionDownloadTask * task;
@property (nonatomic,strong) NSData                   * resumeData;
@property (nonatomic,strong) NSURLSession             * session;
@end
@implementation ViewController
   - (void)viewDidLoad {
    [super viewDidLoad];
}
#pragma mark ------------------ didFinishDownloadingToURL ------------------
//下载完成时调用
 - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask
didFinishDownloadingToURL:(NSURL *)location{
    NSString*toPath = [@"abc.mp4" cacheDir];
    NSURL*toUrl = [NSURL fileURLWithPath:toPath];
    NSFileManager *manager = [NSFileManager defaultManager];
    [manager moveItemAtURL:location toURL:toUrl error:nil];
    NSLog(@"%@",toUrl);
    NSLog(@"didFinishDownloadingToURL");
    _startBtn.userInteractionEnabled = YES;
}
#pragma mark ------------------ didWriteData ------------------
//接收服务器返回的数据时调用
 - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask
      didWriteData:(int64_t)bytesWritten
 totalBytesWritten:(int64_t)totalBytesWritten
totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite{
    //bytesWritten:每次接收了多少
    //totalBytesWritten:本地写入了多少
    //totalBytesExpectedToWrite:一共多少
NSLog(@"%zd,%zd,%zd",bytesWritten,totalBytesWritten,totalBytesExpectedToWrite);
    self.pro.progress = 1.0* totalBytesWritten/totalBytesExpectedToWrite;
}
#pragma mark ------------------ didResumeAtOffset ------------------
//恢复下载时候调用(使用resumeData恢复下载的时候才能调用)
 - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask
 didResumeAtOffset:(int64_t)fileOffset
expectedTotalBytes:(int64_t)expectedTotalBytes{
    NSLog(@"didResumeAtOffset");
}
//下载完成时调用
//如果下载时候error有值,代表着下载出错
-(void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error{
    NSLog(@"didCompleteWithError");
    _startBtn.userInteractionEnabled = YES;
}
#pragma mark ------------------ 开始 ------------------
 - (IBAction)start:(id)sender {
    //图片:http://img1.imgtn.bdimg.com/it/u=298400068,822827541&fm=21&gp=0.jpg%2F2008-09-08%2F200898163242920_2.jpg&bdtype=0&fr=ala&ala=1&alatpl=others&pos=1
    //MP4:http://mvvideo1.meitudata.com/55d99e5939342913.mp4
    NSURL*url = [NSURL URLWithString:@"http://mvvideo1.meitudata.com/55d99e5939342913.mp4"];
    NSURLRequest*request = [NSURLRequest requestWithURL:url];
    //1、创建NSURLSession
    /*
     第一个参数:Session的配置信息
     第二个参数:代理
     第三个参数:规定了代理方法在哪个线程中执行
     */
    _session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:[NSOperationQueue mainQueue]];
    //2、利用NSURLSession创建Task
    _task = [self.session downloadTaskWithRequest:request];
    //3、执行Task
    [_task resume];
    _startBtn.userInteractionEnabled = NO;
}
#pragma mark ------------------ 暂停 ------------------
 - (IBAction)pause:(id)sender {
    NSLog(@"暂停");
    [_task suspend];
}
#pragma mark ------------------ 继续 ------------------
  - (IBAction)resume:(id)sender {
    NSLog(@"继续");
//    [_task resume];
    //恢复信息恢复下载,用获取到resumeData继续下载
    self.task = [_session downloadTaskWithResumeData:_resumeData];
    [self.task resume];
}
#pragma mark ------------------ 取消 ------------------
 - (IBAction)cancel:(id)sender {
    //取消
    //注意点:一旦取消,就不能恢复了
//    [_task cancel];
    //如果是调用了cancelByProducingResumeData方法,方法内部会回调一个block,在block中会将resumeData传递给我们
    //resumeData中就保存了当前下载任务的配置信息(下载到什么地方,从什么地方恢复等等)
    [self.task cancelByProducingResumeData:^(NSData *resumeData) {
        self.resumeData = resumeData;
    }];
}
@end
  • 上传
#import "ViewController.h"
// 请求头的
#define MitchellHeaderBoundary @"----xiaomage"
// 请求体的
#define MitchellBoundary [@"------xiaomage" dataUsingEncoding:NSUTF8StringEncoding]
// 结束符
#define MitchellEndBoundary [@"------xiaomage--" dataUsingEncoding:NSUTF8StringEncoding]
// 将字符串转换为二进制
#define MitchellEncode(str) [str dataUsingEncoding:NSUTF8StringEncoding]
// 换行
#define MitchellNewLine [@"\r\n" dataUsingEncoding:NSUTF8StringEncoding]
@interface ViewController ()<NSURLSessionTaskDelegate>
@end
@implementation ViewController
 - (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
}
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
    NSURL*url = [NSURL URLWithString:@""];
    NSMutableURLRequest*request = [NSMutableURLRequest requestWithURL:url];    
    //1、创建Session
    NSURLSession*session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:[NSOperationQueue mainQueue]];
    //2、根据Seesion创建Task
    /*
    //注意:fromFile方法是用于PUT请求上传文件的
    //而我们的服务器只支持POST请求上传文件
    NSURL*fileUrl = [NSURL fileURLWithPath:@""];
    NSURLSessionUploadTask*task = [session uploadTaskWithRequest:request fromFile:fileUrl completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
    }];
     */
    /*请求体
     第一个参数:需要请求的地址/请求头/
     第二个参数:需要上传拼接之后的二进制数据
     */
    request.HTTPMethod = @"POST";
//    NSURL*fileUrl = [NSURL fileURLWithPath:@""];
//    NSData*data =[NSData dataWithContentsOfURL:fileUrl];
    // 2.2设置请求体
    NSMutableData *data = [NSMutableData data];
    // 2.2.1设置文件参数
    [data appendData:MitchellBoundary];
    [data appendData:MitchellNewLine];
    // name : 对应服务端接收的字段类型(服务端参数的名称)
    // filename: 告诉服务端当前的文件的文件名称(也就是告诉服务器用什么名称保存当前上传的文件)
    [data appendData:[@"Content-Disposition: form-data; name=\"file\"; filename=\"videos.plist\"" dataUsingEncoding:NSUTF8StringEncoding]];
    [data appendData:MitchellNewLine];
    [data appendData:[@"Content-Type: application/octet-stream" dataUsingEncoding:NSUTF8StringEncoding]];
    [data appendData:MitchellNewLine];
    [data appendData:MitchellNewLine];
    //上传文件的数据
    NSURL*fileUrl = [NSURL fileURLWithPath:@""];
    NSData*imgData = [NSData dataWithContentsOfURL:fileUrl];    
    [data appendData:imgData];
    [data appendData:MitchellNewLine];
    [data appendData:MitchellNewLine];
    // 2.2.2设置非文件参数
    [data appendData:MitchellBoundary];
    [data appendData:MitchellNewLine];
    // name : 对应服务端接收的字段类型(服务端参数的名称)
    [data appendData:[@"Content-Disposition: form-data; name=\"username\"" dataUsingEncoding:NSUTF8StringEncoding]];
    [data appendData:MitchellNewLine];
    [data appendData:MitchellNewLine];
    [data appendData:[@"Mitchell" dataUsingEncoding:NSUTF8StringEncoding]];
    [data appendData:MitchellNewLine];
    // 2.2.3设置结束符号
    [data appendData:MitchellEndBoundary];
     //注意点:如果利用NSURLSessionUploadTask上传文件,那么请求体必须卸载fromData参数中,不能设置在request中
    //如果设置在request中会被忽略
    //
    request.HTTPBody = data;
    NSURLSessionUploadTask*task = [session uploadTaskWithRequest:request fromData:data completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) { 
    }];
    //3、执行Task
    [task resume];   
}
#pragma mark ------------------ Delegate ------------------
//上传过程中调用
//bytesSent:当前这一次上传的数据大小
//totalBytesSent:总共已经上传的数据大小
//totalBytesExpectedToSend:需要上传文件的大小
 -(void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didSendBodyData:(int64_t)bytesSent totalBytesSent:(int64_t)totalBytesSent totalBytesExpectedToSend:(int64_t)totalBytesExpectedToSend{
NSLog(@"%f",1.0*totalBytesSent/totalBytesExpectedToSend);
}
//请求完毕时调用
 -(void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error{
    //用block方式 创建是不会调用这个方法
}
@end
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,948评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,371评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,490评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,521评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,627评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,842评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,997评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,741评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,203评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,534评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,673评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,339评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,955评论 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,770评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,000评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,394评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,562评论 2 349

推荐阅读更多精彩内容