我们这个Web服务器有了一个基本的门面以后,我们是时候来用它做点实际的事情了。还记得我们最早提到HTTP协议的用途是什么吗?它叫超文本传输协议啊,所以我们必须考虑让我们的服务器能够接收到客户端传来的数据。因为我们目前完成了大部分的工作,所以对数据传输这个问题我们这里选择以最简单的GET和POST为例来实现,这样我们今天的重点就落实在Get和Post的实现这个问题上来。而从原理上来讲,无论Get方式请求还是Post方式请求,我们都可以在请求报文中获得其请求参数,不同的是前者出现在请求行中,而后者出现在消息体中。例如我们传递的两个参数num1和num2对应的数值分别是12和24,那么在具体的请求报文中我们都能找到类似“num1=12&num2=24”这样的字符结构,所以只要针对这个字符结构进行解析,就可以获得客户端传递给服务器的参数啦。
首先我们来实现Get请求,Get是HTTP协议中默认的请求类型,我们平时访问网页、请求资源实际上都是通过Get方式实现的。Get方式请求需要通过类似“?id=001&option=10”这样的形式附加在URL上,因此Get方式对浏览器来说是透明的,即用户可以通过浏览器地址栏知道,这个过程中传递了哪些参数以及这些参数的值分别是什么。而由于浏览器的限制,我们通过这种方式请求的时候能够传递的参数数目和长度都是有限的,而且当参数中存在中文数值的时候还需要对其进行编码。Get方式请求相对简单,我们下面来看看它的请求报文:
GET /?num1=23&num2=12 HTTP/1.1 Accept: text/html, application/xhtml+xml, image/jxr, */* Accept-Language: zh-Hans-CN,zh-Hans;q=0.5 User-Agent: Mozilla/5.0 (Windows NT 10.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2486.0 Safari/537.36 Edge/13.10586 Accept-Encoding: gzip, deflate Host: localhost:4040 Connection: Keep-Alive Cookie: _ga=GA1.1.1181222800.1463541781 1 2 3 4 5 6 7 8
此时我们可以注意到在请求报文第一行,即请求行中出现了“/?num1=23&num2=12”这样的字样,这就是客户端传递给服务器的参数,我们很容易想到只需要将这个字段串中的“键”和“值”都解析出来,服务器就可以对这些数据进行处理然后返回给客户端了。所以下面我们通过这样的方式来实现,我们为HtttpRequest类增加了一个Parms属性,它是一个键和值均为字符串类型的字典,我们使用这个字典来存储和管理客户端传递来的参数。
//获取请求参数 if(this.Method=="GET" && this.URL.Contains('?')) this.Params=GetRequestParams(lines[0].Split(' ')[1].Split('?')[1]); 1 2 3
显然我们首先需要判断请求类型是否为GET以及请求中是否带有参数,其方法是判断请求地址中是否含有“?”字符。这里的lines是指将报文信息按行分割以后的数组,显然请求地址在第一行,所以我们根据“?”分割该行数据以后就可以得到“num1=23&num2=12”这样的结果,这里我们使用一个方法GetRequestParms来返回参数字典,这样作做是为了复用方法,因为在处理Post请求的时候我们会继续使用这个方法。该方法定义如下:
/// <summary> /// 从内容中解析请求参数并返回一个字典 /// </summary> /// <param name="content">使用&连接的参数字符串</param> /// <returns>如果存在参数则返回参数否则返回null</returns> protected Dictionary<string, string> GetRequestParams(string content) { //防御编程 if(string.IsNullOrEmpty(content)) return null; //按照&对字符进行分割 string[] reval=content.Split('&'); if(reval.Length <=0) return null; //将结果添加至字典 Dictionary<string, string> dict=new Dictionary<string, string>(); foreach(string val in reval) { string[] kv=val.Split('='); if(kv.Length <=1) dict.Add(kv[0], ""); dict.Add(kv[0],kv[1]); } //返回字典 return dict; } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
Post请求相对Get请求比较安全,因为它克服了Get请求参数长度的限制问题,而且由于它的参数是存放在消息体中的,所以在传递参数的时候对用户而言是不可见的,我们平时接触到的网站登录都是这种类型,而复杂点的网站会通过验证码、Cookie等形式来避免爬虫程序模拟登录,在Web开发中Post请求可以由一个表单发起,可以由爬虫程序如HttpWebRequest、WebClient等发起,下面我们重点来分析它的请求报文:
POST / HTTP/1.1 Accept: text/html, application/xhtml+xml, image/jxr, */* Accept-Language: zh-Hans-CN,zh-Hans;q=0.5 User-Agent: Mozilla/5.0 (Windows NT 10.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2486.0 Safari/537.36 Edge/13.10586 Accept-Encoding: gzip, deflate Host: localhost:4040 Connection: Keep-Alive Cookie: _ga=GA1.1.1181222800.1463541781 num1=23&num2=12 1 2 3 4 5 6 7 8 9 10
我们可以注意到此时请求行的请求方法变成了POST,而在报文结尾增加了一行内容,我们称其为“消息体”,这是一个可选的内容,请注意它前面有一个空行。所以,当我们处理一个Posst请求的时候,通过最后一行就可以解析出客户端传递过来的参数,和Get请求相同,我们这里继续使用GetRequestParams来完成解析。
if(this.Method=="POST") this.Params=GetRequestParams(lines[lines.Length-1]); 1 2
现在我们来完成一个简单地实例,服务器自然由我们这里设计的这个服务器来完成咯,而客户端则由Unity来完成因为Unity有简单的WWW可以使用。首先来编写服务端,这个继承HttpServer就好了,我们主要来写这里的方法:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using HttpServerLib; using System.IO; namespace HttpServer { public class ExampleServer : HttpServerLib.HttpServer { /// <summary> /// 构造函数 /// </summary> /// <param name="ipAddress">IP地址</param> /// <param name="port">端口号</param> public ExampleServer(string ipAddress, int port) : base(ipAddress, port) { } public override void OnPost(HttpRequest request) { //获取客户端传递的参数 int num1=int.Parse(request.Params["num1"]); int num2=int.Parse(request.Params["num2"]); //设置返回信息 string content=string.Format("这是通过Post方式返回的数据:num1={0},num2={1}",num1,num2); //构造响应报文 HttpResponse response=new HttpResponse(content, Encoding.UTF8); response.StatusCode="200"; response.Content_Type="text/html; charset=UTF-8"; response.Server="ExampleServer"; //发送响应 ProcessResponse(request.Handler, response); } public override void OnGet(HttpRequest request) { //获取客户端传递的参数 int num1=int.Parse(request.Params["num1"]); int num2=int.Parse(request.Params["num2"]); //设置返回信息 string content=string.Format("这是通过Get方式返回的数据:num1={0},num2={1}",num1,num2); //构造响应报文 HttpResponse response=new HttpResponse(content, Encoding.UTF8); response.StatusCode="200"; response.Content_Type="text/html; charset=UTF-8"; response.Server="ExampleServer"; //发送响应 ProcessResponse(request.Handler, response); } } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62
因为这里需要对Get和Post进行响应,所以我们这里对OnGet和OnPost两个方法进行了重写,这里的处理方式非常简单,按照一定格式返回数据即可。下面我们来说说Unity作为客户端这边要做的工作。WWW是Unity3D中提供的一个简单的HTTP协议的封装类,它和.NET平台下的WebClient、HttpWebRequest/HttpWebResponse类似,都可以处理常见的HTTP请求如Get和Post这两种请求方式。
WWW的优势主要是简单易用和支持协程,尤其是Unity3D中的协程(Coroutine)这个特性,如果能够得到良好的使用,常常能够起到事倍功半的效果。因为WWW强调的是以HTTP短链接为主的易用性,所以相应地在超时、Cookie等HTTP头部字段支持的完整性上无法和WebClient、HttpWebRequest/HttpWebRespons相提并论,当我们需要更复杂的HTTP协议支持的时候,选择在WebClient、HttpWebRequest/HttpWebResponse上进行深度定制将会是一个不错的选择。我们这里需要的是发起一个简单的HTTP请求,所以使用WWW完全可以满足我们的要求,首先我们来看在Unity3D中如何发起一个Get请求,这里给出一个简单的代码示例:
//采用GET方式请求数据 IEnumerator Get() { WWW www=new WWW ("http://127.0.0.1:4040/?num1=12&num2=23"); yield return www; Debug.Log(www.text); } 1 2 3 4 5 6 7
现在我们是需要使用StartCoroutine调用这个方法就可以啦!同样地,对于Post请求,我们这里采用一个WWWForm来封装参数,而在网页开发中我们通常都是借助表单来向服务器传递参数的,这里给出同样简单的代码示例:
//采用POST方式请求数据 IEnumerator Post() { WWWForm form=new WWWForm (); form.AddField ("num1", 12); form.AddField ("num2", 23); WWW www=new WWW ("http://127.0.0.1:4040/", form); yield return www; Debug.Log (www.text); } 1 2 3 4 5 6 7 8 9 10
而运行这个实例,我们可以得到下面的结果:
都是谁告诉你做服务器开发一定要用Java的啊,现在我们可以写出自己的服务器了,本篇结束
访问中需要用到HTTPRequest组件,HTTPRequest发起HTTP请求,异步回调返回HTTPResponse。本篇就来给大家分享下在项目开发中HTTP访问和下载的实现。
在unity中,www类虽然满足了很多需求但也失去了一部分自由,下面我们用C#实现发送post请求,传递表单,并获取或设置cookie。
private IEnumerator Login(string _url)
{
//设置链接
Uri url=new Uri(_url);
//设置http请求
HttpWebRequest request=(HttpWebRequest)HttpWebRequest.Create(url);
request.Method="POST";
//表单数据
byte[] _data=Encoding.UTF8.GetBytes("account=" + "CarefreeQ" + "&password=" + "CarefreeQ");
//内容类型
request.ContentType="application/x-www-form-urlencoded";
//内容长度
request.ContentLength=_data.Length;
//设置cookie,如要获取则不能为空
request.CookieContainer=new CookieContainer();
//创建流
Stream stream=request.GetRequestStream();
//写入数据
stream.Write(_data, 0, _data.Length);
stream.Close();
//开始接收响应
HttpWebResponse response=(HttpWebResponse)request.GetResponse();
//获取cookie
string cookie=request.CookieContainer.GetCookieHeader(url);
//接收流
stream=response.GetResponseStream();
//内容长度
int max=(int)response.ContentLength;
int len=0;
//数据长度
_data=new byte[max];
while (len < max)
{
//写入响应数据
int _len=stream.Read(_data, len, _data.Length);
len +=_len;
yield return new WaitForEndOfFrame();
}
//读取数据
string text=Encoding.UTF8.GetString(_data);
}
在Unity的开发中,我们经常会把资源保存到网上,比如一些网格,assetbundle,配置文件,贴图等等。我们希望在用户使用时下载到本地。
privateIEnumeratorDownload(string_url)
{
//设置保存路径
stringpath="自定义目录";
//这个方法可以新建一个线程运行,来提高效率和降低卡顿,这里就不写了
Uriurl=newUri(_url);
//创建接受
WebRequestrequest=WebRequest.Create(url);
//以下为接收响应的方法
WebResponseresponse=request.GetResponse();
//创建接收流
Streamstream=response.GetResponseStream();
//检查目录是否存在,不存在则创建
stringdir=path.Substring(0,path.LastIndexOf("/"));
if(!Directory.Exists(dir))
Directory.CreateDirectory(dir);
//文件写入路径
FileStreamfile=newFileStream(path,FileMode.OpenOrCreate,FileAccess.Write);
//返回内容总长度
intmax=(int)response.ContentLength;
intlen=0;
while(len<max)
{
//byte容器
byte[]data=newbyte[10240000];
//循环读取
int_len=stream.Read(data,0,data.Length);
//写入文件
file.Write(data,0,_len);
len+=_len;
//如果需要传递进度,可以这样算
//floatprogress=len/(float)max;
yieldreturnnewWaitForEndOfFrame();
}
//写入完后关闭文件流和接收流
file.Close();
stream.Close();
}
最后,以上就是给大家分享的关于HTTP访问和下载实现,希望能对大家有所帮助。
GDC 2019大会上,Unity的Demo小组展示了一段名为“异教徒”的小短片。这是一段使用Unity引擎实时演算的技术demo,游戏的画面逼真得让人难以置信。值得一提的是,这个demo是在家用消费级的台式机PC上运行的,游戏的画面为1440P/30帧,好了,让我们一起来看看吧!
这段“异教徒”技术demo演示是用最新版Unity引擎的“post-processing(后处理)”功能制作的,使用这一功能,Unity引擎为demo画面加上了动态模糊、高光、景深、胶片颗粒、色彩分级等画面特效。实现了无比逼真的画面效果。
这段技术Demo中的光线效果全部都是实时演算的,它使用了移动检测技术的光线解决方案。Demo最终呈现出来画面让人惊讶,而Unity也承诺不久之后就会放出完整版的短片。
视频截图:
*请认真填写需求信息,我们会在24小时内与您取得联系。