将Python XML-RPC服务器作为Windows服务

7
我试图创建一个作为Windows服务的XML-RPC服务器。
XML-RPC服务器能够像FTP一样获取和放置文件,并通过os.system()执行远程客户端发送给它的命令。
我正在尝试在Windows服务事件循环中封装服务器。
创建服务时没有任何问题。
python.exe remoteServer.py --startup=auto install
Installing service XMLRPCServerService
Service installed

启动服务时没有问题。

python.exe remoteServer.py start
Starting service XMLRPCServerService

停止服务时没有问题。
python.exe remoteServer.py stop
Stopping service XMLRPCServerService

重新启动服务时出现问题。
python.exe remoteServer.py start
Starting service XMLRPCServerService
Error starting service: An instance of the service is already running.

查看进程监视器,我看到一个名为“pythonservice.exe”的进程仍在运行。

这是remoteServer.py的内容:

#!/bin/python

# $Revision: 1.7 $
# $Author: dot $
# $Date: 2011/12/07 01:16:13 $

LISTEN_HOST='0.0.0.0'
LISTEN_PORT=8000

import os
import SocketServer
import BaseHTTPServer
import SimpleHTTPServer
import xmlrpclib
import SimpleXMLRPCServer 
import socket
import httplib
import inspect
import win32service
import win32serviceutil
import win32api
import win32con
import win32event
import win32evtlogutil

class XMLRPCServer(BaseHTTPServer.HTTPServer,SimpleXMLRPCServer.SimpleXMLRPCDispatcher):
    def __init__(self, server_address, HandlerClass, logRequests=True):
        """ XML-RPC server. """
        self.logRequests = logRequests

        SimpleXMLRPCServer.SimpleXMLRPCDispatcher.__init__(self,False,None)
        SocketServer.BaseServer.__init__(self, server_address, HandlerClass)

        self.socket = socket.socket(
                socket.AF_INET, socket.SOCK_STREAM)

        self.server_bind()
        self.server_activate()

class XMLRPCRequestHandler(SimpleXMLRPCServer.SimpleXMLRPCRequestHandler):
    """ XML-RPC request handler class. """
    def setup(self):
        self.connection = self.request
        self.rfile = socket._fileobject(self.request, "rb", self.rbufsize)
        self.wfile = socket._fileobject(self.request, "wb", self.wbufsize)

    def do_POST(self):
        """ Handles the HTTPS request.
            It was copied out from SimpleXMLRPCServer.py and modified to shutdown the socket cleanly.
        """

        try:
            # get arguments
            data = self.rfile.read(int(self.headers["content-length"]))
            # In previous versions of SimpleXMLRPCServer, _dispatch
            # could be overridden in this class, instead of in
            # SimpleXMLRPCDispatcher. To maintain backwards compatibility,
            # check to see if a subclass implements _dispatch and dispatch
            # using that method if present.
            response = self.server._marshaled_dispatch(
                    data, getattr(self, '_dispatch', None)
                )
        except: 
            # This should only happen if the module is buggy
            # internal error, report as HTTP server error
            self.send_response(500)
            self.end_headers()
        else:
            # got a valid XML RPC response
            self.send_response(200)
            self.send_header("Content-type", "text/xml")
            self.send_header("Content-length", str(len(response)))
            self.end_headers()
            self.wfile.write(response)

            # shut down the connection
            self.wfile.flush()
            self.connection.shutdown() # Modified here!

def XMLRPCServerGet(HandlerClass = XMLRPCRequestHandler,ServerClass = XMLRPCServer):
    """Test xml rpc over http server"""
    class xmlrpc_registers:
        def _listMethods(self):
            return list_public_methods(self)

        def _methodHelp(self, method):
            f = getattr(self, method)
            return inspect.getdoc(f)

        def list(self, dir_name):
            """list(dir_name) => [<filenames>]

            Returns a list containing the contents of the named directory.
            """
            return os.listdir(dir_name)

        def put(self,filename,filedata):
            try:
                with open(filename, "wb") as handle:
                    handle.write(filedata.data) 
                    handle.close()
                return True
            except Exception,ex:
                return 'error'

        def get(self,filepath):
            try:
                handle = open(filepath)
                return xmlrpclib.Binary(handle.read())
                handle.close()
            except:
                return 'error'

        def system(self, command):
            result = os.system(command)
            return result

    server_address = (LISTEN_HOST, LISTEN_PORT) # (address, port)
    server = ServerClass(server_address, HandlerClass)    
    server.register_introspection_functions()
    server.register_instance(xmlrpc_registers())    
    #sa = server.socket.getsockname()
    return server
    #server.serve_forever()
    #print "Serving HTTP on", sa[0], "port", sa[1]
    #try:
    #    #print 'Use Control-C to exit'
    #    server.serve_forever()
    #except KeyboardInterrupt:
    #    #print 'Exiting'

