Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to close a client? #227

Closed
davidkingzyb opened this issue Dec 29, 2018 · 15 comments
Closed

How to close a client? #227

davidkingzyb opened this issue Dec 29, 2018 · 15 comments
Labels

Comments

@davidkingzyb
Copy link

Hello,
I use flask-socketio and python-socketio[client].
After I disconnect from the client the script is still runing and ping pong with the server.
How to close the client?

@miguelgrinberg
Copy link
Owner

How do you disconnect? If you are calling the wait() method in the client, the expected behavior is that when the connection breaks this function should return, but this is all very new code, maybe I missed some use case.

@davidkingzyb
Copy link
Author

I tried calling the wait() method in the client, but after called disconnect() the connection was not broken. Is there a destory() method here? I can use it to cleanup the client.

    sio=socketio.Client()
    @sio.on('connect')
    def on_connect():
        print('disconnet')
        sio.disconnect()
    sio.connect(conf.IO_HOST)   
    sio.wait()

@miguelgrinberg
Copy link
Owner

You can't call disconnect in the connect handler, the connection isn't established yet at that point. Return False from the handler if you want to reject the connection. This is covered in the docs, by the way.

@davidkingzyb
Copy link
Author

I solved my problem by calling sio.eio.disconnect(True) after sio.disconnect(). I think maybe this step should be done in disconnect()? I'm not sure it's the right way, please tell me. Thanks for your help and Happy New Year ! 🎉

@davidkingzyb
Copy link
Author

#228

@saltaverde
Copy link

@davidkingzyb FYI I was having a problem in my application's unit tests where after I called sio.disconnect() and the test ended I was seeing BrokenPipeError exception on a thread other than the main one. I added sio.eio.disconnect() and that seems to remedy the problem.

@davidkingzyb
Copy link
Author

Maybe the old version has this problem. This bug should have been fixed at v3.0.1 #228, now sio.disconnect() contain eio.disconnect(), you don't need call it manually.

@saltaverde
Copy link

Hmm, I'm using v3.1.2 and was experiencing the BrokenPipeError when not manually calling sio.eio.disconnect()

@neuronflow
Copy link

neuronflow commented Jan 6, 2020

Hmm I am facing the exact same problem....things I tried so far:

self.sio.disconnect()
print("**************")
print("disco called")
self.sio.eio.disconnect()
print("sio.eio called")
sys.exit(0)
print("exit called")
return`

using

python-engineio==3.10.0

python-socketio==4.4.0

I want to close the connection and finish execution of the program once the server finishes a computation and sends the finish msg, or when the server says the client is outdated.

@miguelgrinberg
Copy link
Owner

@neuronflow please share a small, complete example that I can use to see your problem here.

@neuronflow
Copy link

neuronflow commented Jan 6, 2020

Hi @miguelgrinberg thanks for the prompt response. I struggle a bit to make a small example, so following the complete code. I try to walk you through it.
The code starts up its own docker backend running Flask SocketIO, which handles preprocessing of MRI brain scans. The preprocessor is instantiated and then called with the brats_preprocess method taking in the input and output folders and other processing stuff (e.g. which brain extraction tool to use as an argument). There is two scenarios when I want the program to terminate, which are marked with the #TODO comments.

  1. when the client is outdated
  2. when the processing has finished.

On a sidenote:

        # setup connection
        self.sio.sleep(5)  # wait 5 secs for docker to start
        self.connect_client()
        self.sio.wait()

Is there a more elegant way to do this? For some reason the whole thing stops working then I remove the wait command, I suspect this might also be the culprit while the program does not return.

Thanks for taking the time, please let me know if you require further information.

the code:

import socketio
from brats_toolkit.preprocessor_utils.docker_functions import start_docker, stop_docker, update_docker
import os
import tempfile
from pathlib import Path

from brats_toolkit.preprocessor_utils.prep_utils import tempFiler
import sys


class Preprocessor(object):
    def __init__(self, noDocker=False):
        # settings
        self.clientVersion = "0.0.1"
        self.confirmationRequired = True
        self.mode = "cpu"

        # init sio client
        self.sio = socketio.Client()

        # set docker usage
        self.noDocker = noDocker

        @self.sio.event
        def connect():
            print("connection established! sid:", self.sio.sid)
            # client identification
            self.sio.emit("clientidentification", {
                "brats_cli": self.clientVersion, "proc_mode": self.mode})

        @self.sio.event
        def connect_error():
            print("The connection failed!")

        @self.sio.event
        def disconnect():
            print('disconnected from server')

        @self.sio.on('message')
        def message(data):
            print('message', data)

        @self.sio.on('status')
        def on_status(data):
            print('status reveived: ', data)
            if data['message'] == "client ID json generation finished!":
                self.inspect_input()
            elif data['message'] == "input inspection finished!":
                if "data" in data:
                    print("input inspection found the following exams: ",
                          data['data'])
                    if self.confirmationRequired:
                        confirmation = input(
                            "press \"y\" to continue or \"n\" to scan the input folder again.").lower()
                    else:
                        confirmation = "y"

                    if confirmation == "n":
                        self.inspect_input()

                    if confirmation == "y":
                        self.process_start()

            elif data['message'] == "image processing successfully completed.":
                stop_docker()
                # TODO
                self.sio.disconnect()
                print("**************")
                print("disco called")
                self.sio.eio.disconnect()
                print("sio.eio called")
                sys.exit(0)
                print("exit called")
                return

        @self.sio.on('client_outdated')
        def outdated(data):
            print("Your client version", self.clientVersion, "is outdated. Please download version", data,
                  "from:")
            print("https://neuronflow.github.io/brats-preprocessor/")
            # TODO
            self.sio.disconnect()
            print("**************")
            print("disco called")
            self.sio.eio.disconnect()
            print("sio.eio called")
            sys.exit(0)
            print("exit called")
            return

        @self.sio.on('ipstatus')
        def on_ipstatus(data):
            print("image processing status reveived:")
            print(data['examid'], ": ", data['ipstatus'])

    def brats_preprocess(self, exam_import_folder=None, exam_export_folder=None, dicom_import_folder=None,
                         nifti_export_folder=None,
                         mode="cpu", confirm=True, skipUpdate=False):
        if confirm != True:
            self.confirmationRequired = False
        self.mode = mode

        if self.noDocker != True:
            stop_docker()
            if skipUpdate != True:
                update_docker()
            start_docker(exam_import_folder=exam_import_folder, exam_export_folder=exam_export_folder,
                         dicom_import_folder=dicom_import_folder, nifti_export_folder=nifti_export_folder, mode=self.mode)

        # setup connection
        self.sio.sleep(5)  # wait 5 secs for docker to start
        self.connect_client()
        self.sio.wait()

    def connect_client(self):
        self.sio.connect('http://localhost:5000')
        print('sid:', self.sio.sid)

    def inspect_input(self):
        print("sending input inspection request!")
        self.sio.emit("input_inspection", {'hurray': 'yes'})

    def process_start(self):
        print("sending processing request!")
        self.sio.emit("brats_processing", {'hurray': 'yes'})

@miguelgrinberg
Copy link
Owner

Unfortunately I cannot test this code. The only comment I have is that you are stopping your docker container before you disconnect, I think it should be the other way around.

But overall, I don't really see the benefit of using a client/server architecture or Socket.IO in this case, since you have one client and one server, and you run the server just to process one unit of work from the client and then stop it.

@neuronflow
Copy link

The only comment I have is that you are stopping your docker container before you disconnect, I think it should be the other way around.

Thank you so much, that was it! So apparently trying to end a connection that does not exist anymore like in my case never returns.

some background info about this project:
The server is primarily designed to handle user interaction with an electron based GUI, so doctors without programming knowledge can preprocess MRI scans. As computations sometimes take long minutes/hours I went for websockets so the server can tell client when the communication is finished.
The computations need to happen locally as patient data needs to be preprocessed / anonymised within the hospitals to comply with data privacy regulations.

I made the python socket-io client to integrate preprocessing capabilities of the server in pipelines and to automate testing of it...How would you implement the interaction between frontend and dockered backend if not with websockets? :)

@miguelgrinberg
Copy link
Owner

How would you implement the interaction between frontend and dockered backend if not with websockets? :)

Well, if you force me to use electron plus a docker backend then you aren't leaving me much choice. For a desktop application I would have implemented a desktop app, maybe all in Python using pyside or wxpython, neither electron nor docker would have been a first choice for me.

@neuronflow
Copy link

Ahh that was my initial plan to, but the software packages several gigabytes of binaries for brain preprocessing which only run on Linux and I found it very hard to package them reliably in a cross platform app. I also appreciate the complex form validation possibilities in web technology.

So overall the docker / electron route seemed to be the path of least resistance.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

4 participants