Stone Monarch
Stone Monarch

Reputation: 395

Shutdown RPyC server from client

I've created a RPyC server. Connecting works, all my exposed methods work. Now I am looking to shut down the server from the client. Is this even possible? Security is not a concern as I am not worried about a rogue connection shutting down the server.

It is started with (Which is blocking):

from rpyc import ThreadPoolServer
from service import MyService

t = ThreadPoolServer(MyService(), port=56565)
t.start()

Now I just need to shut it down. I haven't found any documentation on stopping the server.

Upvotes: 2

Views: 1998

Answers (2)

SomeFrenchGuy
SomeFrenchGuy

Reputation: 31

In case anybody is interested, I have found another way of doing it. I'm just making the server object on a global scope, and then adding an exposed method to close it.

import rpyc
from rpyc.utils.server import ThreadedServer


class MyService(rpyc.Service):

    def exposed_stop(self):
        server.close()

    def exposed_echo(self, text):
        print(text)

server = ThreadedServer(MyService, port = 18812)

if __name__ == "__main__":

    print("server start")

    server.start()

    print("Server closed")

On the client side, you will have an EOF error due to the connection being remotely closed. So it's better to catch it.

import rpyc

c = rpyc.connect("localhost", 18812)
c.root.echo("hello")

try :
    c.root.stop()
except EOFError as e:
    print("Server was closed")

EDIT: I needed to be able to dinamically specify the server. So I came with this (Is it better ? I don't know, but it works well. Be careful though, if you have multiple server running this service: things could become weird):

    import rpyc
from rpyc.utils.server import ThreadedServer



class MyService(rpyc.Service):
    _server:ThreadedServer

    @staticmethod
    def set_server(inst=ThreadedServer):
        MyService._server = inst

    def exposed_stop(self):
        if self._server:
            self._server.close()

    def exposed_echo(self, text):
        print(text)


if __name__ == "__main__":

    server = ThreadedServer(MyService, port = 18812)
    MyService.set_server(server)
    
    print("server start")

    server.start()

    print("Server closed")

PS: It probably is possible to avoid the EOF error by using Asynchronous Operations

Upvotes: 2

midrare
midrare

Reputation: 2764

You can add to your Service class the method:

def exposed_stop(self):
    pid = os.getpid()

    if platform.system() == 'Windows':
        PROCESS_TERMINATE = 1
        handle = ctypes.windll.kernel32.OpenProcess(PROCESS_TERMINATE, False, pid)
        ctypes.windll.kernel32.TerminateProcess(handle, -1)
        ctypes.windll.kernel32.CloseHandle(handle)
    else:
        os.kill(pid, signal.SIGTERM)

This will make the service get its own PID and send SIGTERM to itself. There may be an better way of doing this hiding in some dark corner of the API, but I've found no better method.


If you want to do clean-up before your thread terminates, you can set up exit traps:

t = rpyc.utils.server.ThreadedServer(service, port=port, auto_register=True)

# Set up exit traps for graceful exit.
signal.signal(signal.SIGINT, lambda signum, frame: t.close())
signal.signal(signal.SIGTERM, lambda signum, frame: t.close())

t.start() # blocks thread

# SIGTERM or SIGINT was received and t.close() was called
print('Closing service.')
t = None

shutil.rmtree(tempdir)
# etc.

Upvotes: 1

Related Questions