c#远程调用linux服务器的Python脚本

参考:https://blog.csdn.net/zxy13826134783/article/details/102977028


c#端

2021/11/29 更新

需要引用一个库:CookComputing.XmlRpcV2.dll (这个我放到资源里去了,找不到的也可以私信我)

ProxyInterface.cs

using CookComputing.XmlRpc;

namespace RemoteServerSample
{
    [XmlRpcUrl("http://IP地址:端口")]
    public interface ProxyInterface:IXmlRpcProxy
    {	// getData就是远程调用的Python函数
        [XmlRpcMethod("getData")]
        string getData(string seq);
    }
}

JsonNetResult.cs

using Newtonsoft.Json;

namespace RemoteServerSample
{
    public static class JsonNetResult
    {
        /// <summary>
        /// Json序列化设置
        /// </summary>
        public static JsonSerializerSettings Settings = new JsonSerializerSettings
        {
            ReferenceLoopHandling = ReferenceLoopHandling.Ignore
        };

        /// <summary>
        /// 执行Json序列化
        /// </summary>
        /// <param name="obj"></param>
        /// <returns></returns>
        public static string SerializeObject(object obj)
        {
            return JsonConvert.SerializeObject(obj, Settings);
        }

        /// <summary>
        /// 反序列化对象
        /// </summary>
        /// <returns></returns>
        public static T DeserializeObject<T>(string json)
        {
            return JsonConvert.DeserializeObject<T>(json, Settings);
        }
    }

}

Program.cs

using CookComputing.XmlRpc;
using System;
using System.Collections.Generic;

namespace RemoteServerSample
{
    class Program
    {
        static void Main(string[] args)
        {
            Program program = new Program();
            // 这个类的定义和对象的赋值我省略了
            GeneOrder order = new GeneOrder();
      		...
      		// 序列化
            var seqName = JsonNetResult.SerializeObject(order);
			//远程调用
            program.CallAlphags_tat(seqName); 
        }

        /// <summary>
        /// 远程调用
        /// </summary>
        private  void CallAlphags_tat(string seqName)
        {
            ProxyInterface proxy = (ProxyInterface)XmlRpcProxyGen.Create(typeof(ProxyInterface));
            Console.WriteLine("BEGIN------------------");
            Console.WriteLine(proxy.getData(seqName));
            Console.WriteLine("END---------------------");
            Console.ReadLine();
        }

linux服务器端

2021/11/25 更新:并发&放后台

原始做法的问题是:1. 要保持脚本始终在后台运行;2.没有考虑多用户访问,即需要并发处理请求的情况。
还有我自己的小问题3:跑脚本的环境是miniconda里的一个环境,每次要先conda activate 环境名。

3是最容易解决的, 去miniconda的路径下面,找到那个环境的解释器,其路径类似:

/XXXXX/miniconda2/envs/环境名/bin/python

用这个路径替代原本的 conda activate 环境名 | python 即可。

**2的解决也很简单。**参考这里:https://blog.csdn.net/weixin_38278993/article/details/89303497
SimpleXMLRPCServer是单线程访问的,所以有多个用户请求的时候,要排队,顺序执行。

改多线程的代码如下:

# 不需要额外装,已经内置了
from socketserver import ThreadingMixIn

# 新建一个继承了ThreadingMixIn(支持多线程)和SimpleXMLRPCServer(支持远程调用)的类
class ThreadXMLRPCServer(ThreadingMixIn, SimpleXMLRPCServer):
    pass
    
if __name__ == '__main__':
	# 只改一下sercer即可
    server = ThreadXMLRPCServer(('ip地址', 6060))
    # 事实上可以注册多个函数
    server.register_function(getData, "getData")
    server.serve_forever()

python本地远程调用服务器上的代码

from xmlrpc.client import ServerProxy

if __name__ == "__main__":
    data = 'xxxxxx'
    server = ServerProxy("http://ip地址:6060")
    print(server.getData(data))

对于1,我考虑了两种做法。

