在Unity中创建简单的套接字服务器

8
我希望在我的Unity项目中使用一个C#插件。该插件应该作为服务器,从客户端获取值,以便我可以将这些值用于进一步处理。 问题是服务器有无限循环。无限循环会导致Unity挂起。如何处理?
编辑:我附上了服务器程序的代码片段。在我看来,可能会导致问题的有两个点。无限循环和程序被暂停的时候,如代码中所注释的那样。
void networkCode()
{
    // Data buffer for incoming data.
    byte[] bytes = new Byte[1024];

    // Establish the local endpoint for the socket.
    // Dns.GetHostName returns the name of the 
    // host running the application.
    IPHostEntry ipHostInfo = Dns.Resolve(Dns.GetHostName());
    IPAddress ipAddress = ipHostInfo.AddressList[0];
    IPEndPoint localEndPoint = new IPEndPoint(ipAddress, 1755);

    // Create a TCP/IP socket.
    listener = new Socket(ipAddress.AddressFamily,
        SocketType.Stream, ProtocolType.Tcp);

    // Bind the socket to the local endpoint and 
    // listen for incoming connections.
    try
    {
        listener.Bind(localEndPoint);
        listener.Listen(10);

        // Start listening for connections.
        while (true)
        {
            // Program is suspended while waiting for an incoming connection.
            Debug.Log("HELLO");     //It works
            handler = listener.Accept();
            Debug.Log("HELLO");     //It doesn't work
            data = null;

            // An incoming connection needs to be processed.
            while (true)
            {
                bytes = new byte[1024];
                int bytesRec = handler.Receive(bytes);
                data += Encoding.ASCII.GetString(bytes, 0, bytesRec);
                if (data.IndexOf("<EOF>") > -1)
                {
                    break;
                }

                System.Threading.Thread.Sleep(1);
            }   

            System.Threading.Thread.Sleep(1);
        }
    }
    catch (Exception e)
    {
        Debug.Log(e.ToString());
    }
}

更新:在@程序员的帮助下,C#插件已经完成。但Unity没有读取正确的值。我附上了Unity端的代码:

using UnityEngine;
using System;

using SyncServerDLL;    //That's our library

public class receiver : MonoBehaviour {

    SynchronousSocketListener obj;   //That's object to call server methods

    // Use this for initialization
    void Start() {
        obj = new SynchronousSocketListener ();
        obj.startServer ();
    }

    // Update is called once per frame
    void Update() {
        Debug.Log (obj.data);
    }
}

我在Visual Studio中彻底测试了SynchronousSocketListener类,结果很好。


请更新您的问题并附上导致程序卡住的代码。 - Programmer
1
@Paul-Jan。我知道他遇到的问题。我想通过修复他的代码来提供解决方案,而不是花时间从头编写新代码。同时,我也想确保他有所收获,不希望别人为他编写所有的代码。 - Programmer
@程序员 如果你需要,我可以添加更多的代码。不过编码方面我可以自己处理。我只需要一个方向指引。 - Salman Younas
如果可以的话,我会给你指示。 - Programmer
检查我的答案。这很容易做到。 - Programmer
显示剩余2条评论
1个回答

15
使用Thread来执行服务器的监听、读取和写入操作。您可以将socket和其他networkstream对象声明为public,然后在线程函数中初始化它们。
Unity与Threads中的while循环不兼容,有时可能会冻结,但可以通过在读取或等待数据从socket到达的while循环中添加System.Threading.Thread.Sleep(1);解决这个问题。
请确保在OnDisable()函数中停止Thread。不要从新的Thread函数中访问Unity API。只需在那里执行socket操作并将数据返回到一个public变量即可。
System.Threading.Thread SocketThread;
volatile bool keepReading = false;

// Use this for initialization
void Start()
{
    Application.runInBackground = true;
    startServer();
}

void startServer()
{
    SocketThread = new System.Threading.Thread(networkCode);
    SocketThread.IsBackground = true;
    SocketThread.Start();
}



