BestHttp文档翻译 1

BestHttp 文档翻译

介绍

BestHTTP 是基于 RFC 2616 的 HTTP/1.1 实现 , 它支持几乎所有Unity移动和独立平台 (请参阅 Supported platforms).

我的目标是为Unity创建一个易于使用但功能强大的插件,以利用HTTP / 1.1中的潜力. 本文档是一个快速指南,并非所有功能和属性都可以在这里找到. 如需支持,功能请求或一般性问题,您可以发送电子邮件至besthttp@gmail.com.

快速入门

  • 1:使用头文件.本文档中的所有示例都没有任何错误检查!在编写时,请确保添加一些空检查
using BestHTTP; 
  • 2:GET 请求,向Web服务器发出请求的最简单方法是创建一个HTTPRequest对象,为其构造函数提供url和回调函数.在我们构造一个新的HTTPRequest对象后,我们唯一需要做的就是使用Send()函数发送请求.
{
        HTTPRequest request = new HTTPRequest(new Uri("https://google.com"), OnRequestFinished); request.Send(); 

        new HTTPRequest(new Uri("https://google.com"), (request, response) => Debug.Log("Finished!")).Send(); 
}
void OnRequestFinished(HTTPRequest request, HTTPResponse response) 
{    
        Debug.Log("Request Finished! Text received: " + response.DataAsText); 
} 

回调函数始终接收原始HTTPRequest对象和保存服务器响应的HTTPResponse对象。如果出现错误,则HTTPResponse对象为null,需要自己判断,并且请求对象具有Exception属性,该属性可能包含有关错误的额外信息(如果有).
虽然请求总是在不同的线程上处理,但调用回调函数已完成Unity的主线程,所以我们不必做任何线程同步。

  • 3:其他请求 上面的例子是简单的GET请求。如果我们没有指定方法,默认情况下所有请求都将是GET请求。构造函数具有另一个参数,可用于指定请求的方法.
        HTTPRequest request = new HTTPRequest(new Uri("http://server.com/path"), HTTPMethods.Post, OnRequestFinished); 
        request.AddField("FieldName", "Field Value"); 
        request.Send(); 

要在不设置字段的情况下POST任何数据,可以使用RawData属性

HTTPRequest request = new HTTPRequest(new Uri("http://server.com/path"), HTTPMethods.Post, OnRequestFinished); 
request.RawData =  Encoding.UTF8.GetBytes("Field Value"); 
request.Send(); 

有关其他样品,请查看Small Code-Samples部分。

除了GET和POST之外,您还可以使用 HEAD,PUT,DELETE,PATCH 方法:

HTTPRequest request = new HTTPRequest(new Uri("http://server.com/path"), HTTPMethods.Head, OnRequestFinished); 
request.Send(); 

HTTPRequest request = new HTTPRequest(new Uri("http://server.com/path"), HTTPMethods.Put, OnRequestFinished); 
request.Send(); 

HTTPRequest request = new HTTPRequest(new Uri("http://server.com/path"), HTTPMethods.Delete, OnRequestFinished); 
request.Send(); 

HTTPRequest request = new HTTPRequest(new Uri("http://server.com/path"), HTTPMethods.Patch, OnRequestFinished); 
request.Send(); 
  • 4:如何使用下载的数据?可以从HTTPResponse对象的Data属性访问原始字节。我们来看一个如何下载图像的例子:
new HTTPRequest(new Uri("http://yourserver.com/path/to/image.png"), (request, response) => 
        { 
                var tex = new Texture2D(0, 0); 
                tex.LoadImage(response.Data); 
                guiTexture.texture = tex; 
        }).Send(); 

new HTTPRequest(new Uri("http://yourserver.com/path/to/image.png"),
(request, response) => guiTexture.texture = response.DataAsTexture2D).Send(); 

