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返回的数据打印输出如下:
可以看到是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信息给取出来了