unity3d远程调用python(xmlrpc)

c#远程调用python可以使用XMLRPC技术(使用别人写好的库CookComputing.XmlRpcV2.dll),可以参考https://blog.csdn.net/zxy13826134783/article/details/102977028

但是在unity3d中使用就不行了,可能是unity3d引擎做了特殊的限制,为了实现unity3d能够远程调用python,我们需要自己编写xml请求数据和模拟post请求

 

下面介绍两种方式,一种是使用unity3d的协程的方式模拟post请求,一种是使用c#的同步的方式模拟post请求

 

我想实现的大概流程是:

unity3d客户端读取图片文件,把图片文件数据转换为字节,再转换成base64的字符串发送到远程的python服务端,远程服务端接收图片数据并以png格式写入服务端的硬盘中

 

远程服务器代码如下(python,如何在服务器运行下面的代码可以参考我前面给出链接的文章):

注意:如果你的服务端是在阿里云或者腾讯云等云上,需要到控制台中开放666端口

import base64
from  xmlrpc.server import SimpleXMLRPCServer
import cv2
import numpy as np
def zi_tu(b_tu):
    tu_b = base64.b64decode(b_tu)
    imga = np.frombuffer(tu_b, np.uint8)
    img = cv2.imdecode(imga,cv2.IMREAD_COLOR)
    cv2.imwrite("./server.png",img)
    print(img)
    return None

def getHelloWorld(b_tu):
    zi_tu(b_tu)
    return 'Hello world'
 
if __name__ == '__main__' :
    server = SimpleXMLRPCServer(("",666), allow_none=True)
    server.register_function(getHelloWorld, "getHelloWorld")
    server.serve_forever()

 

 

unity3d客户端代码:

 

读取图片文件的代码(放心,后面我会给出全部的代码):

    //图片转换为base64的字符串
    string ImageString = "";
    //定义文件流,读取图片文件,需要你自己放一张png图片在unity3d工程目录下
    FileStream fs = File.OpenRead("test.png"); //OpenRead
    int filelength = 0;
    filelength = (int)fs.Length; //获得文件长度 
    Byte[] image = new Byte[filelength]; //建立一个字节数组 
    fs.Read(image, 0, filelength); //按字节流读取 
    //把图片的字节流转换为base64的字符串,方便发送到python服务器
    ImageString=Convert.ToBase64String(image);
    fs.Close();

 

拼接xml请求数据方法如下:

 /// 
    /// 拼接要传递的xml参数
    /// 
    /// 包含方法参数值的Hashtable
    /// 要远程调用的方法名
    /// 
    public string buildXMLRPCRequest(Hashtable FieldArray, string MethodName)
    {
        //要发送的xml字符串
        //下面的" + FieldArray["para1"] + @"
        //是拼接方法的参数,现在传递的是字符串,所以需要拼接标签,如果发送的是整形数据
        //需要拼接,如果要传递多个参数,根据需要在标签下添加标签
        string SendXmlString = @"

   "+MethodName+@"
      
         
            " + FieldArray["para1"] + @"
         
      
";
        return SendXmlString;
    }

 

方式1   使用unity3d协程的方式的代码如下:

    /// 
    /// 使用Unity3d特有的协程方式发送远程调用请求
    /// 
    /// 要远程调用的方法名
    /// 包含的方法参数值的Hashtable
    public void UnityPostXML( string MethodName,Hashtable FieldArray)
    {
        //python服务端的url地址
        string WebServiceURL = "http://47.94.102.147:666/";

        //获取发送的xml请求字符串
        string XMLRequest = buildXMLRPCRequest(FieldArray, MethodName);

        //把xml请求字符串转换为字节数组,方便发送
        System.Text.Encoding enc = System.Text.Encoding.UTF8;
        byte[] myByteArray = enc.GetBytes(XMLRequest);




        var form = new WWWForm();
        var url = WebServiceURL;

        
        //获取请求头请指定请求头类型
        var headers = form.headers;
        headers["Content-Type"] = "text/xml;charset=UTF-8";

      
        var www = new WWW(WebServiceURL, myByteArray, headers);

        
        //开启协程,发送请求
        StartCoroutine(WaitForRequest(www));
    }


    IEnumerator WaitForRequest(WWW www)
    {
        yield return www;

        // 没有出错
        if (www.error == null)
        {
            Debug.Log("请求的内容为: " + www.text);
        }
        else
        {
            Debug.Log("请求出错 " + www.error);
        }
    }

 