除 response.DataAsTexture2D 外,还有一个 response.DataAsText 属性可将响应解码为Utf8字符串。将来可能会添加更多数据解码属性。如果您有任何想法,请自己添加吧.

  • 5:替换 WWW,使用协程,一般不推荐使用这种方式
HTTPRequest request = new HTTPRequest(new Uri("http://server.com"));
request.Send(); 
yield return StartCoroutine(request); 
Debug.Log("Request finished! Downloaded Data:" + request.Response.DataAsText); 

高级用法

  • 1:methodType,我们将向服务器发送什么样的请求。默认的methodType是HTTPMethods.Get
  • 2:IsKeepAlive:向服务器指示我们希望tcp连接保持打开状态,因此连续的http请求不需要再次建立连接。如果我们将它保留为默认值true,它可以为我们节省大量时间。如果我们知道我们不会使用通常会将其设置为false的请求。默认值是true。
  • 3:disableCache:告诉BestHTTP系统使用或完全跳过缓存机制。如果其值为true,则系统不会检查缓存中是否存储了响应,并且也不会保存响应。默认值为false
  • 4:请求一次:
public static void RequestAsyncShort(string url, RequestCallBack callBack)
    {
        BestHTTP.HTTPRequest req = new HTTPRequest(new Uri(url), HTTPMethods.Get, 
            (originalRequest, response) =>
            {
                if (originalRequest.State == HTTPRequestStates.Finished)
                {
                    callBack((int) originalRequest.State, response.DataAsText);
                }
                else
                {
                    callBack((int) originalRequest.State, "");
                }
            });
        req.IsKeepAlive = false;
        req.DisableCache = true;
        req.Send();
    }
  • 5:Best HTTP通过HTTPRequest的Credentials属性支持Basic和Digest身份验证:
using BestHTTP.Authentication; 
var request = new HTTPRequest(new Uri("http://yourserver.org/auth-path"), (req, resp) => 
        { 
                if (resp.StatusCode != 401) 
                        Debug.Log("Authenticated"); 
                else 
                        Debug.Log("NOT Authenticated"); 
                Debug.Log(resp.DataAsText); 
        }); 
request.Credentials = new Credentials("usr", "paswd"); 
request.Send(); 
  • 6:下载流媒体(Download Streaming)
    默认情况下,当完全下载并处理服务器的答案时,我们提供给HTTPRequest的构造函数的回调函数将只调用一次。这样,如果我们想要下载更大的文件,我们就会在移动设备上快速耗尽内存。我们的应用程序会崩溃,用户会对我们生气,应用程序会得到很多不好的评级。理所当然。为了避免这种情况,BestHTTP旨在非常容易地处理这个问题:只需将一个标志(属性)切换为true,每次下载预定义数据量时(指定内存中缓存多少字节量)都会调用我们的回调函数。此外,如果我们没有关闭缓存,下载的响应将被缓存,以便下次我们可以从本地缓存流式传输整个响应,而无需更改我们的代码,甚至无需访问Web服务器。 (备注:服务器必须发送有效的缓存头(“Expires”头:请参阅RFC以允许此操作。)
var request = new HTTPRequest(new Uri("http://yourserver.com/bigfile"), (req, resp) => 
{ 
        List<byte[]> fragments = resp.GetStreamedFragments(); 
        // Write out the downloaded data to a file: 
        using (FileStream fs = new FileStream("pathToSave", FileMode.Append)) {
                foreach(byte[] data in fragments) fs.Write(data, 0, data.Length); 
        }
        if (resp.IsStreamingFinished) Debug.Log("Download finished!"); 
}); 
request.UseStreaming = true; 
request.StreamFragmentSize = 1 * 1024 * 1024; // 1 megabyte
request.DisableCache = true; // already saving to a file, so turn off caching request.Send();  
    1):我们将标志 - UseStreaming - 切换为true,因此我们的回调可能被调用多次。
    2):StreamFragmentSize指示在调用回调之前我们想要在内存中缓冲的最大数据量。
    3):每次下载StreamFragmentSize大小的块时都会调用我们的回调,并且当IsStreamingFinished设置为true时再调用一次。
    4):要获取下载的数据,我们必须使用GetStreamedFragments()函数。我们应该将结果保存在临时变量中,因为在此调用中清除了内部缓冲区(返回字节量,清除内存,写入文件),因此连续调用将为我们提供null结果。
    5):我们在此示例中禁用了缓存,因为我们已经保存了下载的缓存,保存在一个磁盘文件中,并且我们不想占用太多空间。

