public void SendRaw(byte[] data, int cnt, IPEndPoint ep, Action <bool> onComplete)
 {
     if (_ListenBroadcast)
     {
         int curVer = 0;
         if (_KnownRemotesS != null)
         {
             curVer = _KnownRemotesS.Version;
         }
         int rver = 0;
         if (_KnownRemotes != null)
         {
             rver = _KnownRemotes.Version;
         }
         if (rver > curVer)
         {
             _KnownRemotesS = System.Threading.Interlocked.Exchange(ref _KnownRemotes, _KnownRemotesS);
         }
         Socket knowSocket = null;
         if (_KnownRemotesS != null)
         {
             KnownRemote remote;
             if (_KnownRemotesS.Remotes.TryGetValue(ep.Address, out remote))
             {
                 knowSocket      = remote.LocalSocket;
                 remote.LastTick = Environment.TickCount;
                 _KnownRemotesS.Remotes[ep.Address] = remote;
             }
         }
         if (knowSocket != null)
         {
             try
             {
                 knowSocket.BeginSendTo(data, 0, cnt, SocketFlags.None, ep, ar =>
                 {
                     bool success = false;
                     try
                     {
                         knowSocket.EndSendTo(ar);
                         success = true;
                     }
                     catch (Exception e)
                     {
                         PlatDependant.LogError(e);
                     }
                     if (onComplete != null)
                     {
                         onComplete(success);
                     }
                 }, null);
                 return;
             }
             catch (Exception e)
             {
                 PlatDependant.LogError(e);
             }
         }
     }
     else
     {
         if (ep.AddressFamily == AddressFamily.InterNetworkV6)
         {
             if (_Socket6 != null)
             {
                 try
                 {
                     _Socket6.BeginSendTo(data, 0, cnt, SocketFlags.None, ep, ar =>
                     {
                         bool success = false;
                         try
                         {
                             _Socket6.EndSendTo(ar);
                             success = true;
                         }
                         catch (Exception e)
                         {
                             PlatDependant.LogError(e);
                         }
                         if (onComplete != null)
                         {
                             onComplete(success);
                         }
                     }, null);
                     return;
                 }
                 catch (Exception e)
                 {
                     PlatDependant.LogError(e);
                 }
             }
         }
         else
         {
             if (_Socket != null)
             {
                 try
                 {
                     _Socket.BeginSendTo(data, 0, cnt, SocketFlags.None, ep, ar =>
                     {
                         bool success = false;
                         try
                         {
                             _Socket.EndSendTo(ar);
                             success = true;
                         }
                         catch (Exception e)
                         {
                             PlatDependant.LogError(e);
                         }
                         if (onComplete != null)
                         {
                             onComplete(success);
                         }
                     }, null);
                     return;
                 }
                 catch (Exception e)
                 {
                     PlatDependant.LogError(e);
                 }
             }
         }
     }
     if (onComplete != null)
     {
         onComplete(false);
     }
 }
        protected override void ConnectWork()
        {
            try
            {
                KnownRemotes remotes = null;
                if (_ListenBroadcast)
                {
                    IPAddressInfo.Refresh();
                    _SocketsBroadcast = new List <BroadcastSocketReceiveInfo>();
                    remotes           = new KnownRemotes();
                    _KnownRemotes     = new KnownRemotes();
                    _KnownRemotesR    = new KnownRemotes();
                    _KnownRemotesS    = new KnownRemotes();
                }

                if (_ListenBroadcast)
                {
                    var ipv4addrs = IPAddressInfo.LocalIPv4Addresses;
                    for (int i = 0; i < ipv4addrs.Length; ++i)
                    {
                        try
                        {
                            var address = ipv4addrs[i];
                            var socket  = new Socket(address.AddressFamily, SocketType.Dgram, ProtocolType.Udp);
                            socket.Bind(new IPEndPoint(address, _Port));
                            socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Broadcast, true);
                            socket.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.AddMembership, new MulticastOption(IPAddressInfo.IPv4MulticastAddress, address));
                            socket.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.MulticastTimeToLive, 5);
                            _SocketsBroadcast.Add(new BroadcastSocketReceiveInfo(this, socket, new IPEndPoint(IPAddress.Any, _Port)));
                            if (_Socket == null)
                            {
                                _Socket = socket;
                            }
                        }
                        catch (Exception e)
                        {
                            PlatDependant.LogError(ipv4addrs[i]);
                            PlatDependant.LogError(e);
                        }
                    }
                }
                if (_Socket == null)
                {
                    var address4 = IPAddress.Any;
                    _Socket = new Socket(address4.AddressFamily, SocketType.Dgram, ProtocolType.Udp);
                    _Socket.Bind(new IPEndPoint(address4, _Port));
                }

