Chat server - can't receive message while getting input from user - python

Okay so I understood what I did wrong. But I have another problem now, where using multithreading doesn't work as intended. In the code below, in the main fucntion, I start the message_listener thread, which should work in the background while I get user input. But when I send something from another client, the message doesn't get printed (and it should) in the other client's terminal. Not even after the receiver client send a message himself. I dont understand what I got wrong.
import socket
import sys
import threading
from datetime import datetime
import errno
import msvcrt
HEADER_LENGTH = 10
HOST = "127.0.0.1" # Standard loopback interface address (localhost)
PORT = 50000 # Port to listen on (non-privileged ports are > 1023)
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client.connect(("127.0.0.1", PORT))
client.setblocking(False)
username = input("Choose a username: ")
username_header = f"{len(username.encode()):<{HEADER_LENGTH}}".encode()
client.send(username_header + username.encode())
def client_input(u_name):
print(f"{u_name}>", end="")
msg = ""
done = False
while not done:
if msvcrt.kbhit():
pressed = msvcrt.getch()
if pressed == b"\r":
print("")
done = True
else:
print(pressed.decode(), end="")
msg += pressed.decode()
return msg
def message_handler():
while True:
try:
uname_header = client.recv(HEADER_LENGTH)
if not len(uname_header):
print('Connection closed by the server')
sys.exit()
# actual username
sender_username_length = int(uname_header.decode().strip())
sender_username = client.recv(sender_username_length).decode()
sender_message_header = client.recv(HEADER_LENGTH)
sender_message_length = int(sender_message_header.decode().strip())
sender_msg = client.recv(sender_message_length).decode()
now = datetime.now()
current_time = now.strftime("%H:%M:%S")
print(f"<{current_time}> {sender_username}: {sender_msg}")
except IOError as error:
if error.errno != errno.EAGAIN and error.errno != errno.EWOULDBLOCK:
print(f"There was some error reading data: {str(error)}")
sys.exit()
# no data was received (everything's fine)
return
except Exception as error:
print(f"There was some error reading data: {str(error)}")
sys.exit()
def main():
msg_listener = threading.Thread(target=message_handler)
msg_listener.start()
while True:
message = client_input(username)
# if message is not empty, send it.
if message:
# Encode message to bytes, prepare header and convert to bytes, like for username above, then send
message = message.encode()
message_header = f"{len(message):<{HEADER_LENGTH}}".encode()
client.send(message_header + message)
if __name__ == "__main__":
main()
It seems like the message_handler doesn't receive anything... Because if for example, I put a print statement after the uname_header = client.recv(HEADER_LENGTH) line , it doesn't get printed. So it means the thread isn't working in the background? i dont get it.
For some reason, after one iteration over the while True loop in the main function, the msg_listener thread stops. Why does it stop tho? it has a while True loop in it!

This is because the program waits for input from the user before receiving new messages. Your actions do not work in parallel.
I suggest making the following changes
import threading
def message_handler():
while True:
# open listening and print new msg
if __name__ == "__main__":
msg_listener = threading.Thread(target=message_handler)
msg_listener.start()
while True:
message = client_input(username)
# send msg
In this case, you will always listen to messages in the background. And in the main thread to process user input

Related

How to take inputs and outputs properly from the same console as a client (connected to a remote server)?