class XMLRPCServerService(win32serviceutil.ServiceFramework):
    _svc_name_         = "XMLRPCServerService"
    _svc_display_name_ = "XMLRPCServerService"
    _svc_description_  = "Tests Python service framework by receiving and echoing messages over a named pipe"

    def __init__(self, args):
        win32serviceutil.ServiceFramework.__init__(self, args)
        self.hWaitStop = win32event.CreateEvent(None, 0, 0, None)           

    def SvcStop(self):
        self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
        win32event.SetEvent(self.hWaitStop)                    

    def SvcDoRun(self):
        import servicemanager      
        servicemanager.LogMsg(servicemanager.EVENTLOG_INFORMATION_TYPE,servicemanager.PYS_SERVICE_STARTED,(self._svc_name_, '')) 
        self.timeout = 100
        server = XMLRPCServerGet()
        #server.serve_forever()

        while 1:
            # Wait for service stop signal, if I timeout, loop again
            rc = win32event.WaitForSingleObject(self.hWaitStop, self.timeout)
            # Check to see if self.hWaitStop happened
            if rc == win32event.WAIT_OBJECT_0:
                # Stop signal encountered
                server.shutdown()
                servicemanager.LogInfoMsg("XMLRPCServerService - STOPPED")
                break
            else:
                server.handle_request()
                servicemanager.LogInfoMsg("XMLRPCServerService - is alive and well")   

def ctrlHandler(ctrlType):
    return True

if __name__ == '__main__':   
    win32api.SetConsoleCtrlHandler(ctrlHandler, True)   
    win32serviceutil.HandleCommandLine(XMLRPCServerService)

1
我不确定这是否是问题的根源,但看起来你在 SvcStop 方法的结尾处缺少对 self.ReportServiceStatus(win32service.SERVICE_STOPPED) 的调用。 - David K. Hess
@David K. Hess,您说得完全正确!它就在明处隐藏着! - user78706
1
谢谢!我已经将它添加为答案。 - David K. Hess
2个回答

4

看起来你的SvcStop方法末尾缺少一个对self.ReportServiceStatus(win32service.SERVICE_STOPPED)的调用。


3

这个应该要归功于David K. Hess。
他建议我在类XMLRPCServerService的def SvcStop的第142行后添加以下内容:

        self.ReportServiceStatus(win32service.SERVICE_STOPPED)

以下是最终版本,供有兴趣的人参考:
#!/bin/python

# $Revision: 1.7 $
# $Author: dot $
# $Date: 2011/12/07 01:16:13 $

LISTEN_HOST='0.0.0.0'
LISTEN_PORT=8000

import os
import SocketServer
import BaseHTTPServer
import SimpleHTTPServer
import xmlrpclib
import SimpleXMLRPCServer 
import socket
import httplib
import inspect
import win32service
import win32serviceutil
import win32api
import win32con
import win32event
import win32evtlogutil

class XMLRPCServer(BaseHTTPServer.HTTPServer,SimpleXMLRPCServer.SimpleXMLRPCDispatcher):
    def __init__(self, server_address, HandlerClass, logRequests=True):
        """ XML-RPC server. """
        self.logRequests = logRequests

        SimpleXMLRPCServer.SimpleXMLRPCDispatcher.__init__(self,False,None)
        SocketServer.BaseServer.__init__(self, server_address, HandlerClass)

        self.socket = socket.socket(
                socket.AF_INET, socket.SOCK_STREAM)

        self.server_bind()
        self.server_activate()

