Unity中Socket和多线程导致Editor和程序卡住解决

Editor卡住的原因

Unity中使用Socket或者多线程,资源没有正确的释放导致Editor卡住。我们的项目中卡住是由于线程没有正确的关掉,TcpClient没有Close导致的。

避免Socket阻塞

在connect的时候如果连不上会等到Timeout,默认Timeout时间是20s。如果放在主循环中连,就会卡住20s。放在协程中会卡住编辑器。也就是说同步连接的时候是一定会等到Timeout的,程序或者Editor必定会卡20s。所以首先要避免连接的时候长时间阻塞。

所以最佳架构是:使用多线程和异步连接。并且消息的收发都使用队列,每个队列开个线程。发送之前检查是否连接上。

在发送队列中,使用TcpClient的异步连接:BeginConnect,然后AsyncWaitHandle.WaitOne来控制timeout。注意不管有没有连接成功,最后都要调用TcpClient.Close()关掉连接。

 IAsyncResult result = client.BeginConnect(IPAddress.Parse(host), port, null, null);              
 connected = result.AsyncWaitHandle.WaitOne(1000, false);
 if (connected )
 {
      client.EndConnect(result);
 } else
 {
     client.Close();
 }

线程要正确的退出

thread.abort()一般是无法退出线程的(abort调用了,如果thread.isAlive仍然为True就说明没有退出)。所以通常在线程的主循环中自定义控制变量isStop,需要结束的时候将isStop设置为true,退出主循环。并且需要将线程设置为守护线程(C#的isBackground,后台线程),这样主程序退出,就会自动关闭线程。

thread:
   while !isStop
      dowork

全部代码如下:

public class NetWork : MonoBehaviour {
    public static NetWork netWork = null;

    public string host = "127.0.0.1";
    public int port = 10002;

    private TcpClient client;
    private bool __connected = false;

    public bool connected
    {
        get { return __connected; }
    }
    private NetworkStream stream;
    private Thread recvProcess = null, sendProcess = null;
    //public InputField hostText, portText;
    private volatile bool stopSendProcess, stopRecvProcess;

    //发送队列
    private Queue sendQueue;

    //半包缓冲区
    private string recvData;
    private int maxBufferSize = 4096;

    private Queue recvQueue;

    private void Awake()
    {
        //保证全局唯一

        if (netWork != null)
        {
            DestroyImmediate(gameObject);
        } else
        {
            DontDestroyOnLoad(gameObject);
            netWork = this;
        }



        sendQueue = new Queue();
        recvQueue = new Queue();
    }

    private void Start()
    {
        netWork = this;

        sendProcess = new Thread(new ThreadStart(SendProcess));
        sendProcess.IsBackground = true;
        sendProcess.Start();
        stopSendProcess = false;
        stopRecvProcess = false;
    }

    public void TryConnect()
    {
        CreateConnection(host, port);
    }

    void CreateConnection(string host, int port)
    {
        try
        {
            client = new TcpClient();
            client.NoDelay = true;
            //client.Connect(IPAddress.Parse(host), port);
            IAsyncResult result = client.BeginConnect(IPAddress.Parse(host), port, null, null);
            __connected = result.AsyncWaitHandle.WaitOne(1000, false);

            if (__connected)
            {
                client.EndConnect(result);
            } else
            {
                client.Close();
            }
        }
        catch (SocketException ex)
        {
            __connected = false;
            Debug.Log("connect error: " + ex.Message);
            client.Close();
            return;
        }

        if (__connected)
        {
            stream = client.GetStream();
            if (recvProcess == null)
            {
                recvProcess = new Thread(new ThreadStart(RecvProcess));
                recvProcess.IsBackground = true;
                recvProcess.Start();
            }

        }
    }

    public static void WriteMessage(BaseStruct item)
    {
        if (netWork != null)
            netWork.Write(item);
    }

    private void Write(BaseStruct item)
    {
        sendQueue.Enqueue(item);
    }

    //数据发送线程,
    void SendProcess()
    {
        while (!stopSendProcess)
        {
            if (!__connected)
            {
                TryConnect();
            }
            while (sendQueue.Count > 0 && __connected)
            {
                BaseStruct item = sendQueue.Dequeue();
                byte[] buffer = System.Text.Encoding.UTF8.GetBytes(item.Serialize());
                stream.Write(buffer, 0, buffer.Length);
                stream.Flush();
            }
            Thread.Sleep(5);
        }
    }

    private void RecvProcess()
    {
        byte[] recvBuf = new byte[maxBufferSize];
        while (!stopRecvProcess)
        {
            int bytesRead = stream.Read(recvBuf, 0, maxBufferSize);
            // 解析消息加到 recvQueue
        }
        //Debug.Log("stopRecvProcess");
    }

    void OnApplicationQuit()
    {
        stopSendProcess = true;
        stopRecvProcess = true;

        if (__connected)
        {
            __connected = false;
            stream.Close();
            client.Close();
        }

        if (recvProcess != null)
        {
            //recvProcess.Abort();
            // 如果没有正确关闭线程,这里的Join就会阻塞,就会卡死编辑器
            // recvProcess.Join();
            Debug.Log("recvProcess: " + recvProcess.IsAlive);
        }

        if (sendProcess != null)
        {
            //sendProcess.Abort();

            // sendProcess.Join();
            Debug.Log("sendProcess: " + sendProcess.IsAlive);
        }

    }

}

你可能感兴趣的:(unity3d)