方式2   使用c#的同步的方式的代码如下:

    /// 
    /// C#方式远程调用
    /// 
    /// 需要远程调用的方法名
    /// 带有方法参数值的Hashtable
    /// 
    private  string NormalXMLCall( string MethodName, Hashtable Fields)
    {
        //远程python服务端的地址
        string WebServiceURL = "http://47.94.102.147:666";

        //获取发送的xml请求字符串
        string XMLRequest = buildXMLRPCRequest(Fields, MethodName);
        
        //创建Http请求对象
        HttpWebRequest httpRequest =(HttpWebRequest)WebRequest.Create(WebServiceURL);
        //请求方式为Post
        httpRequest.Method = "POST";

        //指定发送的内容格式
        httpRequest.ContentType = "text/xml";

        //把xml请求字符串转换为字节数组,方便发送 
        byte[] bytedata = Encoding.UTF8.GetBytes(XMLRequest);

        // 获取请求流.
        Stream requestStream = httpRequest.GetRequestStream();

        // 把数据写入请求流中
        requestStream.Write(bytedata, 0, bytedata.Length);
        requestStream.Close();

        //获取响应流对象
        HttpWebResponse httpResponse = (HttpWebResponse)httpRequest.GetResponse();

        //获取响应流
        Stream receiveStream = httpResponse.GetResponseStream ();

        //指定流读取器
        StreamReader readStream = new StreamReader (receiveStream, Encoding.UTF8);
        //读取相应数据,xml格式返回,需要自己解析
        string  ReceivedData    =   readStream.ReadToEnd ();
        httpResponse.Close ();
        readStream.Close ();

        return  ReceivedData;
    }

 

全部代码如下:

using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Text;
using System.Threading;
using UnityEngine;

public class NewBehaviourScript : MonoBehaviour {
    //图片转换为base64的字符串
    string ImageString = "";
	void Start () {
            
            //定义文件流,读取图片文件
            FileStream fs = File.OpenRead("test.png"); //OpenRead
            int filelength = 0;
            filelength = (int)fs.Length; //获得文件长度 
            Byte[] image = new Byte[filelength]; //建立一个字节数组 
            fs.Read(image, 0, filelength); //按字节流读取 
            //把图片的字节流转换为base64的字符串,方便发送到python服务器
            ImageString=Convert.ToBase64String(image);
            fs.Close();

            try
            {
                //客户端远程调用python的方法,自定义Post方式发送
                XMLRpcMethod(ImageString);

            }
            catch (Exception ex)
            {
                Debug.Log("发生异常" + ex.Message);

            }
	}


    /// 
    /// 客户端远程调用python的方法,自定义Post方式发送
    /// 
    /// 传递过去的图片数据
    void XMLRpcMethod(string ImageString)
    {
        Hashtable ht = new Hashtable();
        //定义参数的键值对,值保存对应的方法参数的值
        ht.Add("para1", ImageString);

        //方式1:使用unity3d特有的协程的方式远程调用
        //UnityPostXML("getHelloWorld", ht); 
        

        //方式2:使用传统的c#方式进行远程调用,第一个参数为方法名,第二个参数存储方法参数的值
        string returnData=NormalXMLCall("getHelloWorld", ht);
        Debug.Log(returnData);
       
    }


    /// 
    /// 拼接要传递的xml参数
    /// 
    /// 包含方法参数值的Hashtable
    /// 要远程调用的方法名
    /// 
    public string buildXMLRPCRequest(Hashtable FieldArray, string MethodName)
    {
        //要发送的xml字符串
        //下面的" + FieldArray["para1"] + @"
        //是拼接方法的参数,现在传递的是字符串,所以需要拼接标签,如果发送的是整形数据
        //需要拼接,如果要传递多个参数,根据需要在标签下添加标签
        string SendXmlString = @"

   "+MethodName+@"
      
         
            " + FieldArray["para1"] + @"
         
      
";
        return SendXmlString;
    }




