通过TCP

时间:2017-03-01 12:25:57

标签: python unity3d tcp simulation

为了进行试验研究,我尝试将Unity中的简单模拟环境与基于神经网络的python控制器连接起来。模拟Unity环境中的主角应该根据它的经验学习行为:

  1. 主角扫描环境并将其存储在数据结构中;
  2. 此数据结构通过TCP发送到python中正在运行的服务器;
  3. 复杂的计算(基于从Unity收到的数据)在python中执行,它产生一个结果(动作);
  4. 结果(动作)通过TCP发送回Unity。角色执行与结果相对应的动作。
  5. 重复步骤1-4直到无穷大(除非客户端或服务器停止)。
  6. 这个过程可以被抽象为通过TCP发送的Ping-Pong消息,其中Unity发送" Ping",Python进行一些计算并生成一个" Pong"正被送回Unity更新模拟并再次生成一个" Ping"并将其发送到python ...

    我目前正在玩具项目中测试这个过程。我有一个脚本附加到Unity场景中的主摄像头,如下所示:

    using UnityEngine;
    using System.Collections;
    using System;
    using System.IO;
    using System.Net.Sockets;
    using Newtonsoft.Json;
    using System.Threading;
    
    
    
    public class networkSocketSO : MonoBehaviour
    {
        public String host = "localhost";
        public Int32 port = 50000;
    
        internal Boolean socket_ready = false;
        internal String input_buffer = "";
    
        TcpClient tcp_socket;
        NetworkStream net_stream;
    
        StreamWriter socket_writer;
        StreamReader socket_reader;
    
        private void Start()
        {
            setupSocket();
        }
    
    
         void Update()
         {
            string received_data = readSocket();
            switch (received_data)
                 {
                     case "pong":
                         Debug.Log("Python controller sent: " + (string)received_data);
                         writeSocket("ping");
                         break;
                     default:
                         Debug.Log("Nothing received");
                         break;
             }
         }
    
        void OnApplicationQuit()
        {
            closeSocket();
        }
    
        // Helper methods for:
        //...setting up the communication
        public void setupSocket()
        {
            try
            {
                tcp_socket = new TcpClient(host, port);
                net_stream = tcp_socket.GetStream();
                socket_writer = new StreamWriter(net_stream);
                socket_reader = new StreamReader(net_stream);
                socket_ready = true;
            }
            catch (Exception e)
            {
                // Something went wrong
                Debug.Log("Socket error: " + e);
            }
        }
    
        //... writing to a socket...
        public void writeSocket(string line)
        {
            if (!socket_ready)
                return;
    
            line = line + "\r\n";
            socket_writer.Write(line);
            socket_writer.Flush();
        }
    
        //... reading from a socket...
        public String readSocket()
        {
            if (!socket_ready)
                return "";
    
            if (net_stream.DataAvailable)
                return socket_reader.ReadLine();
    
            return "";
        }
    
        //... closing a socket...
        public void closeSocket()
        {
            if (!socket_ready)
                return;
    
            socket_writer.Close();
            socket_reader.Close();
            tcp_socket.Close();
            socket_ready = false;
        }
    }
    

    带有以下代码的python服务器:

    import socket
    import json
    
    host = 'localhost' 
    port = 50000
    backlog = 5 
    size = 1024 
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 
    s.bind((host,port)) 
    s.listen(backlog) 
    
    while 1:
        client, address = s.accept() 
        print "Client connected."
        while 1:
            data = client.recv(size)
            if data == "ping":
                print ("Unity Sent: " + str(data))
                client.send("pong")
            else:
                client.send("Bye!")
                print ("Unity Sent Something Else: " + str(data))
                client.close()
                break
    

    如果我启动服务器然后运行Unity项目,我会发生奇怪的事情。也就是说,python服务器似乎不理解" ping" Unity客户端发送的字符串。此外,我希望Unity模拟中的帧在收到" Pong"之前不要更新。来自python服务器的消息。

    我已经阅读了有关使用协同程序或线程的类似问题的解决方案,但我担心它会建立另一个" clock"结果没有真正的ping-pong-updateframe-ping-pong-updateframe ...进程。

    有什么想法吗?

    提前致谢!!!

    P.S。请记住,真正的信息不会是" Ping"和" Pong"但适当的Json对象。这只是一种简化。

    编辑: 我首先启动python服务器,然后启动Unity模拟(在编辑器中)。 在python控制台上我得到:

    Client connected.
    Unity Sent Something Else: ping
    

    ...这证明我声称" python不理解通过Unity发送的字符串(C#)"

    在Unity控制台中,我得到:

    enter image description here

2 个答案:

答案 0 :(得分:0)

我刚刚找到了第一个问题的解决方案,即" python不理解Unity发送的字符串"。似乎换行序列导致了这个问题。

我刚删除了:

line = line + "\r\n";

原始代码中的行。这样,字符串中不会添加其他转义字符。添加\r\n导致python的socket.recv()pingping\r\n进行比较,并且它始终在switch语句中触发默认大小写。

其他方式(来自python)我发送pong\n(注意转义字符),以便Unity(C#)streamWriter.ReadLine()能够接收清晰的pong

现在相互的消息理解起作用了。但是,它无法解决我的第二个问题 - 达到ping-pong - 更新帧过程。

答案 1 :(得分:0)

您应该将响应设为异步。您可以使用回调。您的错误是您正在进行连续轮询,并且套接字可用调用将始终为false。

在Unity中尝试此代码:

using UnityEngine;
using System.Collections;
using System;
using System.Net;
using System.Net.Sockets;

public class Client : MonoBehaviour {

private Socket _clientSocket = new Socket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp);
private byte[] _recieveBuffer = new byte[8142];

void Start() {
    SetupServer ();
}

void Update() {

}

void OnApplicationQuit()
{
    _clientSocket.Close ();
}

private void SetupServer()
{
    try
    {
        _clientSocket.Connect(new IPEndPoint(IPAddress.Loopback,50003));
    }
    catch(SocketException ex)
    {
        Debug.Log(ex.Message);
    }

    _clientSocket.BeginReceive(_recieveBuffer,0,_recieveBuffer.Length,SocketFlags.None,new AsyncCallback(ReceiveCallback),null);

}


private void ReceiveCallback(IAsyncResult AR)
{
    //Check how much bytes are recieved and call EndRecieve to finalize handshake
    int recieved = _clientSocket.EndReceive(AR);

    if(recieved <= 0)
        return;

    //Copy the recieved data into new buffer , to avoid null bytes
    byte[] recData = new byte[recieved];
    Buffer.BlockCopy(_recieveBuffer,0,recData,0,recieved);

    //Process data here the way you want , all your bytes will be stored in recData
    Debug.Log(System.Text.Encoding.Default.GetString(_recieveBuffer));
    SendData (System.Text.Encoding.Default.GetBytes ("ping"));

    //Start receiving again
    _clientSocket.BeginReceive(_recieveBuffer,0,_recieveBuffer.Length,SocketFlags.None,new AsyncCallback(ReceiveCallback),null);
}

private void SendData(byte[] data)
{
    SocketAsyncEventArgs socketAsyncData = new SocketAsyncEventArgs();
    socketAsyncData.SetBuffer(data,0,data.Length);
    _clientSocket.SendAsync(socketAsyncData);
}

}