I am currently working on a program in Python that works as a client, and needs to connect remotely to a server using the TCP/IP protocol. After the server receives the client's username, the client can send messages to other clients by typing "#<username> <message>", and this input will be further processed and the message that will be sent to the server will be constructed as "SEND <username> <message>", and this will be actually recognized by the server. Then the server will send back an acknowledgement to the sending client, and the actual message to the destination client.
My approach is to use a main function named chat_run(), used for input and constructing the message that will be sent to the server, and in parallel to run a function named OutputRecvMsg() in a different thread that will receive messages from the server and output them in the console.
The problem is, I want the beginning of all the input lines to start with username >, and the messages received from the server to be output immediately on a new line, and the client to wait for a new input.
My current implementation problem seems to be in receiving messages (the OutputRecvMsg() function). After it outputs a message to the console, I need to press Enter to ask for input, because it remains stuck.
For me, there are two questions regarding the current problem, maybe two threads try to access the same resource (console), maybe I made a mistake regarding the construction of the received message (because I know that sock.recv(4096) is blocking and I tried to avoid a blocking state).
import socket
import time
import re
import threading as th
SERVER_REPLY_1 = 'HELLO'
SERVER_REPLY_2 = 'IN-USE'
AT_SYMBOL = '#'
host_port = ('remote_server_add', 5378)
def build_loggin_msg(msg):
return 'HELLO-FROM ' + msg + ' \n'
def chat_run(sock, currentUser):
while True:
rawInput = input(currentUser + '> ')
if rawInput == '!who':
sock.sendall('WHO\n'.encode())
elif rawInput == '!quit':
sock.close()
break
else:
splittedMsg = re.split(r'\s', rawInput, maxsplit = 1)
if len(splittedMsg) > 1 and splittedMsg[0].startswith(AT_SYMBOL):
userNameToSend = splittedMsg[0][1:]
message = 'SEND ' + userNameToSend + ' ' + splittedMsg[1] + ' \n'
sock.sendall(message.encode())
def OutputRecvMsg(sock, currentUser):
OutMsg =''
chunk = ''
while True:
try:
chunk = sock.recv(4096).decode()
if not chunk:
pass
else:
OutMsg += chunk
except BlockingIOError as e:
if OutMsg:
print(OutMsg)
OutMsg = ''
if __name__ == '__main__':
loggedIn = False
currentUser = None
_data = ''
while not loggedIn:
currentUser = input('Add a username please: ')
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect(host_port)
sock.sendall(build_loggin_msg(currentUser).encode())
data = sock.recv(4096).decode()
print(data)
if data.startswith(SERVER_REPLY_1):
loggedIn = True
else:
print('Close connection for retry another username')
sock.close()
time.sleep(1)
sock.setblocking(0)
th.Thread(target=OutputRecvMsg, args=(sock, currentUser)).start()
chat_run(sock, currentUser)
As an example:
Add a username please: Nickname
HELLO Nickname
Nickname> #Nickname hello man -> send to me
Nickname> DELIVERY Nickname hello man
SEND-OK -> here I have to press enter to get the next lines
Nickname>

How to terminate a thread from outside or use input in multiprocessing in python