#if NET_STANDARD_2_0 || NET_4_6
                // Notice: it is a pitty that unity does not support ipv6 multicast. (Unity 5.6)
                if (_ListenBroadcast)
                {
                    var ipv6addrs = IPAddressInfo.LocalIPv6Addresses;
                    for (int i = 0; i < ipv6addrs.Length; ++i)
                    {
                        try
                        {
                            var address = ipv6addrs[i];
                            var maddr   = IPAddressInfo.IPv6MulticastAddressOrganization;
                            if (address.IsIPv6SiteLocal)
                            {
                                maddr = IPAddressInfo.IPv6MulticastAddressSiteLocal;
                            }
                            else if (address.IsIPv6LinkLocal)
                            {
                                maddr = IPAddressInfo.IPv6MulticastAddressLinkLocal;
                            }
                            var socket = new Socket(address.AddressFamily, SocketType.Dgram, ProtocolType.Udp);
                            socket.Bind(new IPEndPoint(address, _Port));
                            var iindex = IPAddressInfo.GetInterfaceIndex(address);
                            if (iindex == 0)
                            {
                                socket.SetSocketOption(SocketOptionLevel.IPv6, SocketOptionName.AddMembership, new IPv6MulticastOption(maddr));
                            }
                            else
                            {
                                socket.SetSocketOption(SocketOptionLevel.IPv6, SocketOptionName.AddMembership, new IPv6MulticastOption(maddr, iindex));
                            }
                            socket.SetSocketOption(SocketOptionLevel.IPv6, SocketOptionName.MulticastTimeToLive, 5);
                            _SocketsBroadcast.Add(new BroadcastSocketReceiveInfo(this, socket, new IPEndPoint(IPAddress.IPv6Any, _Port)));
                            if (_Socket6 == null)
                            {
                                _Socket6 = socket;
                            }
                        }
                        catch (Exception e)
                        {
                            PlatDependant.LogError(ipv6addrs[i]);
                            PlatDependant.LogError(e);
                        }
                    }
                }
