1

I'm trying to send data to a client, as part of a development process for a chat server in Python. Currently the client has a GUI, however the server is run from command line. I've included the full code to them both below.

Client

from tkinter import *
import socket

host = socket.gethostname()
port = 8000

s=socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

class Application(Frame):
    def __init__(self, master=None):
        #Create master frame
        Frame.__init__(self,master)
        self.grid()
        self.master.title("Test 1")
        self.conn=False #State of connection to server


        #Configure main frame
        for r in range (4):
            self.master.rowconfigure(r, weight=1)
        for c in range (2):
            self.master.columnconfigure(c)

        #Create sub frames
        TopFrame=Frame(master, bg="red")
        TopFrame.grid(row=0, column=0, rowspan=3)
        BottomFrame=Frame(master, bg="blue")
        BottomFrame.grid(row=4, column=0)
        SideFrame=Frame(master, bg="green")
        SideFrame.grid(column=1, row=0, rowspan=4)

        #Create Chat log
        self.chatlog=Text(TopFrame)
        self.chatlog.pack(padx=5, pady=5)


    #Create entry field
        self.e1=StringVar()
        self.e1=Entry(BottomFrame)
        self.e1.pack(pady=5, padx=5)

        #Create buttons
        b1=Button(SideFrame, text="Connect", command=self.connect)
        b1.grid(row=0, column=0, padx=5, pady=5)
        b2=Button(SideFrame, text="Disconnect", command=self.disconnect)
        b2.grid(row=1, column=0, padx=5, pady=5)
        b3=Button(SideFrame, text="Send", command=self.sendmessage)
        b3.grid(row=2, column=0, padx=5, pady=5)

    def connect(self): #Connect to server
        self.chatlog['state'] = NORMAL
        self.chatlog.insert(END, ("===ATTEMPTING TO CONNECT TO SERVER\n"))
        self.chatlog['state'] = DISABLED
        self.chatlog.yview(END)
        try:
            s.connect((host,port))
            self.chatlog['state'] = NORMAL
            self.chatlog.insert(END, ("===CONNECTED TO SERVER\n"))

            self.chatlog['state'] = DISABLED
            self.chatlog.yview(END)
            self.conn=True
            print("Connected") #Connection successful

            #Receive messages
            self.receive_msg()

        except ConnectionRefusedError: #Can't find server
            self.chatlog['state'] = NORMAL
            self.chatlog.insert(END, ("===SERVER COULD NOT BE FOUND\n" + "===PLEASE MAKE SURE THE SERVER IS ON, AND YOU'RE CONNECTED TO THE NETWORK\n"))
            self.chatlog['state'] = DISABLED
            self.chatlog.yview(END)
        except: #Other errors
            self.chatlog['state'] = NORMAL
            self.chatlog.insert(END, ("===THERE'S AN ERROR WITH THE PROGRAM\n" + "===PLEASE TURN IT OFF AND ON AGAIN\n"))
            self.chatlog['state'] = DISABLED
            self.chatlog.yview(END)

        # When attempting to connect a second time, produces OS error: an operation was attempted on something that is not a socket

    def disconnect(self):
        if self.conn: #Tests to see if client is connected
            s.close()
            self.chatlog['state'] = NORMAL
            self.chatlog.insert(END, ("===DISCONNECTED FROM SERVER.\n"))
            self.chatlog['state'] = DISABLED
            self.chatlog.yview(END)
            self.conn=False
        else: #Prevents attempting to disconnect when already disconnected
            self.chatlog['state'] = NORMAL
            self.chatlog.insert(END, ("===YOU AREN'T CURRENTLY CONNECTED.\n"))
            self.chatlog['state'] = DISABLED
            self.chatlog.yview(END)


    def sendmessage(self):
        if self.conn: #Prevents sending if not connected
            self.msg=self.e1.get()
            if self.msg == "": #Empty message catcher
                self.chatlog['state'] = NORMAL
                self.chatlog.insert(END, ("===YOU CANNOT SEND AN EMPTY MESSAGE.\n" ))
                self.chatlog['state'] = DISABLED
                self.chatlog.yview(END)
            else:
                self.chatlog['state'] = NORMAL
                self.chatlog.insert(END, ('You: ' + self.msg + '\n'))
                self.chatlog['state'] = DISABLED
                self.chatlog.yview(END)
                self.send_data(self.msg) #Sends message
        else:
                self.chatlog['state'] = NORMAL
                self.chatlog.insert(END, ("===YOU ARE NOT CONNECTED TO A SERVER. YOU CANNOT SEND A MESSAGE.\n" ))
                self.chatlog['state'] = DISABLED
                self.chatlog.yview(END)

    def receive_msg(self):
        #Prepared to receive message
        print("Preparing to receive")
        while 1:
            data=s.recv(1024)
            decoded_data=data.decode('UTF-8')
            print(decoded_data) #Only printing to cmd line for now, to resolve errors
            #self.mainloop() ##Only fix I've found. This loop seems to crash the mainloop used to update GUI

    def send_data(self, message):
        try:
            s.send(message.encode('UTF-8'))
        except:
                self.chatlog['state'] = NORMAL
                self.chatlog.insert(END, ("===THE PREVIOUS MESSAGE DIDN'T SEND. THIS IS POSSIBLY DUE TO A SERVER ERROR.\n"))
                self.chatlog['state'] = DISABLED
                self.chatlog.yview(END)


