这篇主要是提供完整的代码。。。注释较少请见谅,如果对流程有疑惑可以参考**《Unity游戏开发 基于多线程的Http网络框架基础篇》
完整篇除了基础篇讲到的内容之外,出于对性能和安全性的考虑还覆盖了如下内容:
单例:(BaseController)
**关于单例请参考《Unity游戏开发 单例基类》
对象池: (Poolable、PoolManager)
加密:(Encry)
这些内容后续有时间写的话会把链接放上来,目前把所有代码贴出来主要是方便直接拷贝就可以使用了。
在更完整的版本里还有一个事件分发的处理,后续如果有时间写UI框架的话会把这块关联起来。
使用示例
HttpManager(http管理器)
HttpClient(http核心逻辑)
HttpPack(http数据包)
BlockQueue(多线程队列)
Encry(加密)
BaseController(单例)
Poolable(对象池之可缓存对象)
PoolManager(对象池管理器)
void Start()
{
// 发起请求
HttpManager.instance.HttpPost(1, "http://es.dota2.uuu9.com/players", false);
}
int id = 0; int code = 0; string data = "";
void Update()
{
// 从接受队列取得消息(此处本来是和lua的http模块以及ui框架结合做错误码处理以及网络事件分发的)
if(HttpManager.instance.TryGetResPonse(ref id, ref code, ref data))
{
UnityEngine.Debug.LogError(data);
}
}
这里加密传了false,如果传入true的话会将返回的消息用Encry模块去解密。
这个需要服务器的配合,两边约定好加密的密钥。
目前只是对接收到的数据进行了解密处理,如果有需要的话也可以和服务器做协商,在发送的json参数中用某个字段将实际游戏中用到的参数通过Encry加密后保存起来发送给服务器,服务器解密后再做处理。
using System.Text;
public enum HttpType
{
Get,
Post,
}
public class HttpManager : BaseController
{
private HttpClient _httpClient = new HttpClient();
private StringBuilder _getBuilder = new StringBuilder();
public void HttpGet(int key, string url, bool encry, string param = null)
{
_getBuilder.Clear();
_getBuilder.Append(url);
if (param != null)
{
_getBuilder.Append("?");
_getBuilder.Append(param);
}
_httpClient.AddHttpReq(key, HttpType.Get, _getBuilder.ToString(), encry);
}
public void HttpPost(int key, string url, bool encry, string param = null)
{
_getBuilder.Clear();
_getBuilder.Append(url);
_httpClient.AddHttpReq(key, HttpType.Post, _getBuilder.ToString(), encry, param);
}
public bool TryGetResPonse(ref int key, ref int code, ref string data)
{
HttpPack pack;
if (_httpClient.TryGetResponse(out pack))
{
key = pack.key;
code = pack.code;
data = pack.response;
pack.Dispose();
return true;
}
return false;
}
}
using System.Net;
using System.Text;
using System.IO;
using System.Threading;
using System;
public class HttpClient
{
private Encry _encry = new Encry("testkey");
private Thread _sendTask;
private BlockQueue _sendList = new BlockQueue(10);
private BlockQueue _receiveList = new BlockQueue(10);
#region 外部接口
public bool TryGetResponse(out HttpPack pack)
{
if (_receiveList.count > 0)
{
pack = _receiveList.Dequeue();
return true;
}
else
{
pack = null;
return false;
}
}
public void AddHttpReq(int key, HttpType type, string url, bool encry, string param = null)
{
HttpPack pack = HttpPack.GetPooled();
pack.key = key;
pack.type = type;
pack.url = url;
pack.param = param;
pack.encry = encry;
pack.tryNum = 3;
_sendList.Enqueue(pack);
}
#endregion
public HttpClient()
{
_sendTask = StartDaemonThread(StartSend);
}
private static Thread StartDaemonThread(ThreadStart threadMethod)
{
Thread thread = new Thread(threadMethod);
thread.IsBackground = true;
thread.Start();
return thread;
}
private void StartSend()
{
while (true)
{
HandleSend();
}
}
private void HandleSend()
{
if (_sendList.count > 0)
{
HttpPack pack = _sendList.Dequeue();
switch (pack.type)
{
case HttpType.Get:
{
Get(ref pack, pack.url, pack.encry);
HandleRecive(ref pack);
return;
}
case HttpType.Post:
{
Post(ref pack, pack.url, pack.param, pack.encry);
HandleRecive(ref pack);
return;
}
}
}
}
// 处理接收
private void HandleRecive(ref HttpPack pack)
{
// 异常时重试
if (pack.code != 200 && pack.tryNum > 0)
{
pack.tryNum--;
_sendList.Enqueue(pack);
}
else
{
_receiveList.Enqueue(pack);
}
}
private void Get(ref HttpPack pack, string url, bool encry, int timeout = 2000, Encoding encode = null)
{
HttpWebRequest req = null;
HttpWebResponse res = null;
req = (HttpWebRequest)WebRequest.Create(url);
req.AllowAutoRedirect = false;
req.Timeout = timeout;
try
{
res = (HttpWebResponse)req.GetResponse();
}
catch (WebException ex)
{
res = (HttpWebResponse)ex.Response;
}
pack.code = ((int)res.StatusCode);
using (Stream stream = res.GetResponseStream())
{
pack.response = GetStrResponse(stream, encry, encode);
}
res.Close();
req.Abort();
}
private void Post(ref HttpPack pack, string url, string postdata, bool encry, int timeout = 2000, Encoding encode = null)
{
HttpWebRequest req;
HttpWebResponse res;
encode = encode ?? Encoding.UTF8;
req = (HttpWebRequest)WebRequest.Create(new Uri(url));
req.Method = "POST";
req.Timeout = timeout;
if (postdata != null)
{
byte[] byteArray = encode.GetBytes(postdata);
Stream stream = req.GetRequestStream();
stream.Write(byteArray, 0, byteArray.Length);
stream.Close();
}
try
{
res = (HttpWebResponse)req.GetResponse();
pack.code = (int)res.StatusCode;
using (Stream resStream = res.GetResponseStream())
{
pack.response = GetStrResponse(resStream, encry, encode);
}
res.Close();
}
catch (WebException ex)
{
pack.code = (int)ex.Status;
}
req.Abort();
}
// 处理加密
private string GetStrResponse(Stream stream, bool encry, Encoding encode)
{
using (StreamReader reader = new StreamReader(stream))
{
string str = reader.ReadToEnd();
if (encry)
{
byte[] unEncryBytes = encode.GetBytes(str);
_encry.DoEncry(unEncryBytes);
return encode.GetString(unEncryBytes);
}
else
{
return str;
}
}
}
}
public class HttpPack : Poolable
{
public HttpType type;
public int code;
public bool encry;
public int tryNum;
public int key;
public string url;
public string param;
public string response;
}
using System;
using System.Collections.Generic;
using System.Threading;
public class BlockQueue
{
private readonly Queue _queue;
private readonly int _capacity;
private bool _closing;
public int count
{
get { return _queue.Count; }
}
public BlockQueue(int capacity)
{
this._capacity = capacity;
this._queue = new Queue(capacity);
}
public void Enqueue(T item)
{
lock (_queue)
{
while (_queue.Count >= _capacity)
{
Monitor.Wait(_queue);
}
_queue.Enqueue(item);
if (_queue.Count == 1)
{
Monitor.PulseAll(_queue);
}
}
}
public T Dequeue()
{
lock (_queue)
{
while (_queue.Count == 0)
{
Monitor.Wait(_queue);
}
T item = _queue.Dequeue();
if (_queue.Count == _capacity - 1)
{
Monitor.PulseAll(_queue);
}
return item;
}
}
public void Close()
{
lock (_queue)
{
_closing = true;
Monitor.PulseAll(_queue);
}
}
public bool TryDequeue(out T value)
{
lock (_queue)
{
while (_queue.Count == 0)
{
if (_closing)
{
value = default(T);
return false;
}
Monitor.Wait(_queue);
}
value = _queue.Dequeue();
if (_queue.Count == _capacity - 1)
{
Monitor.PulseAll(_queue);
}
return true;
}
}
}
public class Encry
{
private string _encryKey;
public Encry(string encryKey)
{
_encryKey = encryKey;
}
public void DoEncry(byte[] data)
{
int keyId = 0;
for (int i = 0; i < data.Length; i++)
{
data[i] ^= (byte)_encryKey[keyId];
keyId++;
if (keyId >= _encryKey.Length)
{
keyId = 0;
}
}
}
}
using UnityEngine;
public abstract class BaseController : MonoBehaviour
where T : BaseController
{
private const string ROOT_NAME = "SingleRoot";
private static T _instance = null;
private static GameObject root
{
get
{
GameObject singleRoot = GameObject.Find(ROOT_NAME);
if (singleRoot == null)
{
singleRoot = new GameObject(ROOT_NAME);
DontDestroyOnLoad(singleRoot);
}
return singleRoot;
}
}
public static T instance
{
get
{
if (_instance == null)
{
GameObject gameObject = new GameObject(typeof(T).Name);
gameObject.transform.SetParent(root.transform);
_instance = gameObject.AddComponent();
}
return _instance;
}
}
public virtual void Init()
{
}
public virtual void Remove()
{
if (_instance == this)
_instance = null;
GameObject.Destroy(gameObject);
}
}
using System;
using System.Collections.Generic;
public class Poolable where T : Poolable
{
private static Queue _pool;
private static Queue Pool
{
get
{
if (_pool == null)
{
_pool = new Queue();
PoolManager.instance.RegisterPool(ClearPool);
}
return _pool;
}
}
public static T GetPooled()
{
T output;
if (Pool.Count == 0)
{
output = Activator.CreateInstance();
}
else
{
output = _pool.Dequeue();
}
return output;
}
public static void ClearPool()
{
_pool.Clear();
}
public void Dispose()
{
OnDispose();
Pool.Enqueue((T)this);
}
protected virtual void OnDispose()
{
}
}
using System;
using System.Collections;
using System.Collections.Generic;
class PoolManager : BaseController
{
public delegate void PoolClearHandler();
private event PoolClearHandler _onFlush;
public PoolManager()
{
}
public void RegisterPool(PoolClearHandler clearHandler)
{
_onFlush += clearHandler;
}
public void Flush()
{
_onFlush?.Invoke();
}
}