123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709 |
- using CommonLang;
- using CommonLang.Concurrent;
- using CommonLang.Log;
- using SimpleJson;
- using System;
- using System.Collections.Generic;
- using System.IO;
- using System.Net;
- using System.Net.Sockets;
- using System.Runtime.InteropServices;
- using System.Text.RegularExpressions;
- using System.Threading;
- namespace Pomelo.DotNetClient
- {
- public class PomeloTCP : PomeloClientAdapter
- {
- protected readonly IPomeloClientAdapterListener listener;
- private long last_heartbeat_r2c = CUtils.CurrentTimeMS;
- private long last_heartbeat_chk = CUtils.CurrentTimeMS;
- private long total_recv_bytes = 0;
- private long total_sent_bytes = 0;
- private const int _SendTimeOut = 3000;
- private const int _ReceiveTimeOut = 3000;
- private const int _MaxRead = 1024 * 16;
- private readonly byte[] _byteBuffer = new byte[_MaxRead];
- private MemoryStream _memStream = null;
- private BinaryReader _reader = null;
- private NetWorkState mCurStatus = NetWorkState.NONE;
- private Logger log = LoggerFactory.GetLogger("PomeloTCP");
- Queue<Message> MsgQueue = new Queue<Message>();
- protected class Connecting
- {
- public string _host;
- public int _port;
- public int _timeout;
- public JsonObject _user;
- }
- protected TcpClient socket;
- protected Connecting _NetInfo;
- public Socket Session
- {
- get { return (socket != null) ? socket.Client : null; }
- }
- public NetWorkState NetWorkState
- {
- get { return mCurStatus; }
- }
- public long TotalRecvBytes { get { return total_recv_bytes; } }
- public long TotalSentBytes { get { return total_sent_bytes; } }
- public void DoSendUpdate() { }
- public bool Connected
- {
- get
- {
- return socket != null &&
- socket.Connected &&
- socket.GetStream().CanRead &&
- socket.GetStream().CanWrite;
- }
- }
- public PomeloTCP(IPomeloClientAdapterListener listener)
- {
- socket = null;
- this.listener = listener;
- _memStream = new MemoryStream();
- _reader = new BinaryReader(_memStream);
- }
- public long GetPing()
- {
- return CUtils.CurrentTimeMS - last_heartbeat_r2c;
- }
- //--------------------------------------------------------------------------------------------------
- #region --Interface--
- public virtual void connect(string host, int port, int timeout, JsonObject user)
- {
- log.Debug("===========socket connect begin " + Thread.CurrentThread.ManagedThreadId);
- if (Connected)
- {
- //send msg
- if (listener != null)
- {
- log.Debug("===========socket connect call function ");
- listener.OnConnected(socket.Client, _NetInfo._user);
- }
- return;
- }
- //已经有一个连接在进行中
- if(mCurStatus == NetWorkState.CONNECTING)
- {
- JsonObject json = new JsonObject();
- DoNetState(NetWorkState.ERROR);
- json["s2c_code"] = 500;
- json["s2c_msg"] = "socket connecting";
- listener.OnConnected(socket.Client, json);
- return;
- }
- _NetInfo = new Connecting()
- {
- _host = host,
- _port = port,
- _timeout = timeout,
- _user = user,
- };
- DoNetState(NetWorkState.CONNECTING);
- string newIp = null;
- AddressFamily addressFamily;
- GetIPType(host, port.ToString(), out newIp, out addressFamily);
- _NetInfo._host = newIp;
- _NetInfo._port = port;
- this.socket = new TcpClient(addressFamily)
- {
- SendTimeout = _SendTimeOut,
- ReceiveTimeout = _ReceiveTimeOut,
- NoDelay = true
- };
- socket.Client.Blocking = true;
- //connect
- try
- {
- log.Debug("===========socket connect call socket.BeginConnect " + _NetInfo._host + ":" + _NetInfo._port);
- _received_heartbeat();
- socket.BeginConnect(_NetInfo._host, _NetInfo._port, new AsyncCallback(OnConnect), socket);
- }
- catch (Exception err)
- {
- if (err is SocketException)
- {
- JsonObject json = new JsonObject();
- switch ((err as SocketException).SocketErrorCode)
- {
- case SocketError.TimedOut:
- DoNetState(NetWorkState.TIMEOUT);
- json["s2c_code"] = 501;
- json["s2c_msg"] = err.Message;
- break;
- default:
- DoNetState(NetWorkState.ERROR);
- json["s2c_code"] = 500;
- json["s2c_msg"] = err.Message;
- break;
- }
- listener.OnConnected(socket.Client, json);
- }
- else
- {
- log.Debug("===========socket connect call socket.BeginConnect CloseSocket");
- CloseSocket();
- }
- }
- }
- private void OnConnect(IAsyncResult ar)
- {
- //reset stream
- try
- {
- socket.EndConnect(ar);
- if (Connected)
- {
- _memStream.SetLength(0);
- _memStream.Position = 0;
- Array.Clear(_byteBuffer, 0, _byteBuffer.Length);
- StartReadMessage();
- _send_handshake(_NetInfo._user);
- log.Debug("socket connect success ");
- }
- else
- {
- log.Debug("socket connect faile ");
- this._set_net_state(NetWorkState.DISCONNECTED);
- }
- }
- catch (Exception e)
- {
- log.Warn("===========OnConnect========== Error" + e.Message);
- CloseSocket();
- _on_error(e);
- }
- }
- public virtual void disconnect()
- {
- log.Debug("socket disconnect " + Thread.CurrentThread.ManagedThreadId);
- DoNetState(NetWorkState.CLOSED);
- CloseSocket();
- ClearMsgQueue();
- if(listener != null)
- {
- listener.OnDisconnected(null, "CLOSED");
- }
-
- }
- public virtual bool resolve_route(RecvMessage msg, byte flag, out string route)
- {
- if ((flag & RecvMessage.MSG_Route_Mask) == 1)
- {
- ushort routeId = RecvMessage.ReadShort(msg.Stream);
- route = "area.playerPush.battleEventPush";
- return true;
- }
- route = null;
- return false;
- }
- public virtual void update(float deltime)
- {
- ProcessMsg();
- }
- public virtual void send(SendMessage send_object)
- {
- try
- {
- startSend(send_object);
- }
- catch (Exception err) {
- CloseSocket();
- log.Warn("socket startSend Error:"+ err.StackTrace);
- _on_error(err);
- }
- }
- public virtual void disposing()
- {
- log.Debug("socket disposing " + Thread.CurrentThread.ManagedThreadId);
- if (_memStream != null)
- {
- _memStream.Close();
- _memStream = null;
- }
- if (_reader != null)
- {
- _reader.Close();
- _reader = null;
- }
- disconnect();
- }
- #endregion
- //--------------------------------------------------------------------------------------------------
- #region --Internal--
- private void _on_error(Exception err)
- {
- log.Warn("_on_error ThreadID:" + Thread.CurrentThread.ManagedThreadId);
- _set_net_state(NetWorkState.DISCONNECTED);
- }
- private void _set_net_state(NetWorkState st)
- {
- LocalNetStatus recv_object = LocalNetStatus.Alloc(st);
- AddMsg(recv_object);
- }
- private bool DoNetState(NetWorkState st)
- {
- if (mCurStatus != st)
- {
- if (NetWorkState.CLOSED == mCurStatus && NetWorkState.DISCONNECTED == st)
- {
- //DISCONNECTED 为重连标识,所以只能在connected状态下切换
- return false;
- }
- mCurStatus = st;
- if (st != NetWorkState.NONE)
- {
- if(NetWorkState.DISCONNECTED == mCurStatus)
- {
- CloseSocket();
- ClearMsgQueue();
- }
- listener.OnNetWorkStateChanged(st);
- return true;
- }
- }
- return false;
- }
- private void CloseSocket()
- {
- try
- {
- log.Debug("====CloseSocket=====");
- if (Connected)
- {
- socket.Client.Shutdown(SocketShutdown.Both);
- socket.Client.Close();
- //socket.GetStream().Close();
-
- socket.Close();
- socket = null;
- log.Debug("====CloseSocket===== end");
- }
- }
- catch (Exception err)
- {
- log.Debug("===== socket === CloseSocket Error");
- log.Warn(err.Message + "\n" + err.StackTrace);
- if(socket != null)
- {
- socket.Close();
- socket = null;
- }
- }
- }
- private void _send_handshake(JsonObject user)
- {
- //Debug.Log("begin handshake!");
- var Version = "0.3.0";
- var Type = "unity-socket";
- if (user == null) user = new JsonObject();
- var msg = new JsonObject();
- //Build sys option
- var sys = new JsonObject();
- sys["version"] = Version;
- sys["type"] = Type;
- //Build handshake message
- msg["sys"] = sys;
- msg["user"] = user;
- var body = Message.UTF8.GetBytes(msg.ToString());
- var send_object = SendMessage.Alloc(PackageType.PKG_HANDSHAKE, body);
- startSend(send_object);
- }
- private void _received_handshake(JsonObject msg)
- {
- var code = msg["code"];
- var sys = msg["sys"] as JsonObject;
- if (code == null || sys == null || Convert.ToInt32(code) != 200)
- {
- throw new Exception("Handshake error! Please check your handshake config.");
- }
- object jobj;
- JsonObject dict;
- if (sys.TryGetValue("dict", out jobj))
- {
- dict = (JsonObject)jobj;
- }
- else
- {
- dict = new JsonObject();
- }
- //Init heartbeat service
- int interval = 0;
- if (sys.TryGetValue("heartbeat", out jobj))
- {
- interval = Convert.ToInt32(jobj);
- //_init_heartbeat(interval);
- }
- var send_object = SendMessage.Alloc(PackageType.PKG_HANDSHAKE_ACK);
- startSend(send_object);
- JsonObject user;
- if (msg.TryGetValue("user", out jobj))
- {
- user = (JsonObject)jobj;
- }
- else
- {
- user = new JsonObject();
- }
- DoNetState(NetWorkState.CONNECTED);
- _received_heartbeat();
- if (listener != null)
- {
- listener.OnConnected(socket.Client, user);
- }
-
- }
- private void _received_package(Message recv)
- {
- if(recv is RecvMessage)
- {
- RecvMessage recv_object = recv as RecvMessage;
- if (recv_object.PkgType == PackageType.PKG_HANDSHAKE)
- {
- var body = Message.UTF8.GetString(
- recv_object.Buffer,
- RecvMessage.FIXED_HEAD_SIZE,
- recv_object.PkgLength);
- JsonObject data = (JsonObject)SimpleJson.SimpleJson.DeserializeObject(body);
- _received_handshake(data);
- }
- else if (recv_object.PkgType == PackageType.PKG_HEARTBEAT)
- {
- listener.OnReceivedMessage(recv_object);
- }
- else if (recv_object.PkgType == PackageType.PKG_DATA)
- {
- recv_object.DecodeBody(this);
- listener.OnReceivedMessage(recv_object);
- }
- else if (recv_object.PkgType == PackageType.PKG_KICK)
- {
- DoNetState(NetWorkState.KICK);
- }
- }
- else
- {
- //异步处理 网络状态更新
- LocalNetStatus netStatus = recv as LocalNetStatus;
- if(netStatus != null)
- {
- DoNetState(netStatus.St);
- }
- }
- recv.Dispose();
- }
- private void _send_heartbeat()
- {
- var send_object = SendMessage.Alloc(PackageType.PKG_HEARTBEAT);
- startSend(send_object);
- }
- private void _received_heartbeat()
- {
- last_heartbeat_r2c = CUtils.CurrentTimeMS;
- }
- #endregion
- //--------------------------------------------------------------------------------------------------
- #region --收发--
- private void StartReadMessage()
- {
- try
- {
- if (!Connected)
- {
- return;
- }
- if (socket.GetStream().CanRead)
- {
- Array.Clear(_byteBuffer, 0, _byteBuffer.Length);
- //log.Debug("WorkStream.StartReadMessage ThreadID:" + Thread.CurrentThread.ManagedThreadId);
- socket.GetStream().BeginRead(_byteBuffer, 0, _byteBuffer.Length,
- new AsyncCallback(TCPReadCallBack), null);
- }
- else
- {
- log.Warn("Network IO problem not cantread");
- }
- }
- catch (Exception err)
- {
- log.Warn("StartReadMessage error :" + err.Message);
- }
- }
- private void TCPReadCallBack(IAsyncResult ar)
- {
- //主动断开时
- if (!Connected)
- {
- return;
- }
- int numberOfBytesRead = 0;
- try
- {
-
- numberOfBytesRead = socket.GetStream().EndRead(ar);
- if (numberOfBytesRead > 0)
- {
- pickpackage(_byteBuffer, numberOfBytesRead);
- Array.Clear(_byteBuffer, 0, _byteBuffer.Length);
- //log.Debug("socket WorkStream.StartReadMessage callback ThreadID:" + Thread.CurrentThread.ManagedThreadId);
- socket.GetStream().BeginRead(_byteBuffer, 0, _byteBuffer.Length,new AsyncCallback(TCPReadCallBack), null);
- }
- else
- {
- //被动断开时 回到登录界面
- log.Debug("===========socket recve numberOfBytesRead: recve = " + numberOfBytesRead.ToString());
- _set_net_state(NetWorkState.DISCONNECTED);
- }
- }
- catch (Exception err)
- {
- log.Warn("=======socket===TCPReadCallBack===Error>" + err.Message);
- _on_error(err);
- }
- }
- private long RemainingBytes()
- {
- return _memStream.Length - _memStream.Position;
- }
- private void pickpackage(byte[] bytes, int length)
- {
- _memStream.Seek(0, SeekOrigin.End);
- _memStream.Write(bytes, 0, length);
- _memStream.Seek(0, SeekOrigin.Begin);
- while (RemainingBytes() >= RecvMessage.FIXED_HEAD_SIZE)
- {
- PackageType PkgType = (PackageType)_reader.ReadByte();
- int PkgLength = (_reader.ReadByte() << 16) + (_reader.ReadByte() << 8) + _reader.ReadByte();
- long remainingLength = RemainingBytes();
- _memStream.Position -= RecvMessage.FIXED_HEAD_SIZE;
- if (remainingLength < PkgLength)
- {
- break;
- }
- int msgSize = RecvMessage.FIXED_HEAD_SIZE + PkgLength;
- RecvMessage recv_object = RecvMessage.Alloc();
- recv_object.stateSocket = socket;
- recv_object.Stream.Write(_reader.ReadBytes(msgSize), 0, msgSize);
- recv_object.DecodeHead();
- //Enqueue
- _received_heartbeat();
- AddMsg(recv_object);
- }
- var leftover = _reader.ReadBytes((int)RemainingBytes());
- _memStream.SetLength(0); //Clear
- _memStream.Write(leftover, 0, leftover.Length);
- }
- #endregion
- #region <<BeginSend 方式发送>>
- private void startSend(SendMessage send_object)
- {
- try
- {
- if (Connected)
- {
- socket.GetStream().BeginWrite(send_object.Buffer, 0, send_object.BufferLength, endSend, send_object);
- }
- }
- catch (Exception err)
- {
- send_object.Dispose();
- log.Warn("startSend Error>" + err.Message);
- }
- }
- private void endSend(IAsyncResult asyncSend)
- {
- var send_object = asyncSend.AsyncState as SendMessage;
- try
- {
- if (!Connected)
- {
- return;
- }
- socket.GetStream().EndWrite(asyncSend);
- }
- catch (Exception err)
- {
- CloseSocket();
- }
- finally
- {
- send_object.Dispose();
- }
- }
- #endregion
- private void AddMsg(Message msg)
- {
- if (msg != null)
- {
- lock (MsgQueue)
- {
- MsgQueue.Enqueue(msg);
- }
- }
- }
- private void ProcessMsg()
- {
- lock (MsgQueue)
- {
- long last = CUtils.CurrentTimeMS;
- while (MsgQueue.Count > 0)
- {
- if(CUtils.CurrentTimeMS - last > 100)
- {
- break;
- }
- var recv_object = MsgQueue.Dequeue();
- if (recv_object != null)
- {
- _received_package(recv_object);
- }
- }
- }
- }
- private void ClearMsgQueue()
- {
- lock(MsgQueue)
- {
- while (MsgQueue.Count > 0)
- {
- var recv_object = MsgQueue.Dequeue();
- if (recv_object != null)
- {
- recv_object.Dispose();
- }
- }
- }
- }
- #region ipv4||6
- ////////////////////////////////////
- [DllImport("__Internal")]
- private static extern string getIPv6(string mHost, string mPort);
- public static string GetIPv6(string mHost, string mPort)
- {
- #if UNITY_IPHONE && !UNITY_EDITOR
- string mIPv6 = getIPv6(mHost, mPort);
- return mIPv6;
- #else
- return mHost + "&&ipv4";
- #endif
- }
- private void GetIPType(string serverIp, string serverPorts, out string newServerIp, out AddressFamily mIPType)
- {
- mIPType = AddressFamily.InterNetwork;
- newServerIp = serverIp;
- try
- {
- var mIPv6 = GetIPv6(serverIp, serverPorts);
- if (!string.IsNullOrEmpty(mIPv6))
- {
- var m_StrTemp = Regex.Split(mIPv6, "&&");
- if (m_StrTemp != null && m_StrTemp.Length >= 2)
- {
- var IPType = m_StrTemp[1];
- if (IPType == "ipv6")
- {
- newServerIp = m_StrTemp[0];
- mIPType = AddressFamily.InterNetworkV6;
- }
- }
- }
- }
- catch (Exception e)
- {
- log.Log("GetIPv6 error:" + e);
- }
- }
- #endregion
- }
- }
|