private string getIPAddress()
{
    IPHostEntry host;
    string localIP = "";
    host = Dns.GetHostEntry(Dns.GetHostName());
    foreach (IPAddress ip in host.AddressList)
    {
        if (ip.AddressFamily == AddressFamily.InterNetwork)
        {
            localIP = ip.ToString();
        }

    }
    return localIP;
}


Socket listener;
Socket handler;

void networkCode()
{
    string data;

    // Data buffer for incoming data.
    byte[] bytes = new Byte[1024];

    // host running the application.
    Debug.Log("Ip " + getIPAddress().ToString());
    IPAddress[] ipArray = Dns.GetHostAddresses(getIPAddress());
    IPEndPoint localEndPoint = new IPEndPoint(ipArray[0], 1755);

    // Create a TCP/IP socket.
    listener = new Socket(ipArray[0].AddressFamily,
        SocketType.Stream, ProtocolType.Tcp);

    // Bind the socket to the local endpoint and 
    // listen for incoming connections.

    try
    {
        listener.Bind(localEndPoint);
        listener.Listen(10);

        // Start listening for connections.
        while (true)
        {
            keepReading = true;

            // Program is suspended while waiting for an incoming connection.
            Debug.Log("Waiting for Connection");     //It works

            handler = listener.Accept();
            Debug.Log("Client Connected");     //It doesn't work
            data = null;

            // An incoming connection needs to be processed.
            while (keepReading)
            {
                bytes = new byte[1024];
                int bytesRec = handler.Receive(bytes);
                Debug.Log("Received from Server");

                if (bytesRec <= 0)
                {
                    keepReading = false;
                    handler.Disconnect(true);
                    break;
                }

                data += Encoding.ASCII.GetString(bytes, 0, bytesRec);
                if (data.IndexOf("<EOF>") > -1)
                {
                    break;
                }

                System.Threading.Thread.Sleep(1);
            }

            System.Threading.Thread.Sleep(1);
        }
    }
    catch (Exception e)
    {
        Debug.Log(e.ToString());
    }
}

void stopServer()
{
    keepReading = false;

    //stop thread
    if (SocketThread != null)
    {
        SocketThread.Abort();
    }

    if (handler != null && handler.Connected)
    {
        handler.Disconnect(false);
        Debug.Log("Disconnected!");
    }
}

void OnDisable()
{
    stopServer();
}

1
你不需要调用 networkCode 或者 Start() 方法。你需要调用 startServer(); 方法。当游戏开始时,Unity 会自动调用 Start() 方法。我已经在 Start() 中调用了 startServer(); 方法。如果你不知道 Start() 方法是什么时候被调用的,建议你看一下我上面发布的代码,并学习一下 Unity API。 - Programmer
1
当调用startServer()函数时,它会创建一个指向networkCode函数的线程,并且该线程将在startServer()函数中调用SocketThread.Start();之后调用networkCode()函数。 - Programmer
1
好的。我建议在Unity开发过程中将IsBackground保持为true,否则会有许多线程在Unity中运行,导致套接字无法正确断开连接。当您仍有1个套接字在监听时,如果您尝试在Unity中再次测试代码,则会收到错误提示。这个话题比较高级,但是当您有时间了解后台线程和前台线程之间的区别时,请阅读有关IsBackground的内容。 - Programmer
我可以问一下为什么吗?因为在那之前,我创建了一个测试的DLL来进行实验。我先在Visual Studio中测试了那段代码,然后将其导入到Unity中,结果也能正常运行。但那只是为了返回一个字符串(仅供测试)。 - Salman Younas
1
我花了时间修复那些东西。不要改变任何东西,否则你会得到新的错误。你不需要异步。哦,还记得在将其编译为插件之前从Start中删除startServer();,这样你就不会调用它两次了。然后你可以在你的主应用程序中的Start()中调用startServer();。已经使用Hercules测试过,一切都正常,没有冻结或错误。不客气! - Programmer
显示剩余25条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接