I'm currently working on the client part in a client-server socket programming, in order
to collect user input and receiving incoming messages from server at the same time (doesn't want to be stuck at input and not receiving or vice versa), i decided to use thread at first it worked pretty good, i'm currently implementing a timeout function e.g. closing the entire client side without manually typing input and couldn't do so (the timeout timing is calculated in the server and server can send a message), so i changed my thread to multiprocessing it can be terminated but it shows a EOF Error when it reads an input.
Just wondering is there a way to run my input_command() function at the same time as receiving messages and being able to terminate both?
def input_command():
while login:
command = input()
command_word = command.split(' ')
if command == 'logout':
clientSocket.send(command.encode('utf-8'))
print('user has logged out')
break
elif command == 'whoelse':
clientSocket.send(command.encode('utf-8'))
else:
clientSocket.send('giberish'.encode('utf-8'))
clientSocket.close()
server_port = int(sys.argv[1])
serverName = 'localhost'
clientSocket = socket(AF_INET, SOCK_STREAM)
clientSocket.connect((serverName, server_port))
login = False
once = False
while True:
received_message_encrypted = clientSocket.recv(1024)
received_message = received_message_encrypted.decode('utf-8')
if received_message == 'bye':
clientSocket.close()
login = False
thread_input.terminate()
break
if login == True and once == False:
once = True
thread_input = multiprocessing.Process(target=input_command)
thread_input.start()
use .close instead of .terminate in process

Using multi threading in python to create a basic chatroom

I've been given a summer assignment to create a chat room on python using sockets, using a main socket as the server that connects to all the other "client" sockets, each time a client sends a message to the server, the server sends it to everyone else but the client that sent it. The catch is that I need to make it so you can write messages and receive them at the same time, which is something I have no idea how to do so I took my friend's advice and tried to do it using multi-threading.
This is what I have right now, it's supposed to get more complicated but this is the very basic part:
client
import socket
import thread
import time
def receive_messages(recieve_socket):
while True:
print recieve_socket.recv(1024)
def send_messages(send_socket):
while True:
data = raw_input()
send_socket.send(data)
def main():
my_socket = socket.socket()
my_socket.connect(('127.0.0.1', 8822))
thread.start_new_thread(send_messages, (my_socket, ))
thread.start_new_thread(receive_messages, (my_socket, ))
time.sleep(1) #this delay lets the threads kick in, otherwise the thread count is zero and it crashes
while thread._count() > 1:
pass
if __name__ == '__main__':
main()
server
import socket
import select
waiting_messages = []
users = []
def add_new_user(user_socket):
new_socket, address = user_socket.accept()
users.append(new_socket)
print "A new user has joined"
def remove_user(user_socket):
users.remove(user_socket)
print "A user has left"
def send_waiting_messages(wlist):
for message in waiting_messages:
receiving_socket, data = message
if receiving_socket in wlist:
receiving_socket.send(data)
waiting_messages.remove(message)
def spread_messages(message, sending_user):
receiving_list = users
receiving_list.remove(sending_user)
for user in receiving_list:
waiting_messages.append((user, message))
print "A user has sent a message"
def main():
server_socket = socket.socket()
server_socket.bind(('0.0.0.0', 8822))
server_socket.listen(5)
# users = []
# messages_to_send = []
while True:
rlist, wlist, xlist = select.select([server_socket] + users, users, [])
for current_socket in rlist:
if current_socket is server_socket:
add_new_user(server_socket)
else:
data = current_socket.recv(1024)
if data == "quit":
remove_user(current_socket)
else:
spread_messages(data, current_socket)
send_waiting_messages(wlist)
if __name__ == '__main__':
main()
The issue is when I try to run it, the first message works fine, but after the second message the server just sends a lot of blank messages and stops sending the messages I send it.
I'd really appreciate help in the matter.
Thanks for the help guys, in the end I asked a friend of mine and he told me to use threading instead of select on the server and it worked great! I'll post the code later if anyone's interested.

(Python) How can I receive messages in real-time without refreshing?

I have followed a tutorial from a YouTuber under the name of DrapsTV. This tutorial was made in Python 2.7 and makes a networked chat using UDP. I converted this to Python 3 and got everything to work. However, the way the threading is setup is that I have to send a message(or press enter, which is a blank message) to refresh and receive any messages from other clients. Here is the video incase you may need it: https://www.youtube.com/watch?v=PkfwX6RjRaI
And here is my server code:
from socket import *
import time
address = input("IP Address: ")
port = input("Port: ")
clients = []
serversock = socket(AF_INET, SOCK_DGRAM)
serversock.bind((address, int(port)))
serversock.setblocking(0)
quitting = False
print("Server is up and running so far.")
while not quitting:
try:
data, addr = serversock.recvfrom(1024)
if "Quit" in str(data):
quitting = True
if addr not in clients:
clients.append(addr)
print(time.ctime(time.time()) + str(addr) + ": :" + str(data.decode()))
for client in clients:
serversock.sendto(data, client)
except:
pass
serversock.close()
Here is my client code:
from socket import *
import threading
import time
tLock = threading.Lock()
shutdown = False
def receiving(name, sock):
while not shutdown:
try:
tLock.acquire()
while True:
data, addr = sock.recvfrom(1024)
print(str(data.decode()))
except:
pass
finally:
tLock.release()
address = input("IP Address: ")
port = 0
server = address, 6090
clientsock = socket(AF_INET, SOCK_DGRAM)
clientsock.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
clientsock.bind((address, int(port)))
clientsock.setblocking(0)
rT = threading.Thread(target=receiving, args=("RecvThread", clientsock))
rT.start()
nick = input("How about we get you a nickname: ")
message = input(nick + "> ").encode()
while message != "q":
if message != "":
clientsock.sendto(nick.encode() + "> ".encode() + message, server)
tLock.acquire()
message = input(nick + "> ").encode()
tLock.release()
time.sleep(0.2)
shutdown = True
rT.join()
clientsock.close()
#furas has kindly explained my issue for me: it is not my receiving methods that are flawed(such as my threading or functions), it is the input call that is preventing the client from not receiving anything. So, in order to fix this, I or anyone else having this issue needs to find a way to call for input when a certain button is pressed so that unless your typing, you can receive messages or data.
Thank you #furas! https://stackoverflow.com/users/1832058/furas

Have a function time out if a certain condition is not fulfilled in time

The issue I have is that my chat client is supposed to recieve and print data from server when the server sends it, and then allow the client to reply.
This works fine, except that the entire process stops when the client is prompted to reply. So messages pile up until you type something, and after you do that, then it prints all the recieved messages.
Not sure how to fix this, so I decided why not have the client's time to type a reply timeout after 5 seconds, so that the replies can come through regardless. It's pretty flawed, because the input will reset itself, but it works better anyways.
Here's the function that needs to have a timeout:
# now for outgoing data
def outgoing():
global out_buffer
while 1:
user_input=input("your message: ")+"\n"
if user_input:
out_buffer += [user_input.encode()]
# for i in wlist:
s.send(out_buffer[0])
out_buffer = []
How should I go about using a timeout? I was thinking of using time.sleep, but that just pauses the entire operation.
I tried looking for documentation. But I didn't find anything that would help me make the program count up to a set limit, then continue.
Any idea's about how to solve this? (Doesn't need to use a timeout, just needs to stop the message pileup before the clients reply can be sent) (Thanks to all who helped me get this far)
For Ionut Hulub:
from socket import *
import threading
import json
import select
import signal # for trying to create timeout
print("client")
HOST = input("connect to: ")
PORT = int(input("on port: "))
# create the socket
s = socket(AF_INET, SOCK_STREAM)
s.connect((HOST, PORT))
print("connected to:", HOST)
#--------- need 2 threads for handling incoming and outgoing messages--
# 1: create out_buffer:
out_buffer = []
# for incoming data
def incoming():
rlist,wlist,xlist = select.select([s], out_buffer, [])
while 1:
for i in rlist:
data = i.recv(1024)
if data:
print("\nreceived:", data.decode())
# now for outgoing data
def outgoing():
global out_buffer
while 1:
user_input=input("your message: ")+"\n"
if user_input:
out_buffer += [user_input.encode()]
# for i in wlist:
s.send(out_buffer[0])
out_buffer = []
thread_in = threading.Thread(target=incoming, args=())
thread_out = threading.Thread(target=outgoing, args=())
thread_in.start() # this causes the thread to run
thread_out.start()
thread_in.join() # this waits until the thread has completed
thread_out.join()
We can use signals for the same. I think the below example will be useful for you.
import signal
def timeout(signum, frame):
raise Exception
#this is an infinite loop, never ending under normal circumstances
def main():
print 'Starting Main ',
while 1:
print 'in main ',
#SIGALRM is only usable on a unix platform
signal.signal(signal.SIGALRM, timeout)
#change 5 to however many seconds you need
signal.alarm(5)
try:
main()
except:
print "whoops"

Categories