下载进度

var request = new HTTPRequest(new Uri(address), OnFinished); 
request.OnProgress = OnDownloadProgress; 
request.Send(); 
void OnDownloadProgress(HTTPRequest request, int downloaded, int length) 
{ 
        float progressPercent = (downloaded / (float)length) * 100.0f;
        Debug.Log("Downloaded: " + progressPercent.ToString("F2") + "%"); 
} 
  • 7:上传流媒体(Upload Streaming)
    使用HTTPRequest对象的UploadStream属性设置上传的数据流Stream。当上传完成并且DisposeUploadStream为true时,插件将调用Stream流上的Dispose()函数。如果流的长度未知,则UseUploadStreamLength属性应设置为false。在这种情况下,插件将使用分块传输编码从流中发送数据:
var request = new HTTPRequest(new Uri(address), HTTPMethods.Post, OnUploadFinished); 
request.UploadStream = new FileStream("File_To.Upload", FileMode.Open); 
request.Send(); 

上传进度回调,要跟踪和显示上传进度,您可以使用HTTPRequest类的OnUploadProgress事件。 OnUploadProgress可以与RawData,表单(通过AddField和AddBinaryData)以及UploadStream一起使用。

var request = new HTTPRequest(new Uri(address), HTTPMethods.Post, OnFinished); 
request.RawData =  Encoding.UTF8.GetBytes("Field Value");
request.OnUploadProgress = OnUploadProgress; 
request.Send(); 
void OnUploadProgress(HTTPRequest request, long uploaded, long length)
{ 
        float progressPercent = (uploaded / (float)length) * 100.0f;
        Debug.Log("Uploaded: " + progressPercent.ToString("F2") + "%"); 
}
  • 8:缓存

缓存也基于HTTP / 1.1 RFC。它使用标头来存储和验证响应。缓存机制在幕后工作,我们唯一要做的就是决定是否要启用或禁用它。如果缓存的响应具有带有未来日期的“Expires”标头,则BestHTTP将使用缓存的响应,而不对服务器进行验证。这意味着我们不必启动与服务器的任何tcp连接。这可以节省我们的时间,带宽和离线工作。 (这段话的意思相当于本地数据缓存,请求服务器,不会真的连接服务器,会从本地取出原来已经请求过的数据,只要这个数据没有过期,则直接返回给使用者)

虽然缓存是自动的,但我们可以控制它,或者我们可以使用HTTPCacheService类的公共函数获取一些信息:

    1):BeginClear(),它将开始在单独的线程上清除整个缓存
    2):BeginMaintainence(),有了这个函数的帮助,我们可以根据上次访问时间删除缓存的条目。它删除上次访问时间早于指定时间的条目。我们还可以使用此函数来控制缓存大小:
            HTTPCacheService.BeginMaintainence(new HTTPCacheMaintananceParams(TimeSpan.FromDays(14), 50 * 1024 * 1024)); 
    3):GetCacheSize(),将以字节为单位返回缓存的大小。
    4):GetCacheEntryCount(),将返回缓存中存储的条目数。可以使用float avgSize = GetCacheSize()/(float)GetCacheEntryCount()公式计算平均缓存条目大小。
  • 9:Cookie ,处理cookie操作对程序员来说是透明的。设置请求Cookie标头以及解析和维护响应的Set-Cookie标头由插件自动完成。有很多关于cookie的Global Settings 。有关详细信息,请参阅 Global Settings 部分。
    它可以以各种方式控制
    1):使用每一次的请求对象HTTPRequest.IsCookiesEnabled属性以及全局的HTTPManager.IsCookiesEnabled属性来禁用
    2):可以通过调用CookieJar.Clear()函数从Cookie Jar中删除Cookie
    3):可以通过响应的Cookies属性访问从服务器发送的新cookie。