class XMLRPCRequestHandler(SimpleXMLRPCServer.SimpleXMLRPCRequestHandler):
    """ XML-RPC request handler class. """
    def setup(self):
        self.connection = self.request
        self.rfile = socket._fileobject(self.request, "rb", self.rbufsize)
        self.wfile = socket._fileobject(self.request, "wb", self.wbufsize)

    def do_POST(self):
        """ Handles the HTTPS request.
            It was copied out from SimpleXMLRPCServer.py and modified to shutdown the socket cleanly.
        """

        try:
            # get arguments
            data = self.rfile.read(int(self.headers["content-length"]))
            # In previous versions of SimpleXMLRPCServer, _dispatch
            # could be overridden in this class, instead of in
            # SimpleXMLRPCDispatcher. To maintain backwards compatibility,
            # check to see if a subclass implements _dispatch and dispatch
            # using that method if present.
            response = self.server._marshaled_dispatch(
                    data, getattr(self, '_dispatch', None)
                )
        except: 
            # This should only happen if the module is buggy
            # internal error, report as HTTP server error
            self.send_response(500)
            self.end_headers()
        else:
            # got a valid XML RPC response
            self.send_response(200)
            self.send_header("Content-type", "text/xml")
            self.send_header("Content-length", str(len(response)))
            self.end_headers()
            self.wfile.write(response)

            # shut down the connection
            self.wfile.flush()
            self.connection.shutdown() # Modified here!

def XMLRPCServerGet(HandlerClass = XMLRPCRequestHandler,ServerClass = XMLRPCServer):
    """Test xml rpc over http server"""
    class xmlrpc_registers:
        def _listMethods(self):
            return list_public_methods(self)

        def _methodHelp(self, method):
            f = getattr(self, method)
            return inspect.getdoc(f)

        def list(self, dir_name):
            """list(dir_name) => [<filenames>]

            Returns a list containing the contents of the named directory.
            """
            return os.listdir(dir_name)

        def put(self,filename,filedata):
            try:
                with open(filename, "wb") as handle:
                    handle.write(filedata.data) 
                    handle.close()
                return True
            except Exception,ex:
                return 'error'

        def get(self,filepath):
            try:
                handle = open(filepath)
                return xmlrpclib.Binary(handle.read())
                handle.close()
            except:
                return 'error'

        def system(self, command):
            result = os.system(command)
            return result

    server_address = (LISTEN_HOST, LISTEN_PORT) # (address, port)
    server = ServerClass(server_address, HandlerClass)    
    server.register_introspection_functions()
    server.register_instance(xmlrpc_registers())    
    #sa = server.socket.getsockname()
    return server
    #server.serve_forever()
    #print "Serving HTTP on", sa[0], "port", sa[1]
    #try:
    #    #print 'Use Control-C to exit'
    #    server.serve_forever()
    #except KeyboardInterrupt:
    #    #print 'Exiting'

class XMLRPCServerService(win32serviceutil.ServiceFramework):
    _svc_name_         = "XMLRPCServerService"
    _svc_display_name_ = "XMLRPCServerService"
    _svc_description_  = "Tests Python service framework by receiving and echoing messages over a named pipe"

    def __init__(self, args):
        win32serviceutil.ServiceFramework.__init__(self, args)
        self.hWaitStop = win32event.CreateEvent(None, 0, 0, None)           

    def SvcStop(self):
        self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
        self.ReportServiceStatus(win32service.SERVICE_STOPPED)
        win32event.SetEvent(self.hWaitStop)                    

    def SvcDoRun(self):
        import servicemanager      
        servicemanager.LogMsg(servicemanager.EVENTLOG_INFORMATION_TYPE,servicemanager.PYS_SERVICE_STARTED,(self._svc_name_, '')) 
        self.timeout = 100
        server = XMLRPCServerGet()
        #server.serve_forever()

        while 1:
            # Wait for service stop signal, if I timeout, loop again
            rc = win32event.WaitForSingleObject(self.hWaitStop, self.timeout)
            # Check to see if self.hWaitStop happened
            if rc == win32event.WAIT_OBJECT_0:
                # Stop signal encountered
                server.shutdown()
                servicemanager.LogInfoMsg("XMLRPCServerService - STOPPED")
                break
            else:
                server.handle_request()
                servicemanager.LogInfoMsg("XMLRPCServerService - is alive and well")   

def ctrlHandler(ctrlType):
    return True

if __name__ == '__main__':   
    win32api.SetConsoleCtrlHandler(ctrlHandler, True)   
    win32serviceutil.HandleCommandLine(XMLRPCServerService)

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