Unity开发之网络一HTTP客户端实现

客户端实现参考:Unity发送HTTP请求和文件下载 注意:仅通过WebRequest实现POST方式,通过测试 Http请求主要涉及两个类:HttpRequest,HttpResponse,在参考的实现基础上有改动 HttpRequest

/// <summary>
/// Http请求
/// </summary>
public class HttpRequest
{
    /// <summary>
    /// 错误代码
    /// </summary>
    public const int ERR_EXCEPTION = -1;

    #region 属性
    private string _url;
    private int _timeout;
    private Action<HttpResponse> _callback;
    private HttpWebRequest _request;
    private string _method;
    private string _contentType;
    private KeyValuePair<string, int> _proxy;
    protected int _range = -1;
    // post内容
    private StringBuilder _postBuilder;
    #region get/set
    /// <summary>
    /// 设置ContentType
    /// </summary>
    /// <value>ContentType value</value>
    public string ContentType
    {
        set
        {
            _contentType = value;
        }
    }
    #endregion

    #endregion

    /// <summary>
    /// 构造函数, 构造GET请求
    /// </summary>
    /// <param name="url">url地址</param>
    /// <param name="timeout">超时时间</param>
    /// <param name="callback">回调函数</param>
    public HttpRequest(string url, string method, int timeout, Action<HttpResponse> callback)
    {
        _url = url;
        _timeout = timeout;
        _callback = callback;
        _method = method.ToUpper();
    }
    /// <summary>
    /// 设置Post内容
    /// </summary>
    /// <param name="data">内容</param>
    public void SetPostData(string data)
    {
        if (_postBuilder == null)
        {
            _postBuilder = new StringBuilder(data.Length);
        }
        if (_postBuilder.Length > 0)
        {
            _postBuilder.Append("&");
        }
        _postBuilder.Append(data);
    }
    /// <summary>
    /// 添加Post内容
    /// </summary>
    /// <param name="key">key值</param>
    /// <param name="value">value值</param>
    public void AddPostData(string key, string value)
    {
        if (_postBuilder == null)
        {
            _postBuilder = new StringBuilder();
        }
        if (_postBuilder.Length > 0)
        {
            _postBuilder.Append("&");
        }

        _postBuilder.Append(key).Append("=").Append(UrlEncode(value));
    }
    /// <summary>
    /// 设置代理
    /// </summary>
    /// <param name="ip">ip地址</param>
    /// <param name="port">端口号</param>
    public void SetProxy(string ip, int port)
    {
        _proxy = new KeyValuePair<string, int>(ip, port);
    }

    /// <summary>
    /// 发动请求
    /// </summary>
    public void Start()
    {
        //Debug.Log("Handle Http Request Start");
        \_request = WebRequest.Create(\_url) as HttpWebRequest;
        \_request.Timeout = \_timeout;
        \_request.Method = \_method;  
        _request.Accept = "text/plain";

        if (_proxy.Key != null)
        {
            \_request.Proxy = new WebProxy(\_proxy.Key, _proxy.Value);
        }

        if (_contentType != null)
        {
            \_request.ContentType = \_contentType;
        }

        if (_range != -1)
        {
            \_request.AddRange(\_range);
        }

        try
        {
            if (_method.Equals("POST"))
            {
                WritePostData();
            }

            AsyncCallback callback = new AsyncCallback(OnResponse);
            _request.BeginGetResponse(callback, null);
        }
        catch (Exception e)
        {
            CallBack(ERR_EXCEPTION, e.Message);
            if (_request != null)
            {
                _request.Abort();
            }
        }
    }
    /// <summary>
    /// 处理读取Response
    /// </summary>
    /// <param name="result">异步回到result</param>
    protected void OnResponse(IAsyncResult result)
    {
        //Debug.Log ("Handle Http Response");
        HttpWebResponse response = null;
        try
        {
            // 获取Response
            response = _request.EndGetResponse(result) as HttpWebResponse;
            if (response.StatusCode == HttpStatusCode.OK)
            {
                if ("HEAD".Equals(_method))
                {
                    // HEAD请求
                    long contentLength = response.ContentLength;
                    CallBack((int)response.StatusCode, contentLength + "");
                    return;
                }

                // 读取请求内容
                string content = string.Empty;
                Stream stream = response.GetResponseStream();
                StreamReader streamReader = new StreamReader(stream, System.Text.Encoding.UTF8);
                content = streamReader.ReadToEnd();

                streamReader.Close();
                response.Close();
                _request.Abort();

                // 调用回调
                CallBack((int)response.StatusCode, content);
            }
            else
            {
                CallBack((int)response.StatusCode, "");
            }
        }
        catch (Exception e)
        {
            CallBack(ERR_EXCEPTION, e.Message);
            if (_request != null)
            {
                _request.Abort();
            }
            if (response != null)
            {
                response.Close();
            }
        }
    }