  1. 把py脚本放到一个docker容器里,然后让容器开着,在容器里保持py运行;
  2. 用nohup,把脚本放到后台运行。

暂时没考虑把脚本做成服务,因为服务器暂时不能重启。

我最终选了解法2,因为我的代码需要本地调用一个软件,虽然我在docker容器中安装了那个软件,但是容器的算力有限,还是服务器跑起来更快。语句如下:

nohup /XXXXX/miniconda2/envs/环境名/bin/python 脚本名.py &

此时,如果退出当前终端,再用jobs其实是不会显示这条命令的,但是可以用ps aux | grep -i python来查:

root      1x  0.0  0.1 160 568 ?       S    16:41   0:02 /xx/miniconda2/envs/xx/bin/python xx.py

大概有这种信息。中间的S表示脚本在等待中断,比如这里就是等待客户的请求(是post,不是get),这就表示脚本确实还在后台持续运行中。
在这里插入图片描述
对于解法1,我的步骤大概如下:

  1. 开启一个共享文件夹、开启端口映射 (可以回看我的docker系列文章)
docker run -itd --privileged -v /宿主机路径:/容器内路径 -p 6060:6060 镜像名:标签
  1. 把要跑的脚本放在共享文件夹下,然后docker attach 容器id 进容器,拷贝到别处(在共享文件夹可能有权限问题,我一般放容器内部)
  2. nohup跑脚本

如果此时不能访问服务
在这里插入图片描述
那么先检查在容器里能不能ping通 宿主机、8.8.8.8啥的(还有一些我忘了,以后想起来再说吧),如果都通,但就是无法访问服务,那检查下main函数里server = SimpleXMLRPCServer((‘localhost’, 6060)) 这一句有没有改成容器的ip。在容器里用 ifconfig ,看eth0的inet值。用这个值替代原来的localhost,应该就可以了。
在这里插入图片描述
还有一种奇葩的是iptables那边缺少规则。在宿主机,用下面这条命令看一下,如果没有容器ip和宿主机ip的映射,要添加规则的。(虽然正常情况下docker会自动创建,但是万一没有,可以考虑这个方法)

iptables -vnL -t nat

添加

iptables -t nat -A PREROUTING -m tcp -p tcp --dport 6060 -j DNAT --to-destination 容器ip:6060
iptables -t nat -A POSTROUTING -m tcp -p tcp --dport 6060 -d 172.17.0.2 -j SNAT --to-source 宿主机ip

原始做法

json和xmlrpc都是标准库的,不用安装

import json
from xmlrpc.server import SimpleXMLRPCServer

# 输入输出都是json字符串
# json.loads要求的格式为:'{"name":"XXX", "List":[]}'
# 即最外层是单引号,里面都是双引号
def getData(data):
    info = json.loads(data)
    ...# 修改info
    return json.dumps(info)
    
if __name__ == '__main__':
    server = SimpleXMLRPCServer(('localhost', 6060))
    # 不管带不带参数,都是只写函数名的
    server.register_function(getData, "getData")
    server.serve_forever()

在服务器上执行python脚本
在这里插入图片描述
保持运行,c#调用即可

端口问题

参考:https://blog.csdn.net/weixin_45869725/article/details/114670943

Centos7默认安装了firewalld,而非iptables

查看防火墙状态
systemctl status firewalld

启动
systemctl start firewalld.service

开启某个端口:--permanent  //--permanent永久生效,没有此参数防火墙重启便失效
firewall-cmd --zone=public --add-port=6060/tcp

关闭某个端口:--permanent 同理
firewall-cmd --zone=public --remove-port=6060/tcp

开启端口
iptables -I INPUT -p TCP --dport 6060 -j ACCEPT

查看端口占用情况的命令:lsof -i 
netstat -lntu

查看某一端口的占用情况: lsof -i:端口号

# 如果报错某个端口
结束占用端口的进程:kill -9 进程号

报错:OSError: [Errno 98] Address already in use

找到占用该端口的进程,杀掉即可

[root@localhost xxx]# lsof -i:6060
COMMAND    PID USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
nc      150140 root    3u  IPv6 704733      0t0  TCP *:6060 (LISTEN)
nc      150140 root    4u  IPv4 704734      0t0  TCP *:6060 (LISTEN)
[root@localhost alphags]# kill -9 150140
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值