1

So I wanted to make a local communicator in Python. I found this guys` tutorial for the server and the client and followed it. But it had one major fraud. You didn't receive other messages until you sent one too. So I modified the client-side code to use multithreading to be able to receive messages without needing to send one.

So now the client side code looks like this:

import socket
import errno
import sys
import threading

HEADER_LENGTH = 10

IP = "127.0.0.1"
PORT = 1234
my_username = input("Username: ")

client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client_socket.connect((IP, PORT))
client_socket.setblocking(False)
username = my_username.encode("utf-8")
username_header = f"{len(username):<{HEADER_LENGTH}}".encode("utf-8")
client_socket.send(username_header + username)


def send():
    while True:
        message = input("> ")
        if message:
            message = message.encode("utf-8")
            message_header = f"{len(message):<{HEADER_LENGTH}}".encode("utf-8")
            client_socket.send(message_header + message)


def recieve():
    while True:
        try:
            while True:
                username_header = client_socket.recv(HEADER_LENGTH) # noqa
                if not len(username_header):
                    print("Connection closed by the server")
                    sys.exit()
                username_length = int(username_header.decode("utf-8").strip())
                username = client_socket.recv(username_length).decode("utf-8") # noqa
                message_header = client_socket.recv(HEADER_LENGTH)
                message_length = int(message_header.decode("utf-8").strip())
                message = client_socket.recv(message_length).decode("utf-8")
                print(f"{username} > {message}")
        except IOError as e:
            if e.errno != errno.EAGAIN and e.errno != errno.EWOULDBLOCK:
                print("Reading error: {}".format(str(e)))
                sys.exit()
            continue
        except Exception as e:
            print("Reading error: ".format(str(e)))
            sys.exit()


def start():
    sendth = threading.Thread(target=send)
    recieveth = threading.Thread(target=recieve)
    sendth.start(); recieveth.start()


start()

But then comes another problem. When the user types the message, and during the typing receives a message, the message gets put in the input line, and it just becomes a giant mess.

So is it possible that the receive function/thread outputs the messages in a new window?

I am also open to other solutions to this problem.

marzeq
  • 21
  • 1
  • 3
  • Add a `time.sleep(1)` just before `message = input("> ")` so the receiver becomes a timeslice to finish it's output. – stovfl Jul 16 '20 at 17:28

1 Answers1

0

As you have mentioned for other solutions to solve your problem, I will provide an alternative. The issue with what you are trying to do is that threads are accessing variables within the same module without locking (not that locking would have solved the problem anyways as you still see overlapping prints in the same file). Not to mention that threads are altogether messy, adn do not provide a large benefit in Python (apart from some applications for IO). One helpful post you can read about that is here.

One way (among other ways) you can approach this problem is that you seperate your sends and receives into two separate programs/scripts that you can run at the same time. The idea here is to have a shared resource, which both programs while running can exchange information with. You use a Python data structure (eg. dict) to store information from one script -> serialize it into a file (shared resource) -> deserialize it with another script at the same time.

I quote here Drewfer's answer on How to share variables across scripts in python?.

In the first script you can create the data structure you want and ush into the shared object.

import pickle

shared = {"Foo":"Bar", "Parrot":"Dead"}
fp = open("shared.pkl","w")
pickle.dump(shared, fp)

In the other script you can read the pickled resource via something like:

import pickle

fp = open("shared.pkl")
shared = pickle.load(fp)
print shared["Foo"]

For a more elaborate answer, check sdaau's answer here.

alt-f4
  • 2,112
  • 17
  • 49