    private void CallBack(int code, string content)
    {
        if (_callback != null)
        {
            HttpResponse response = new HttpResponse();
            response.StatusCode = code;
            if (code == (int)HttpStatusCode.OK)
            {
                response.Content = content;
            }
            else
            {
                response.Error = content;
            }

            _callback(response);
        }
    }

    /// <summary>
    /// 写Post内容
    /// </summary>
    private void WritePostData()
    {
        if (null == \_postBuilder || \_postBuilder.Length <= 0)
        {
            return;
        }

        byte\[\] bytes = Encoding.UTF8.GetBytes(_postBuilder.ToString());
        _request.ContentLength = bytes.Length;
        _request.GetRequestStream().Write(bytes, 0, bytes.Length);
    }

    /// <summary>
    /// URLEncode
    /// </summary>
    /// <returns>encode value</returns>
    /// <param name="value">要encode的值</param>
    private string UrlEncode(string value)
    {
        StringBuilder sb = new StringBuilder();
        byte\[\] byStr = System.Text.Encoding.UTF8.GetBytes(value);
        for (int i = 0; i < byStr.Length; i++)
        {
            sb.Append(@"%" + Convert.ToString(byStr\[i\], 16));
        }
        return (sb.ToString());
    }
}

HttpResponse

/// <summary>
/// HTTP返回内容
/// </summary>
public class HttpResponse
{
    #region 属性
    // 状态码
    private int _statusCode;
    // 响应字节
    private string _content;
    // 错误内容
    private string _error;

    #region get/set

    /// <summary>
    /// 获取状态码
    /// </summary>
    /// <value>状态码</value>
    public int StatusCode
    {
        set
        {
            this._statusCode = value;
        }
        get
        {
            return this._statusCode;
        }
    }
    /// <summary>
    /// 获取错误消息
    /// </summary>
    /// <value>错误消息</value>
    public string Error
    {
        set
        {
            this._error = value;
        }
        get
        {
            return this._error;
        }
    }

    public string Content
    {
        get
        {
            return _content;
        }

        set
        {
            _content = value;
        }
    }
    #endregion

    #endregion

    /// <summary>
    /// 默认构造函数
    /// </summary>
    public HttpResponse()
    {

    }
}

在两个类的基础上,封装HttpHelper方便应用中使用

public class HttpHelper : Singleton<HttpHelper>
{
    //常量
    //应用配置信息
    public const string COMMAND\_APP\_INFO = "app_info";
    //服务器地址列表
    public const string COMMAND\_SERVER\_LIST = "server_list";

    #region 属性
    //基本数据
    private Dictionary<string, string> _baseData;
    private Dictionary<Action<bool, object>, HttpResponse> _responseDict;

    #region get/set
    public Dictionary<string, string> BaseData
    {
        get
        {
            return _baseData;
        }

        set
        {
            _baseData = value;
        }
    }
    #endregion

    #endregion

    public HttpHelper()
    {
        _responseDict = new Dictionary<Action<bool, object>, HttpResponse>();
        _baseData = new Dictionary<string, string>();

        \_baseData.Add("app\_id", Global.GlobalDef.AppId);
        \_baseData.Add("app\_ver", Global.GlobalDef.AppVer);
        \_baseData.Add("device\_id", Global.GlobalDef.DeviceId);
    }

