这几天一直在写Unity当中的C#的网络传输,可是发现因为Unity采用的mono框架,支持的仅仅是.Net Framework 3.5的API,并不支持更高级别的HttpClient,则不少功能须要手动开发。最先简单测试过GET数据,而且JSON文件反序列化为对象成功。以后实现了POST的JSON文件,获取返回的JSON,因此麻痹大意,致使在uploadfile这个环节花费了大量的时间。apache
笔者认为apache commons的库设计很是好,因此仿照了httpclient进行了设计。json
└─Utils ├─GlobalConfig │ UnityTemplate.cs - 用来存储Unity项目规范结构 │ ├─HttpHelper │ │ HttpHelper.cs - 针对访问http程序的主要方法 │ │ │ ├─http │ │ HttpEntity.cs - HTTP Body中的内容 │ │ HttpHeader.cs - HTTP Headers │ │ │ └─methods │ HttpGet.cs - Get方法 │ HttpPost.cs - Post方法 │ HttpRequestBase.cs - 公共继承方法 │ └─JsonHelper - 另外的库,用来处理Json序列化与反序列化
根据HttpClient方法,因为已经可以确认.net core开源版本已经集成HttpClient,若是将来Unity何时集成core,则不须要考虑如今的方法,则把如今的方法命名为HttpHelper
HttpHelper主要方法为execute,模仿httpclient,参数是存入的get或者post内容。因为返回内容不肯定是键值对仍是json,则直接返回HttpWebResponse浏览器
public static HttpWebResponse execute(HttpRequestBase methods)
罗列了简单的http header信息,并设置默认值,方便使用。网络
public class HttpHeader { // 默认值通过http编码 public string ContentType = "application/x- www-form-urlencoded"; public string UserAgent = "Unity/HttpHelper"; public long ContentLength; public System.Net.IWebProxy Proxy = null; public bool KeepAlive = true; }
因为C#只可以经过ArrayList或者词典,对不定类型的对象进行存储,因此这里使用了ArrayList存储有可能发生的多种类型。
因为即使是多种类型,依旧是采用键值对的方式进行访问(json不经过键值对,不过会以键值对的方式声明。)app
using System.Collections; using System.Collections.Generic; using System.IO; public class HttpEntity { private ArrayList entitys; public HttpEntity() { this.entitys = new ArrayList(); } public void setKeyValuePair(KeyValuePair<string, long> keyValuePair) { this.entitys.Add(keyValuePair); } public void setKeyValuePair(KeyValuePair<string,int> keyValuePair) { this.entitys.Add(keyValuePair); } public void setKeyValuePair(KeyValuePair<string,string> keyValuePair) { this.entitys.Add(keyValuePair); } public void setKeyValuePair(KeyValuePair<string,FileInfo> keyValuePair) { this.entitys.Add(keyValuePair); } public ArrayList getHttpElements() { return this.entitys; } public long getContentLength() { return -1; } public bool hasFile() { foreach(object item in this.entitys) { if (item is KeyValuePair<string, FileInfo>) { return true; } } return false; } }
根据http的需求,须要生成分隔符,可是根据浏览器的建议,不会真正生成一个和文件内容毫无关系的字符串做为分隔符。这样作的缘由是要花费大量的时间去解决分隔符冲突的问题。因此通用作法是随机找一个尽量复杂的分隔符下降重复的几率。
做者以guid的方式生成分隔符,使分隔符更复杂。框架
// 生成分隔符 string boundary = Guid.NewGuid().ToString("N");
KeyValuePair<string, FileInfo> kv = (KeyValuePair<string, FileInfo>)item; // HTTP规定,起始的分割线 string fileboundary = "--" + boundary + "\r\n"; httpbody_length += getLength(fileboundary); // HTTP规定,Content-Disposition string ContentDisposition = "Content-Disposition: form-data; name=\"" + kv.Key + "\"; filename=\"" + kv.Value.Name + "\"\r\n"; httpbody_length += getLength(ContentDisposition); // HTTP规定,ContentType string ContentType = "Content-Type: application/octet-stream" + "\r\n"; httpbody_length += getLength(ContentType); // HTTP规定,ContentTransferEncoding string ContentTransferEncoding = "ContentTransferEncoding:binary" + "\r\n"; httpbody_length += getLength(ContentTransferEncoding); // 区分文件头与文件内容 string separator = "\r\n"; httpbody_length += getLength(separator); // 获取文件长度 FileStream fileStream = new FileStream(kv.Value.FullName, FileMode.Open, FileAccess.Read); httpbody_length += fileStream.Length; fileStream.Close(); // 再次添加separator httpbody_length += getLength(separator);
HttpWebRequest必须先设置ContentLength这个参数,而后才可以经过GetRequestStream获取requestStream。
阅读API,描述为必须这样,猜想实现是根据ContentLength对Stream进行初始化致使的。这种方式直接致使,没法经过FileStream的大小来判断最终的ContentLength大小。
解决方法:post
// 先计算body的长度,在写入stream中 request.ContentLength = httpbody_length; // get http body bytes Stream requestStream = request.GetRequestStream();
在Unity端报400 request bad,因为客户端的限制,实质是看不到Unity的实际报错内容。则须要到服务端去查看错误内容。测试
因为须要手动编写http的body规则,手误致使编写错误,而且没有看到,致使调试好久。ui