可以通过将Cookie添加到Cookie列表中将Cookie添加到HTTPRequest:

var request = new HTTPRequest(new Uri(address), OnFinished);
request.Cookies.Add(new Cookie("Name", "Value")); 
request.Send(); 

这些cookie将与服务器发送的cookie合并。如果在请求或HTTPManager中将IsCookiesEnabled设置为false,则仅发送这些用户设置的cookie

  • 10:代理,HTTPProxy对象可以设置为HTTPRequest的Proxy属性。这样,请求将通过给定的代理。
request.Proxy = new HTTPProxy(new Uri("http://localhost:3128")); 

您也可以设置全局代理,因此您不必手动将其设置为所有请求。请参阅Global Settings一章

  • 11:终止请求,您可以通过调用HTTPRequest对象的Abort()函数来中止正在进行的请求
request = new HTTPRequest(new Uri("http://yourserver.com/bigfile"), (req, resp) => { ... }); 
request.Send(); 
// And after some time: 
request.Abort(); //将调用回调函数,并且响应对象(resp)将为null。
  • 12:超时,2 种情况,第一种是:ConnectTimeout,使用此属性,您可以控制等待在应用程序和远程服务器之间建立连接的时间。其默认值为20秒
request = new HTTPRequest(new Uri("http://yourserver.com/"), (req, resp) => { ... }); 
request.ConnectTimeout = TimeSpan.FromSeconds(2); 
request.Send(); 

第二种:Timeout 使用此属性,您可以控制等待处理请求的时间(此时已连接到服务器,正在等待服务器响应,发送请求和下载响应)。其默认值为60秒。

request = new HTTPRequest(new Uri("http://yourserver.com/"), (req, resp) => { ... }); 
request.Timeout = TimeSpan.FromSeconds(10); 
request.Send(); 



string url = "http://besthttp.azurewebsites.net/api/LeaderboardTest?from=0&count=10"; 
HTTPRequest request = new HTTPRequest(new Uri(url), (req, resp) => 
 { 
         switch (req.State) 
         { 
                 // The request finished without any problem. 
                 case HTTPRequestStates.Finished: 
                        Debug.Log("Request Finished Successfully!\n" + resp.DataAsText); 
                        break; 
                        // The request finished with an unexpected error. 
                // The request's Exception property may contain more information about the error. 
                case HTTPRequestStates.Error: 
                        Debug.LogError("Request Finished with Error! " + (req.Exception != null ? (req.Exception.Message + "\n" + req.Exception.StackTrace) : "No Exception")); 
                break; 
                // The request aborted, initiated by the user. 
                case HTTPRequestStates.Aborted: 
                        Debug.LogWarning("Request Aborted!"); 
                break; 
                // Ceonnecting to the server timed out. 
                case HTTPRequestStates.ConnectionTimedOut: 
                        Debug.LogError("Connection Timed Out!"); 
                break; 
                // The request didn't finished in the given time. 
                case HTTPRequestStates.TimedOut: 
                        Debug.LogError("Processing the request Timed Out!"); break; 
        } 
}); 
// Very little time, for testing purposes: 
request.ConnectTimeout = TimeSpan.FromMilliseconds(2); 
request.Timeout = TimeSpan.FromSeconds(5); 
request.IsKeepAlive = false; 
request.DisableCache = true; 
request.Send(); 
  • 13:Request States 请求状态,所有请求都有一个包含其内部状态的State属性。可能的状态如下:
    1):Initial:请求的初始状态。使用此状态不会调用任何回调
    2):Queued:在队列中等待处理。使用此状态不会调用任何回调
    3):Processing:开始处理请求。在此状态下,客户端将发送请求,并解析响应。使用此状态不会调用任何回调。
    4):Finished:请求完成没有问题。解析完成的响应后,可以使用结果。将使用有效的响应对象调用用户定义的回调。请求的Exception属性将为null。
    5):Error:请求在插件中以意外错误结束。将使用null响应对象调用用户定义的回调。请求的Exception属性可能包含有关错误的更多信息,但它可以为null。
    6):Aborted: 请求由客户端中止(HTTPRequest的Abort()函数)。将使用null响应调用用户定义的回调。请求的Exception属性将为null。
    7):ConnectionTimedOut:连接到服务器超时。将使用null响应调用用户定义的回调。请求的Exception属性将为null。
    8):TimedOut:请求未在给定时间内完成。将使用null响应调用用户定义的回调。请求的Exception属性将为null。
  • 14:请求的优先级Request Priority ,可以通过HTTPRequest的Priority属性更改请求的优先级。与较低优先级请求相比,将从请求队列中选择更高优先级的请求。