    //消息分发:主线程
    public void Update(float dt)
    {
        if (_responseDict.Count > 0)
        {
            foreach (var item in _responseDict)
            {
                Action<bool, object> callback = item.Key;
                HttpResponse response = item.Value;
                try
                {
                    bool result = false;
                    string content = response.Error;
                    if (response.StatusCode == (int)System.Net.HttpStatusCode.OK)
                    {
                        result = true;
                        content = response.Content;
                        if (callback != null)
                        {
                            JsonData jsonResult = JsonMapper.ToObject(content);
                            callback(result, jsonResult\["data"\]);
                        }
                    }
                    else
                    {
                        callback(result, content);
                    }
                }
                catch (Exception e)
                {
                    UnityLibs.Debuger.Log(e.ToString());
                }
            }

            _responseDict.Clear();
        }
    } 

    #region 应用信息
    public void AppInfo(Action<bool,object> callback)
    {
        LitJson.JsonData jsonData = new LitJson.JsonData();
        jsonData\["cmd"\] = COMMAND\_APP\_INFO;
        foreach (var item in _baseData)
        {
            jsonData\[item.Key\] = item.Value;
        }

        string jsonStr = LitJson.JsonMapper.ToJson(jsonData);
        string url = Global.GlobalDef.HostAddr;
        _onRequest(url, jsonStr, delegate (HttpResponse response)
        {
            _responseDict.Add(callback, response);
        });
    }

    #endregion

    #region 服务器列表
    public void ServerList(Action<bool, object> callback)
    {
        LitJson.JsonData jsonData = new LitJson.JsonData();
        jsonData\["cmd"\] = COMMAND\_SERVER\_LIST;
        foreach (var item in _baseData)
        {
            jsonData\[item.Key\] = item.Value;
        }

        string jsonStr = LitJson.JsonMapper.ToJson(jsonData);
        string url = Global.GlobalDef.HostAddr;
        _onRequest(url, jsonStr, delegate (HttpResponse response)
        {
            _responseDict.Add(callback, response);
        });
    }

    #endregion

    #region 内部函数

    private void _onRequest(string url, string data, Action<HttpResponse> callback)
    {
        HttpRequest client = new HttpRequest(url, "POST", 30 * 1000, callback);
        client.ContentType = "application/x-www-form-urlencoded";
        client.AddPostData("data", data);
        client.Start();
    }

    #endregion
}

调用示例:

HttpHelper.Instance.ServerList(delegate(bool result,object obj) 
{
    if (result)
    {
        LitJson.JsonData jsonData = obj as LitJson.JsonData;
        string serverAddr = jsonData\["ip"\].ToString();
        int port = int.Parse(jsonData\["port"\].ToString());
        HandlerObservable.Instance.OnInit(serverAddr, (short)port);
    }
    else
    {
        HandlerObservable.Instance.OnInit(GlobalDef.SERVER\_ADDR, GlobalDef.SERVER\_PORT); 
    }

    //Logo展示完后加载
    StartCoroutine(\_startLoading(\_loadingTime));
});

说明: 1.ContentType 设置为application/x-www-form-urlencoded,在服务实现部分会说明 2.数据使用Json格式编码; 3.Http响应回调是在单独的线程中,为了调用方式需要将回调在主线程完成(Update的作用)。 4.HttpHelper的Update方法需要在游戏中不会被销毁的继承MonoBehaviour的类每帧调用:

private void Update()
{
    HttpHelper.Instance.Update(UnityEngine.Time.deltaTime);
}

下一篇介绍服务端实现


转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 xue_huashan@163.com

文章标题:Unity开发之网络一HTTP客户端实现

文章字数:1.5k

本文作者:max-xue

发布时间:2018-06-22, 20:51:32

最后更新:2019-11-09, 22:35:58

原始链接:http://blog.le-more.com/2018/06/22/u3d/unity-e5-bc-80-e5-8f-91-e12/

版权声明: "署名-非商用-相同方式共享 4.0" 转载请保留原文链接及作者。

目录
×

喜欢就点赞,疼爱就打赏