root = Tk()
app = Application(root)
app.mainloop()

SERVER

import socket

host=socket.gethostname()
port=(8000)
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

def connect():
    s.bind((host,port))
    s.listen(2)
    print("Server listening")
    conn,addr=s.accept()
    print("Connected")
    send(conn)

# def receive(conn):
#   while 1:
#       try:
#       data=conn.recv(1024)
#       decoded_data=data.decode('UTF-8')
#       if not decoded_data:
#           print("No data")
#       else:
#           print("New data")
#           print(decoded_data)

def send(conn):
    while 1:
        data=input("Input data to send: ")
        encoded_data=data.encode('UTF-8')
        conn.send(encoded_data)

The issue I'm having is that the receive_msg() loop for always being ready to take in new messages stops the mainloop keeping the GUI updatated. I've commented that line about recalling app.mainloop(), however that means I only receive one message before the mainloop recalls infinitely inside the receive_msg loop. Can anyone help me with a fix?

5
  • you need to thread the socket server, then use a global variable to share the recieved message. Since your socket server will be the only thing writing to the global variable and the gui will be reading, you should have no problems as far as resource locking goes. Commented Feb 19, 2014 at 23:19
  • Thanks. Could you possibly post some pseudocode or briefly code it as I'm new to coding in general, and have no idea how to use threading. Commented Feb 19, 2014 at 23:23
  • There are actually two ways to do this. You could interleave a select on your socket(s) into the Tkinter main loop (although it may be easier to go the other way, and use a networking library like Twisted that can interleave the Tkinter main loop into its own), or you could use threads. Commented Feb 20, 2014 at 0:15
  • Anyway, StackOverflow is not a good place to ask people to write tutorials. You may want to search for threading tutorials, or try a mailing list like python-tutor or some other kind of site that's a better fit. Commented Feb 20, 2014 at 0:15
  • This blog post explains the ideas in a library-neutral way, with some Tkinter-specific stuff, but you'll have to read and understand the whole thing and figure out how to apply it to your code—which will probably lead you to some more specific questions that are perfect for SO. Commented Feb 20, 2014 at 0:17

1 Answer 1

1

You can use multiprocessing.connection Listener and Client.

Like this.

Server

from multiprocessing.connection import Listener


listener = Listener(("", 25000), authkey=b"selam")
client = listener.accept()
while 1:
    try:
        print(client.recv_bytes())
    except EOFError:
        print("Connection closed")
        break

Client

 from multiprocessing.connection import Client

 remote = Client(("", 25000), authkey=b"selam")
 ...
 ...
 class Application(Frame):
 ...
 ...

     def sendmessage(self):
         remote.send_bytes(self.e1.get().encode('ascii'))
....
Sign up to request clarification or add additional context in comments.

1 Comment

Link is broken.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.