    /// 
    /// 使用Unity3d特有的协程方式发送远程调用请求
    /// 
    /// 要远程调用的方法名
    /// 包含的方法参数值的Hashtable
    public void UnityPostXML( string MethodName,Hashtable FieldArray)
    {
        //python服务端的url地址
        string WebServiceURL = "http://47.94.102.147:666/";

        //获取发送的xml请求字符串
        string XMLRequest = buildXMLRPCRequest(FieldArray, MethodName);

        //把xml请求字符串转换为字节数组,方便发送
        System.Text.Encoding enc = System.Text.Encoding.UTF8;
        byte[] myByteArray = enc.GetBytes(XMLRequest);




        var form = new WWWForm();
        var url = WebServiceURL;

        
        //获取请求头请指定请求头类型
        var headers = form.headers;
        headers["Content-Type"] = "text/xml;charset=UTF-8";

      
        var www = new WWW(WebServiceURL, myByteArray, headers);

        
        //开启协程,发送请求
        StartCoroutine(WaitForRequest(www));
    }


    IEnumerator WaitForRequest(WWW www)
    {
        yield return www;

        // 没有出错
        if (www.error == null)
        {
            Debug.Log("请求的内容为: " + www.text);
        }
        else
        {
            Debug.Log("请求出错 " + www.error);
        }
    }



    /// 
    /// C#方式远程调用
    /// 
    /// 需要远程调用的方法名
    /// 带有方法参数值的Hashtable
    /// 
    private  string NormalXMLCall( string MethodName, Hashtable Fields)
    {
        //远程python服务端的地址
        string WebServiceURL = "http://47.94.102.147:666";

        //获取发送的xml请求字符串
        string XMLRequest = buildXMLRPCRequest(Fields, MethodName);
        
        //创建Http请求对象
        HttpWebRequest httpRequest =(HttpWebRequest)WebRequest.Create(WebServiceURL);
        //请求方式为Post
        httpRequest.Method = "POST";

        //指定发送的内容格式
        httpRequest.ContentType = "text/xml";

        //把xml请求字符串转换为字节数组,方便发送 
        byte[] bytedata = Encoding.UTF8.GetBytes(XMLRequest);

        // 获取请求流.
        Stream requestStream = httpRequest.GetRequestStream();

        // 把数据写入请求流中
        requestStream.Write(bytedata, 0, bytedata.Length);
        requestStream.Close();

        //获取响应流对象
        HttpWebResponse httpResponse = (HttpWebResponse)httpRequest.GetResponse();

        //获取响应流
        Stream receiveStream = httpResponse.GetResponseStream ();

        //指定流读取器
        StreamReader readStream = new StreamReader (receiveStream, Encoding.UTF8);
        //读取相应数据,xml格式返回,需要自己解析
        string  ReceivedData    =   readStream.ReadToEnd ();
        httpResponse.Close ();
        readStream.Close ();

        return  ReceivedData;
    }



    /// 
    /// 解析xml,获取返回的内容
    /// 
    /// 需要解析的xml字符串
    /// 
    public string findValue(string ReturnXml)
    {
        //找出两个标签的下标值,然后截取
        int first=ReturnXml.IndexOf("", 0);
        int second = ReturnXml.IndexOf("", first);
        string value = ReturnXml.Substring(first + 8, second - first-8);
        return value;
    }


}



 

 

 

 

以方式2发起调用的结果如下:

 

服务端运行结果:

打印输出如下,输出的是以三维数组表示的图片数据

unity3d远程调用python(xmlrpc)_第1张图片

同时可以看到图片已经保存在服务端上

unity3d远程调用python(xmlrpc)_第2张图片

 

unity3d返回的数据打印输出如下:

unity3d远程调用python(xmlrpc)_第3张图片

 

可以看到是xml格式的,看到Hello world已经正确返回

 

针对我这个特定的测试方法,我的解析xml的方法如下:

    /// 
    /// 解析xml,获取返回的内容
    /// 
    /// 需要解析的xml字符串
    /// 
    public string findValue(string ReturnXml)
    {
        //找出两个标签的下标值,然后截取
        int first=ReturnXml.IndexOf("", 0);
        int second = ReturnXml.IndexOf("", first);
        string value = ReturnXml.Substring(first + 8, second - first-8);
        return value;
    }

 

这样就可以吧Hello world信息给取出来了

 

你可能感兴趣的:(Unity3D,unity3d,xmlrpc,rpc,python,c#)