I am creating a simple chat room server using python and when I send data using I'm not receiving it. The code worked until I sepperated it using functions and classes. I did this so it would be simpler to add a UI
here's my server side code:
import socket
import select
from tkinter import *
import threading
HEADER_LENGTH = 10
IP = socket.gethostbyname('0.0.0.0')
PORT = 1234
class ServerNoGui():
def __init__(self):
self.server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
self.server_socket.bind((IP, PORT))
self.server_socket.listen()
self.sockets_list = [self.server_socket]
self.clients = {}
print("server started")
print("Starting thread sub-proccesses...")
acceptLoop = threading.Thread(target=self.acceptUsers)
acceptLoop.start()
recieveLoop = threading.Thread(target=self.manageDataSending)
recieveLoop.start()
print("sub-proccesses started!")
def recieve_message(self, client_socket):
message_header = client_socket.recv(HEADER_LENGTH)
if not len(message_header):
return False
message_length = int(float(message_header.decode('utf-8').strip()))
return {'header': message_header, 'data': client_socket.recv(message_length)}
def acceptUsers(self):
read_sockets, _x_, exception_sockets = select.select(self.sockets_list, [], self.sockets_list)
for notified_socket in read_sockets:
if notified_socket == self.server_socket:
client_socket, client_address = self.server_socket.accept()
print("accepted")
user = self.recieve_message(client_socket)
#print(user)
print("Recieved")
if(not user):
continue
self.sockets_list.append(client_socket)
print("added to list")
self.clients[client_socket] = user
print("created user")
print(f"Accepted connection from {client_address[0]}{client_address[1]} username: {user['data'].decode('utf-8')}")
def manageDataSending(self):
while True:
read_sockets, _x_, exception_sockets = select.select(self.sockets_list, [], self.sockets_list)
print("point")
for notified_socket in read_sockets:
if notified_socket == self.server_socket:
print("point 0")
self.acceptUsers()
else:
print("point 1")
message = self.recieve_message(notified_socket)
if(message is False):
print(f"Closed connection from {self.clients[notified_socket]['data'].decode('utf-8')}")
self.sockets_list.remove(notified_socket)
del self.clients[notified_socket]
continue
else:
user = self.clients[notified_socket]
type_, data = message['data'].decode("utf-8").split("$")
if(type_ == "message"):
print(f"Recieved Message from {user['data'].decode('utf-8')} : {message['data'].decode('utf-8')} of type {type_}")
for client_socket in self.clients:
if client_socket != notified_socket:
client_socket.send(user['header'] + user['data'] + message['header'] + message['data'])
for notified_socket in exception_sockets:
sockets_list.remove(notified_socket)
del clients[notified_socket]
print(f"Closed connection from {clients[notified_socket]['data'].decode('utf-8')}")
class serverGUI():
def __init__():
window = Tk()
window.title(f"Chatt.py HOSTING SERVER (IP : {IP} \\\\ HOST : {HOST})")
def createWidgets(self):
return False
def log(self, data):
return False
def loop(self):
window.mainloop()
serverBackend = ServerNoGui()
and here's the client
import socket
import select
import errno
import sys
HEADER_LENGTH = 10
IP = socket.gethostbyname("0.0.0.0")#'192.168.0.40'
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)
while True:
messageInput = input(f"Me({my_username}) > ")
message = f"message${messageInput}"
if(message):
message = message.encode("utf-8")
message_header = f"{len(message):<{HEADER_LENGTH}}".encode("utf-8")
client_socket.send(message_header + message)
print(f"sent {message_header} : {message}")
try:
while True:
username_header = client_socket.recv(HEADER_LENGTH)
if(not len(username_header)):
print("connection closed by server")
sys.exit()
username_lenght = int(username_header.decode("utf-8").strip())
username = client_socket.recv(username_lenght).decode("utf-8")
message_header = client_socket.recv(HEADER_LENGTH)
message_length = int(message_header.decode("utf-8").strip())
messageRaw = client_socket.recv(message_length).decode("utf-8")
type_, message = messageRaw.split("$")
if(type_ == message):
print(f"{username} >> {message}")
except IOError as e:
if(e.errno != errno.EAGAIN and e.errno != errno.EWOULDBLOCK):
print("READ ERR",str(e))
sys.exit()
continue
except Exception as e:
print("Error".str(e))
sys.exit()
I decided to try with two clients I get the following output Server:
server started
Starting thread sub-proccesses...
sub-proccesses started!
point
point 0
accepted
Recieved
added to list
created user
Accepted connection from 127.0.0.160338 username: test1
point
point 0
accepted
Recieved
added to list
created user
Accepted connection from 127.0.0.160340 username: test2
point
point 1
Recieved Message from test2 : message$hello of type message
point
point 1
Recieved Message from test1 : message$ of type message
point
point 1
Recieved Message from test1 : message$hello of type message
client1:
Username: test1
Me(test1) >
sent b'8 ' : b'message$'
Me(test1) > hello
sent b'13 ' : b'message$hello'
client2:
Username: test2
Me(test2) > hello
sent b'13 ' : b'message$hello'
as you can see the messages are sent and recieved by the server but not displayed (I'm not stupid I hit enter a few times).
Your server code is confusing and probably not doing what you thought. First you create a thread to accept connections giving it acceptUsers
as its thread function (call this thread A ). However, that function will only run once in that thread , then it will exit after it has visited all of the read_sockets
(because there's no while True
loop).
Second, your other thread ( B ) is running in manageDataSending
-- also executing select
, but when a client connects, it's calling acceptUsers
. That means on the first connection to your server, there's a "race" between the two threads. It's questionable what will happen next because both are now destined to execute the acceptUsers
code at more or less the same time, so the exact order of operations in the two threads is now indeterminate.
It's possible that thread A could run first, handle the server_socket
(accept the connection and do the receive) and quit, before thread B enters the select
call in acceptUsers
. That would leave thread B waiting in the select
(in acceptUsers
) until the next time a client connects or sends data.
On the other hand, it's possible that thread A and thread B both get past the select
in acceptUsers
and both execute the accept
call. Then the accept
will succeed in one, and block in the other, waiting for a subsequent incoming connection. In either case, your thread B eventually ends up blocked in a place in your code that you didn't expect it to be.
Bottom line: there's no obvious reason for you to have the two separate threads, both doing a select
call. It will be much simpler and easier to understand what is going on if create only one thread, and have it execute your socket select
in one place, have that place handle the socket notifications for both incoming connections and incoming client data.
One other thing to note: you don't really need to handle the exception_sockets
separately. If one of your clients goes away, its entry in the read_sockets
list will be signaled as "ready-to-read" and when you attempt to read it, you will get an end-of-file indication (ie zero-length buffer returned).
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.