What I am trying to do is basically simulate a client-server-client chat using threads. Here is the code so far:
from socket import *
from threading import Thread
import threading
import time
from random import randint
clients = []
HOST = 'localhost'
PORT = 8000
class Server():
def __init__(self):
self.addr = (HOST,PORT)
global clients
self.start()
for i in range(5): Thread(target=self.clientHandler).start()
self.s.close()
def clientHandler(self):
conn, addr = self.s.accept()
clients.append(addr)
print addr[1], "is Connected"
while 1:
time.sleep(5)
# message=repr(data)
# message=message[1:-1].split(':')
message='test' #for debug
conn.sendto(message, clients[1]) #for debug
# conn.sendto(message[1], clients[int(message[0])])
def start(self):
self.s = socket(AF_INET, SOCK_STREAM)
self.s.bind((HOST, PORT))
self.s.listen(5)
print "Server is running......"
class Client(threading.Thread):
global clients
def sendMessage(self):
if len(clients)>1:
to=randint(0, len(clients)-1)
message = str(to)+':hello from '+self.name
print message
self.ss.send(message)
def receiveMessage(self):
while 1:
reply=self.ss.recv(1024)
if reply != '':
print self.name+" received ", repr(reply)
break
def run(self):
self.ss = socket(AF_INET, SOCK_STREAM)
self.ss.connect((HOST, PORT)) # client-side, connects to a host
self.name=self.ss.getsockname()[1]
while True:
# time.sleep(randint(1,5))
# self.sendMessage()
self.receiveMessage()
server=Server()
client1=Client()
client2=Client()
client3=Client()
client1.start()
client2.start()
client3.start()
The idea is that one client should send a message to another random one (have not considered excluding itself yet) over and over again. To do so, the message has the format dest: message
, where dest
is a random index for choosing a client from the clients
list.
After hours of "debugging", I discovered that the sendto() method sends the message to all the clients, not just the one with the specified address (as the above code does). Am I using it wrong?
Also, when I make the clients send messages, they just receive the message right back. What am I doing wrong?
I know it is messy, and sorry about that. I just started working with Python and I am testing multiple methods to see how they work.
Thank you. - Python Noob
Your code works since the second argument to sendto
is ignored for TCP sockets.
For your idea to work, you must actually read something on the server. But first, let's look at the other obvious problems:
You are creating exactly 5 threads on the server. This is not harmful yet, but may be in the future. Instead, just create a thread when needed, as in
class Server(threading.Thread):
def __init__(self):
super(Server, self).__init__()
self.addr = (HOST,PORT)
self.start()
def run(self):
self.s = socket(AF_INET, SOCK_STREAM)
self.s.bind((HOST, PORT))
self.s.listen(5)
print "Server is running......"
while True:
conn, addr = self.s.accept()
threading.Thread(target=self.clientHandler, args=(conn, addr)).start()
Then, don't store the address, store the socket:
def clientHandler(self, conn, addr):
clients.append(conn)
Furthermore, in clientHandler
, read from the sockets:
def clientHandler(self, conn, addr):
clients.append(conn)
while True:
msg = read_from_socket(conn)
id_str, _, msg = msg.partition(b':')
clients[int(id_str.decode('ascii'))].sendall(msg)
read_from_socket
must be a function that reads your message format. Since TCP is packet-less (imagine it as one infinite stream, until the remote end closes), you must define a message format. Let's do that now, by waiting for the newline b'\n'
:
def read_from_socket(conn):
buf = bytearray(0)
while True:
b = conn.recv(1)
buf.extend(b)
if b == b'\n':
return bytes(buf)
Then we simply have to adapt the client to add a \n
byte:
class Client(threading.Thread):
def sendMessage(self):
if len(clients)>1:
to = randint(0, len(clients) - 1)
message = ('%s:hello from %s\n' % (to, self.name)).encode('utf-8')
print(message)
self.ss.send(message)
Finally, make sure to actually send a message (at the moment, you don't):
server=Server()
client1=Client()
client2=Client()
client3=Client()
client1.start()
client2.start()
client3.start()
time.sleep(1)
client1.sendMessage()