#endif
                if (_Socket6 == null)
                {
                    var address6 = IPAddress.IPv6Any;
                    _Socket6 = new Socket(address6.AddressFamily, SocketType.Dgram, ProtocolType.Udp);
                    _Socket6.Bind(new IPEndPoint(address6, _Port));
                }

                if (_ListenBroadcast)
                {
                    for (int i = 0; i < _SocketsBroadcast.Count; ++i)
                    {
                        var bsinfo = _SocketsBroadcast[i];
                        bsinfo.BeginReceive();
                    }
                    int knownRemotesVersion = 0;
                    while (!_ConnectWorkCanceled)
                    {
                        bool knownRemotesChanged = false;
                        var  curTick             = Environment.TickCount;
                        for (int i = 0; i < _SocketsBroadcast.Count; ++i)
                        {
                            var bsinfo = _SocketsBroadcast[i];
                            if (bsinfo.ReceiveCount > 0)
                            {
                                var ep = bsinfo.RemoteEP as IPEndPoint;
                                //var remote = new IPEndPoint(ep.Address, ep.Port);
                                remotes.Remotes[ep.Address] = new KnownRemote()
                                {
                                    Address = ep.Address, LocalSocket = bsinfo.LocalSocket, LastTick = curTick
                                };
                                knownRemotesChanged = true;
                            }
                        }
                        if (remotes.Remotes.Count > 100)
                        {
                            KnownRemote[] aremotes = new KnownRemote[remotes.Remotes.Count];
                            remotes.Remotes.Values.CopyTo(aremotes, 0);
                            Array.Sort(aremotes, (ra, rb) => ra.LastTick - rb.LastTick);
                            for (int i = 0; i < aremotes.Length - 100; ++i)
                            {
                                var remote = aremotes[i];
                                if (remote.LastTick + 15000 <= curTick)
                                {
                                    remotes.Remotes.Remove(remote.Address);
                                }
                                else
                                {
                                    break;
                                }
                            }
                        }
                        // TODO: check dead knownRemotes...
                        if (knownRemotesChanged)
                        {
                            _KnownRemotesR.Remotes.Clear();
                            foreach (var kvp in remotes.Remotes)
                            {
                                _KnownRemotesR.Remotes[kvp.Key] = kvp.Value;
                            }
                            _KnownRemotesR.Version = ++knownRemotesVersion;
                            _KnownRemotesR         = System.Threading.Interlocked.Exchange(ref _KnownRemotes, _KnownRemotesR);
                        }

                        if (_OnReceive != null)
                        {
                            for (int i = 0; i < _SocketsBroadcast.Count; ++i)
                            {
                                var bsinfo = _SocketsBroadcast[i];
                                if (bsinfo.ReceiveCount > 0)
                                {
                                    _OnReceive(bsinfo.ReceiveData, bsinfo.ReceiveCount, bsinfo.RemoteEP);
                                }
                            }
                        }
                        for (int i = 0; i < _SocketsBroadcast.Count; ++i)
                        {
                            var bsinfo = _SocketsBroadcast[i];
                            if (bsinfo.ReceiveResult.IsCompleted)
                            {
                                bsinfo.BeginReceive();
                            }
                        }

                        if (_OnUpdate != null)
                        {
                            _OnUpdate(this);
                        }

                        _HaveDataToSend.WaitOne(_UpdateInterval);
                    }
                }
                else
                {
                    EndPoint sender4 = new IPEndPoint(IPAddress.Any, _Port);
                    EndPoint sender6 = new IPEndPoint(IPAddress.IPv6Any, _Port);

                    byte[]       data4   = new byte[CONST.MTU];
                    byte[]       data6   = new byte[CONST.MTU];
                    int          dcnt4   = 0;
                    int          dcnt6   = 0;
                    IAsyncResult readar4 = null;
                    IAsyncResult readar6 = null;

                    Action BeginReceive4 = () =>
                    {
                        readar4 = _Socket.BeginReceiveFrom(data4, 0, CONST.MTU, SocketFlags.None, ref sender4, ar =>
                        {
                            try
                            {
                                dcnt4 = _Socket.EndReceiveFrom(ar, ref sender4);
                            }
                            catch (Exception e)
                            {
                                if (IsConnectionAlive)
                                {
                                    _ConnectWorkCanceled = true;
                                    PlatDependant.LogError(e);
                                }
                            }
                            _HaveDataToSend.Set();
                        }, null);
                    };
                    Action BeginReceive6 = () =>
                    {
                        readar6 = _Socket6.BeginReceiveFrom(data6, 0, CONST.MTU, SocketFlags.None, ref sender6, ar =>
                        {
                            try
                            {
                                dcnt6 = _Socket6.EndReceiveFrom(ar, ref sender6);
                            }
                            catch (Exception e)
                            {
                                if (IsConnectionAlive)
                                {
                                    _ConnectWorkCanceled = true;
                                    PlatDependant.LogError(e);
                                }
                            }
                            _HaveDataToSend.Set();
                        }, null);
                    };
                    BeginReceive4();
                    BeginReceive6();
                    while (!_ConnectWorkCanceled)
                    {
                        if (_OnReceive != null)
                        {
                            if (dcnt4 > 0)
                            {
                                _OnReceive(data4, dcnt4, sender4);
                                dcnt4 = 0;
                            }
                            if (dcnt6 > 0)
                            {
                                _OnReceive(data6, dcnt6, sender6);
                                dcnt6 = 0;
                            }
                        }
                        if (readar4.IsCompleted)
                        {
                            BeginReceive4();
                        }
                        if (readar6.IsCompleted)
                        {
                            BeginReceive6();
                        }

                        if (_OnUpdate != null)
                        {
                            _OnUpdate(this);
                        }

                        _HaveDataToSend.WaitOne(_UpdateInterval);
                    }
                }
            }
            catch (ThreadAbortException)
            {
                Thread.ResetAbort();
            }
            catch (Exception e)
            {
                PlatDependant.LogError(e);
            }
            finally
            {
                _ConnectWorkRunning  = false;
                _ConnectWorkCanceled = false;
                if (_PreDispose != null)
                {
                    _PreDispose(this);
                }
                if (_Socket != null)
                {
                    _Socket.Close();
                    _Socket = null;
                }
                if (_Socket6 != null)
                {
                    _Socket6.Close();
                    _Socket6 = null;
                }
                if (_SocketsBroadcast != null)
                {
                    for (int i = 0; i < _SocketsBroadcast.Count; ++i)
                    {
                        var bsinfo = _SocketsBroadcast[i];
                        if (bsinfo != null && bsinfo.LocalSocket != null)
                        {
                            bsinfo.LocalSocket.Close();
                        }
                    }
                    _SocketsBroadcast = null;
                }
                // set handlers to null.
                _OnReceive      = null;
                _OnSend         = null;
                _OnSendComplete = null;
                _OnUpdate       = null;
                _PreDispose     = null;
            }
        }