var request = new HTTPRequest(new Uri("https://google.com"), ...);
request.Priority = -1; 
request.Send(); 
  • 15:服务器证书验证,Server Certificate Validation 可以通过实现ICertificateVerifyer接口并将其设置为HTTPRequest的CustomCertificateVerifyer来验证服务器发送的证书:
using System; 
using Org.BouncyCastle.Crypto.Tls; 
using Org.BouncyCastle.Asn1.X509; 
class CustomVerifier : ICertificateVerifyer 
{ 
        public bool IsValid(Uri serverUri, X509CertificateStructure[] certs) 
        { 
        // TODO: Return false, if validation fails return true; 
        } 
} 
var request = new HTTPRequest(new Uri("https://google.com"), ...);
request.CustomCertificateVerifyer = new CustomVerifier(); 
request.UseAlternateSSL = true; 
request.Send(); 
  • 16:控制重定向,Control Redirections ,重定向由插件自动处理,但有时我们必须在向我们重定向到的uri发出新请求之前进行更改。我们可以在HTTPRequest的OnBeforeRedirection事件处理程序中进行这些更改。在插件向新uri发出新请求之前调用此事件。函数的返回值将控制重定向:如果为false,则重定向将被中止,子线程中调用
var request = new HTTPRequest(uri, HTTPMethods.Post); 
request.AddField("field", "data"); 
request.OnBeforeRedirection += OnBeforeRedirect; 
request.Send(); 
bool OnBeforeRedirect(HTTPRequest req, HTTPResponse resp, Uri redirectUri) 
{ 
        if (req.MethodType == HTTPMethods.Post && resp.StatusCode == 302) 
        { 
                req.MethodType = HTTPMethods.Get; 
                // Don't send more data than needed. 
                // So we will delete our already processed form data.
                req.Clear(); 
        } 
        return true; 
} 
  • 17:统计(Statistics),您可以使用HTTPManager.GetGeneralStatistics函数获取有关底层插件的一些统计信息:
GeneralStatistics stats = HTTPManager.GetGeneralStatistics(StatisticsQueryFlags.All); Debug.Log(stats.ActiveConnections); 
    1):Connections:将返回基于连接的统计信息。这些是以下内容:
            A:RequestsInQueue:队列中等待空闲连接的请求数。
            B:Connections:插件跟踪的HTTPConnection实例数。这是以下所有连接的总和
            B:ActiveConnections:活动连接数。这些连接当前正在处理请求。
            C:FreeConnections:免费连接数。这些连接完成了请求,他们正在等待另一个请求或回收。
            D:RecycledConnections:回收连接数。这些连接将尽快删除。
    2):Cache:基于缓存的统计信息这些是以下内容:
            A:CacheEntityCount:缓存响应的数量。
            B:CacheSize:缓存响应的总和大小
    3):Cookie:基于Cookie的统计信息。这些是以下内容
            A:CookieCount:Cookie Jar中的Cookie数量
            B:CookieJarSize:Cookie Jar中Cookie的总和大小

