Paul Sigonoso Paul Sigonoso - 3 months ago 23
Python Question

Creating a chat (client) program. How can I add simultaneous conversation?

# -*- coding: utf-8 -*-
#!/usr/bin/python3
import socket
# nao tem servidor UDP no google -> vamos usar netcat como servidor UDP!
#Programa de chat: so fala um de cada vez
#implementar falando ao mesmo tempo

client = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)


"""
pacotes_recebidos = client.recvfrom(1024) devolve uma tupla:
(' llallalaaaa\n', ('192.168.1.4', 667))

msg recebida + (IP,porta)
"""
try:

while 1: #while True
client.sendto(input("Voce: ") +"\n", ("192.168.1.4", 668)) # endere├žo do servidor UDP do kali linux usando netcat
msg, friend = client.recvfrom(1024)
print(str(friend) + ": " + msg)
#se quiser apenas o ip: use friend[0]
# convertemos str(friend) porque recebemos o erro:
# (TypeError(can only concatenate tuple (not str) to tuple,))

client.close()


except Exception as erro:
print("Conexao falhou ")
print("O erro foi: ", erro)
client.close()


In python 3.5 (Linux), when I sent "hi" this code shows error:

('O erro foi: ', NameError("name 'hi' is not defined",))


The code run on python 2.7.

I would like to make two people can talk simultaneously , how do? At the moment , only one person at a time can enter the message we have to wait for one of the participants write and hit to continue Could someone help me?
I use netcat as server.

Answer

Here is an example of a 'multithreaded' UDP python client in python3 that allows messages to be sent and received simultaneously.

I personally like to make a Wrapper for the socket class that has all the threading and functions built in, so we'll start with the wrapper.

import socket
import threading

class socketwrapper:
    def __init__(self, host, port):
        self.server = (host, port)
        self.client = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        self.client.settimeout(5) # Time is in seconds
        self.connected = False

    def startrecvdata(self):
        recvThread = threading.Thread(target=self.recvdata)
        recvThread.daemon = True
        recvThread.start()
        self.connected = True

    def recvdata(self):
        while self.connected:
            try:
                data, friend = self.client.recvfrom(1024)
                if data:
                    print(str(friend) + ": " + data.decode('utf-8'))
            except socket.timeout:
                else:
                    print("No Message Received before timeout.")
                    continue
            except:
                self.connected = False
        self.stop()

    def sendmessage(self, data):
        data += "\n"
        self.client.sendto(data.encode('utf-8'), self.server)

    def stop(self):
        self.client.shutdown(socket.SHUT_RDWR)
        self.client.close()

So as you can see we have a couple functions in this, the two you should notice are startrecvdata(self) and recvdata(self). We will call the startrecvdata(self) from our main function, which will start the recvdata(self) thread. That function will print any received data to the console.

Additionally, notice that we have settimeout(5) in the __init__ function of the wrapper, which sets a 5 second timeout on the socket connection. This way, we can close the entire program cleanly, shutting down and closing the socket with the stop() function.

Now for the main loop. Since we setup all of our functions in the wrapper class, we can have a super simple and clean loop:

def main():
    server = socketwrapper('192.168.1.1', 30000)
    server.startrecvdata()

    while not server.connected:
        continue

    print("Connected to server! Type 'exit' to quit.")

    while server.connected:
        message = input("Voce: ")
        if message == "exit":
            server.connected = False
            break

        server.sendmessage(message)

    server.stop()

In this loop, we create a an instance of our socketwrapper which initializes everything for us. Then we call server.startrecvdata() which, as we said above, starts the function to receive and print data from the UDP connection. The while not server.connected blocks the program until the thread has started.

Lastly, we have our while server.connected loop, which waits for user input in the console. We check if the user wants to exit, and if they do, we set server.connected = False and break our while loop.
If the user does not want to exit, we send the message to the UDP server.

After the loop ends, we call server.stop to ensure that the socket is closed before exiting the application.