0

I am starting out with learning to build server client apps and found a nice example My ultimate goal is to transfer files from server to clients and plot the received data on the app.

I have built up on an example app here. Could someone suggest how to call the close socket function when exiting from the app?

I am assuming one needs to close the socket only on program exit and not after each file transfer. Hence I put the code to close the socket right at the end.

I thought of using on_stop in the ClientAPP class. But since the variables haven't been created yet, referencing the function from there doesn't seem possible.

I am following the suggestions from one of the answers here :

server.py

# -*- coding: utf-8 -*-

import socket
from threading import Thread

class Server(object):
    def __init__(self):
        #Creates socket object
        self.serversocket = socket.socket()

        self.host = 'localhost'
        self.port = 54545

    #If we had used s.bind(('localhost', 80)) or s.bind(('127.0.0.1', 80)) we would still have a �server� socket, but one that was only visible within the same machine. s.bind(('', 80)) specifies that the socket is reachable by any address the machine happens to have.

    def start_server(self):
        self.serversocket.bind(('', self.port))

        self.serversocket.listen(10)

        self.clientsocket, self.addr = self.serversocket.accept()
        print("got a connection from %s" % str(self.addr))

        confirmation = 'Connected'
        self.clientsocket.send(confirmation.encode())

        #start_listening
        Thread(target=self.print_received_client_data).start()

    def print_received_client_data(self):
        while True:
            print('Received from client: %s' % self.clientsocket.recv(1024).decode())        

if __name__=="__main__":
    Server().start_server()

ClientAPP.py

# -*- coding: utf-8 -*-

from kivy.app import App
from kivy.uix.label import Label
from threading import Thread
from kivy.uix.boxlayout import BoxLayout
from kivy.properties import ObjectProperty
import socket, time
from kivy.uix.screenmanager import Screen, ScreenManager

class ScreenManagement(ScreenManager):
    pass

class HomeScreen(Screen):
    def __init__(self, **kwargs):
        super(HomeScreen,self).__init__(**kwargs)

        self.sock_instance = MySocket()
        Thread(target=self.set_text_input).start()

    host = "localhost"
    port = 7000
    display = ObjectProperty()

    def set_text_input(self):
        while True:
            time.sleep(0.1)
            self.text = self.sock_instance.get_data()                  

    def send_message(self):  
        #self.ids.display.text is encoded and sent            
        self.sock_instance.sock.send(self.ids.display.text.encode()) 

    def receive_message(self):
        # Decodes a reply, Converts it to a str, prints it           
        print("Message from server - %s" % str(self.sock_instance.sock.recv(1024).decode("ascii")))

class SettingsScreen(Screen):
    pass

class MySocket:

    def __init__(self, host="localhost", port=54545):

        self.sock = socket.socket()
        self.sock.connect((host, port))

        #Receives confirmation from Server, Converts to string, Prints confirmation
        print("%s" % str(self.sock.recv(1024).decode()))         

    def get_data(self):
        return self.sock.recv(1024)

    def close_socket(self):
        self.sock.close()

class ClientAPP(App):

    def build(self):
        runtime_instance = ScreenManagement()
        return runtime_instance

if __name__ == '__main__':
    app_instance = ClientAPP()
    try:
        app_instance.run()
    finally:
        #Code to close socket here, ideally, call close_socket
        print("Closed the socket")

ClientAPP.kv

#: import FadeTransition kivy.uix.screenmanager.FadeTransition

<ScreenManagement>:
    transition:FadeTransition()
    HomeScreen:
    SettingsScreen:

<HomeScreen>:
    RelativeLayout:
        Button:
            size_hint: 0.2,0.2
            pos_hint:{"x":0,"y":0.4}
            text: "Send Message"
            on_press: root.send_message()
        TextInput:
            id: display
            size_hint: 0.2,0.2
            pos_hint:{"x":0.2,"y":0.4}
            text: "abc"

Edit - Incorrect server.py posted earlier

buzzBee
  • 19
  • 7
  • By the time that you get to the finally statement your server thread and associated objects will be dead so you won't have access to the socket. You have to do it before the background thread terminates. – shrewmouse Jun 18 '18 at 16:35

1 Answers1

0

Solution

Close Socket - when window is closed i.e. click on X

  1. Add import statement, from kivy.core.window import Window
  2. Add Window.bind(on_request_close=self.close_socket) into __init__() method of class MySocket:.

Close Socket - when exit app

  1. In kv files, add id: home_screen
  2. In Python files, add on_stop() event and invoke close_socket() method, into class ServerApp() and ClientApp().

kv files - add id

server.kv & client.kv

<ScreenManagement>:
    HomeScreen:
        id: home_screen
        name: 'home_screen'
    SettingsScreen:
        name: 'settings_screen'

Python files

server.py

class MySocket:

    def __init__(self, host="localhost", port=54545):
        Window.bind(on_request_close=self.close_socket)

    ...

    def close_socket(self, *largs, **kwargs):
        print("\nServerApp.close_socket:")
        print("\tClosed the socket")
        self.sock.close()


class ServerApp(App):

    def build(self):
        self.runtime_instance = ScreenManagement()
        return self.runtime_instance

    def on_stop(self):
        print('\nServerApp.on_stop:')
        self.runtime_instance.ids.home_screen.sock_instance.close_socket()


if __name__ == '__main__':
    ServerApp().run()

main.py

class MySocket:

    def __init__(self, host="localhost", port=54545):
        Window.bind(on_request_close=self.close_socket)

    ...

    def close_socket(self, *largs, **kwargs):
        print("\nClientApp.close_socket:")
        print("\tClosed the socket")
        self.sock.close()

class ClientApp(App):

    def build(self):
        self.runtime_instance = ScreenManagement()
        return self.runtime_instance

    def on_stop(self):
        print('\nClientApp.on_stop:')
        self.runtime_instance.ids.home_screen.sock_instance.close_socket()


if __name__ == '__main__':
    ClientApp().run()

Output

ServerApp sends data

Img01 - ServerApp send data

ClientApp received data

Img02 - ClientApp received data

ServerApp exit

Img02 - ServerApp exit

ikolim
  • 15,721
  • 2
  • 19
  • 29
  • Hey, thanks for the nice answer :) Assign an ID and call thus call the function. I posted the wrong server.py by mistake. I implemented your suggestions to the ClientAPP.py. Using window worked. The socket is being closed on shutdown now. Add "from kivy.core.window import Window" as well. For anyone else reading the post. Im having a new problem now. When I close the client app, and try to open it again, the ClientAPP hangs. Presumably it cannot connect to the server again. Any idea what might be wrong? – buzzBee Jun 19 '18 at 12:37
  • Put the statement, waiting for connection i.e. `self.clientsocket, self.addr = self.serversocket.accept()` in a loop before receiving / sending data (nested loops). – ikolim Jun 19 '18 at 14:43
  • Works like a charm :) Thank you – buzzBee Jun 20 '18 at 17:50