Global Settings

  • 1:使用以下属性,我们可以更改一些默认值,否则应在HTTPRequest的构造函数中指定。因此,大多数这些属性都是节省时间的快捷方式。
  • 2:这些更改将影响其值更改后创建的所有请求。可以通过HTTPManager类的静态属性更改默认值:
    1):MaxConnectionPerServer:允许唯一主机的连接数。 http://example.org和https://example.org被视为两个独立的服务器。默认值为4。
    2):KeepAliveDefaultValue:HTTPRequest的IsKeepAlive属性的默认值。如果IsKeepAlive为false,则将在每个请求之前设置与服务器的tcp连接,并在其之后立即关闭。如果连续请求很少,则应将其更改为false。赋予HTTPRequest构造函数的值将仅覆盖此请求的此值。默认值是true。
    3):IsCachingDisabled:使用此属性,我们可以全局禁用或启用缓存服务。赋予HTTPRequest构造函数的值将仅覆盖此请求的此值。默认值是true。
    4):MaxConnectionIdleTime:指定BestHTTP在完成最后一次请求后销毁连接之前应等待的空闲时间。默认值为2分钟。
    5):IsCookiesEnabled:使用此选项,可以启用或禁用所有Cookie操作。默认值是true。
    6):CookieJarSize:使用此选项可以控制Cookie存储的大小。默认值为10485760(10 MB).
    7):EnablePrivateBrowsing:如果启用此选项,则不会将Cookie写入磁盘。默认值为false
    8):ConnectTimeout:使用此选项,您可以设置HTTPRequests的默认ConnectTimeout值。默认值为20秒。
    9):RequestTimeout:使用此选项,您可以设置HTTPRequests.Timeout的默认超时值。默认值为60秒。
    10):RootCacheFolderProvider:默认情况下,插件会将所有缓存和cookie数据保存在Application.persistentDataPath返回的路径下。您可以为此委托指定一个函数,以返回自定义根路径以定义新路径。这个代理将在子线程上调用!
    11):Proxy:所有HTTPRequests的全局默认代理。 HTTPRequest的代理仍然可以按请求进行更改。默认值为null
    12):Logger:ILogger实现,能够控制将记录有关插件内部的信息,以及如何记录这些信息
    13):DefaultCertificateVerifyer:可以将ICertificateVerifyer实现设置为此属性。之后创建的所有新请求将在使用安全协议且请求的UseAlternateSSL为true时使用此验证程序。 ICertificateVerifyer实现可用于实现服务器证书验证。
    14):UseAlternateSSLDefaultValue:可以通过此属性更改HTTPRequest的UseAlternateSSL的默认值。
    15):HTTPManager.MaxConnectionPerServer = 10; HTTPManager.RequestTimeout = TimeSpan.FromSeconds(120); 

关于线程

  • 1:因为插件内部使用线程并行处理所有请求,所以所有共享资源(缓存,cookie等)都是在设计和实现线程安全时考虑的。
  • 2:调用请求的回调函数和所有其他回调(如WebSocket的回调)都是在Unity的主线程上回调(如Unity的事件:awake, start, update, etc),因此您不必进行任何线程同步。
  • 3:在多个线程上创建,发送请求也是安全的,但是你应该调用BestHTTP.HTTPManager.Setup();在从Unity的一个事件(例如,awake,start)发送任何请求之前发挥作用。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,233评论 4 360
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,013评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,030评论 0 241
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,827评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,221评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,542评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,814评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,513评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,225评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,497评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,998评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,342评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,986评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,055评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,812评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,560评论 2 271
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,461评论 2 266