/// <summary>
        /// Called to bind to socket and start heartbeat thread.
        /// The socket will be bound to listen on any network interface unless the <see cref="NetConfiguration.Address"/> explicitly specifies an interface.
        /// </summary>
        public void Start()
        {
            if (m_isBound)
            {
                return;
            }

            // TODO: this check should be done somewhere earlier, in uLink preferably.
            if (m_config.StartPort > m_config.EndPort)
            {
                throw new NetException("The start port (" + m_config.StartPort + ") must be less or equal to the end port (" + m_config.EndPort + ")");
            }

            //by WuNan @2016/09/28 14:26:22
            var bindIP = String.IsNullOrEmpty(m_config.AddressStr) ?
#if (UNITY_IOS || UNITY_TVOS) && !UNITY_EDITOR
                         (uLink.NetworkUtility.IsSupportIPv6() ? IPAddress.IPv6Any : IPAddress.Any) : NetUtility.Resolve(m_config.AddressStr);
#else
                         IPAddress.Any : NetUtility.Resolve(m_config.AddressStr);
#endif

            Log.Debug(LogFlags.Socket, "Creating non-blocking UDP socket");

            var sock = NetworkSocket.Create(m_config.SocketType);

            Log.Debug(LogFlags.Socket, "Successfully created Socket");

            for (int port = m_config.StartPort; port <= m_config.EndPort; port++)
            {
                try
                {
                    sock.Bind(new NetworkEndPoint(bindIP, port));

                    m_isBound = true;
                    break;
                }
                catch (SocketException ex)
                {
                    Log.Debug(LogFlags.Socket, "Failed to bind to specific port ", port, ": ", ex);

                    if (port == m_config.EndPort)
                    {
                        try
                        {
                            sock.Close(0);
                        }
                        catch
                        {
                        }

                        throw new NetException("Failed to bind to port range " + m_config.StartPort + "-" + m_config.EndPort, ex);
                    }
                }
            }

            m_socket = sock;

            LogWrite("Listening on " + m_socket.listenEndPoint);

            if (m_config.ReceiveBufferSize != 0)
            {
                try
                {
                    m_socket.receiveBufferSize = m_config.ReceiveBufferSize;
                    m_config.ReceiveBufferSize = m_socket.receiveBufferSize;                     // make sure we have the actual size
                }
                catch (Exception ex)
                {
                    Log.Warning(LogFlags.Socket, "Unable to set socket ", SocketOptionName.ReceiveBuffer, " size to ", m_config.ReceiveBufferSize, ": ", ex);
                }
            }
            else
            {
                try
                {
                    m_config.ReceiveBufferSize = m_socket.receiveBufferSize;

                    Log.Debug(LogFlags.Socket, "Socket ", SocketOptionName.ReceiveBuffer, " is set to OS-specific default ", m_config.ReceiveBufferSize);
                }
                catch (Exception ex)
                {
                    Log.Warning(LogFlags.Socket, "Unable to get socket ", SocketOptionName.ReceiveBuffer, ": ", ex);
                }
            }

            if (m_config.SendBufferSize != 0)
            {
                try
                {
                    m_socket.sendBufferSize = m_config.SendBufferSize;
                    m_config.SendBufferSize = m_socket.sendBufferSize;                     // make sure we have the actual size
                }
                catch (Exception ex)
                {
                    Log.Warning(LogFlags.Socket, "Unable to set socket ", SocketOptionName.SendBuffer, " size to ", m_config.SendBufferSize, ": ", ex);
                }
            }
            else
            {
                try
                {
                    m_config.SendBufferSize = m_socket.sendBufferSize;

                    Log.Debug(LogFlags.Socket, "Socket ", SocketOptionName.SendBuffer, " is set to OS-specific default ", m_config.SendBufferSize);
                }
                catch (Exception ex)
                {
                    Log.Warning(LogFlags.Socket, "Unable to get socket ", SocketOptionName.SendBuffer, ": ", ex);
                }
            }

            m_receiveBuffer.EnsureBufferSizeInBytes(m_config.ReceiveBufferSize);
            m_sendBuffer.EnsureBufferSizeInBytes(m_config.SendBufferSize);

            // TODO: ugly hack to determine if server
            if (this is NetServer)
            {
                m_socket.Listen(m_config.MaxConnections);
            }

            // display simulated networking conditions in debug log
            if (m_simulatedLoss > 0.0f)
            {
                LogWrite("Simulating " + (m_simulatedLoss * 100.0f) + "% loss");
            }
            if (m_simulatedMinimumLatency > 0.0f || m_simulatedLatencyVariance > 0.0f)
            {
                LogWrite("Simulating " + ((int)(m_simulatedMinimumLatency * 1000.0f)) + " - " + NetTime.ToMillis(m_simulatedMinimumLatency + m_simulatedLatencyVariance) + " ms roundtrip latency");
            }
            if (m_simulatedDuplicateChance > 0.0f)
            {
                LogWrite("Simulating " + (m_simulatedDuplicateChance * 100.0f) + "% chance of packet duplication");
            }

            if (m_config.m_throttleBytesPerSecond > 0)
            {
                LogWrite("Throttling to " + m_config.m_throttleBytesPerSecond + " bytes per second");
            }

            m_isBound          = true;
            m_shutdownComplete = false;
            m_statistics.Reset();
        }