I have a server set up with sockets and threading, and when I connect multiple clients to it, if a client sends a message, the server repeats that same message back to it, instead of to all other connected clients. For example:
#server terminal
Server is connected on 8000
('127.0.0.1', 50328) is Connected
('127.0.0.1', 50329) is Connected
Received Message b'hi\n'
#Client 1 terminal
#input
[user1]hi
#returns:
[user1] b'hi\nhi\n'[user1]
#Client 2 terminal
#doesn't return anything, just sits at the prompt
[user2]
The relevant code for the server is:
def clientHandler():
c, addr = s.accept()
print(addr, "is Connected")
if addr not in clients:
clients.append(addr)
try:
while True:
data = c.recv(1024)
if not data:
break
print("Received Message ", repr(data))
for client in clients:
c.sendto(data, client)
except:
print("Error. Data not sent.")
I have read the following sources, but to no avail:
python tcp server sending data to multiple clients
https://docs.python.org/3/library/socket.html
What must I do to make it send user1's message to all other users through the server?
Edit 1:
All server.py code:
from socket import *
from threading import Thread
clients = []
def clientHandler():
c, addr = s.accept()
print(addr, "is Connected")
if addr not in clients:
clients.append(addr)
try:
while True:
data = c.recv(1024)
if not data:
break
for client in clients:
c.sendto(data, client)
except:
print("Error. Data not sent to all clients.")
HOST = '' #localhost
PORT = 8000
s = socket(AF_INET, SOCK_STREAM)
s.bind((HOST, PORT))
s.listen(5)
print("Server is running on "+ str(PORT))
#Thread(target=clientHandler).start()
#Thread(target=clientHandler).start()
#Thread(target=clientHandler).start()
for i in range(5):
Thread(target=clientHandler).start()
s.close()
I see a few issues in your code -
You are starting clientHandler threads, but then you are not making the main thread join any , this may cause main thread to die before the child threads finish processing, I think you would want to save the Thread objects you create to a variable and then make them join the main thread.
Instead of making the clientHandlers directly, you should first wait for accepting a connection from client (outside the handler function) and once you get the connection, add it to list of clients and send it over to the clientHandler.
In your code - for client in clients: c.sendto(data, client) m this sends data to all clients ,instead you should check if client is not the client that this thread is servicing, by checking against the addr that this thread is servicing.
Example changes -
from socket import *
from threading import Thread
clients = []
def clientHandler(c, addr):
global clients
print(addr, "is Connected")
try:
while True:
data = c.recv(1024)
if not data:
break
for client in clients:
if addr != client:
c.sendto(data, client)
except:
print("Error. Data not sent to all clients.")
HOST = '' #localhost
PORT = 8000
s = socket(AF_INET, SOCK_STREAM)
s.bind((HOST, PORT))
s.listen(5)
print("Server is running on "+ str(PORT))
#Thread(target=clientHandler).start()
#Thread(target=clientHandler).start()
#Thread(target=clientHandler).start()
trds = []
for i in range(5):
c, addr = s.accept()
clients.append(addr)
t = Thread(target=clientHandler, args = (c, addr))
trds.append(t)
t.start()
for t in trds:
t.join()
s.close()
Related
So can someone please tell me how to have an admin-client shutting down the Server (server.py) in a socket multiple clients architecture? I want admin-client to type "shutdown" in client side then server will be shutdown. and right after submit, the server will call a function that shows network load graph . a graph with the number of requests per time slot.
Server:
`
import socket, threading
class ClientThread(threading.Thread):
def __init__(self,clientAddress,clientsocket):
threading.Thread.__init__(self)
self.csocket = clientsocket
print ("New connection added: ", clientAddress)
def run(self):
print ("Connection from : ", clientAddress)
#self.csocket.send(bytes("Hi, This is from Server..",'utf-8'))
msg = ''
while True:
data = self.csocket.recv(2048)
msg = data.decode()
if msg=='bye':
break
print ("from client", msg)
self.csocket.send(bytes(msg,'UTF-8'))
print ("Client at ", clientAddress , " disconnected...")
LOCALHOST = "127.0.0.1"
PORT = 8080
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
server.bind((LOCALHOST, PORT))
print("Server started")
print("Waiting for client request..")
while True:
server.listen(1)
clientsock, clientAddress = server.accept()
newthread = ClientThread(clientAddress, clientsock)
newthread.start()
Client:
import socket
SERVER = "127.0.0.1"
PORT = 8080
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client.connect((SERVER, PORT))
client.sendall(bytes("This is from Client",'UTF-8'))
while True:
in_data = client.recv(1024)
print("From Server :" ,in_data.decode())
out_data = input()
client.sendall(bytes(out_data,'UTF-8'))
if out_data=='bye':
break
client.close()
`
I have tried
if message == "shutdown":
close()
exit(0)
but dont know how to apply it
Is there a way to stop the while loop until the clients connect to this server?
The server shuld create a new thread for each new client connection, it is possible?
import socket
import threading
def clientdialog(myS):
conn, addr = myS.accept()
print ("Connection from: " + str(addr))
while 1:
data = conn.recv(1024).decode("utf-8")
if not data or data == 'q':
break
print ("from connected user: " + str(data))
host = "192.168.1.3"
port = 1998
mySocket = socket.socket()
mySocket.bind((host,port))
while True:
mySocket.listen(10)
#whait until socket don't connect
try:
threading._start_new_thread(clientdialog, (mySocket))
except:
print("error starting thread")
The socket.listen function is to be called once, because it sets the size of the connection queue.
Another function called socket.accept will block until connections are made. Modify your code like this:
mySocket = socket.socket()
mySocket.bind((host,port))
mySocket.listen(10)
while True:
client_socket, client_address = mySocket.accept() # blocking call
.... # do something with the connection
For more information, visit the docs.
Additionally, you'd want to pass the details of the client socket to the thread. The server socket isn't required. In effect, something like this:
def handle_client(client_socket, client_address):
.... # do something with client socket
client_socket.close()
...
while True:
client_socket, client_address = mySocket.accept()
T = threading.Thread(target=handle_client, args=(client_socket, client_address))
T.start()
You accept the connection in the main loop, then pass the client details to the thread for processing.
I have been experimenting with network programing with python. To teach myself how to do this I have been playing with multiple versions of TCP chat servers and clients. My newest attempt has left me wondering what is wrong with the code I have just written. Only when a message has been sent from the client I have just created, does the other messages come in. I am not sure why this is happening. I have been searching online and I can't figure out why it is happening. All I do know is that it is not the servers mistake. I am certain that the problem is with the client I just created.
import socket, thread, threading, os
def sendMsg():
Message = raw_input('[-]You:')
s.send(Message)
def recvMsg():
data = s.recv(buff)
print(data)
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
host = raw_input('[-]Target Ip-> ')
port = 5000
buff = 1024
try:
s.connect((host, port))
except:
print('[-]Failed to Connect')
s.close()
loop = True
threads = []
while loop == True:
try:
t1 = threading.Thread(target=sendMsg())
threads.append(t1)
t1.start()
t2 = threading.Thread(target=recvMsg())
threads.append(t2)
t2.start()
except KeyboardInterrupt:
print('\n')
break
s.close()
os.system('clear')
Server code
# Tcp Chat server
import socket, select, os, time
def broadcast_data (sock, message):
for socket in CONNECTION_LIST:
if socket != server_socket and socket != sock :
try :
socket.send(message)
except :
socket.close()
CONNECTION_LIST.remove(socket)
if __name__ == "__main__":
CONNECTION_LIST = []
RECV_BUFFER = 4096
IP_MNG = ''
PORT = 5000
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
server_socket.bind((IP_MNG, PORT))
server_socket.listen(10)
CONNECTION_LIST.append(server_socket)
print "[-]Connected To " + str(PORT)
start = True
while start == True:
try:
read_sockets,write_sockets,error_sockets = select.select(CONNECTION_LIST,[],[])
for sock in read_sockets:
if sock == server_socket:
sockfd, addr = server_socket.accept()
CONNECTION_LIST.append(sockfd)
print "Client (%s, %s) connected" % addr
broadcast_data(sockfd, "[-][%s:%s] entered room\n" % addr)
else:
try:
data = sock.recv(RECV_BUFFER)
if data:
broadcast_data(sock, "\r" + '<' + str(sock.getpeername()) + '> ' + data)
except:
broadcast_data(sock, "Client (%s, %s) is offline" % addr)
print "Client (%s, %s) is offline" % addr
sock.close()
CONNECTION_LIST.remove(sock)
continue
except KeyboardInterrupt:
print('[-]Server Stopped')
start = False
server_socket.close()
time.sleep(1)
os.system('clear')
I am pretty new to socket programming, so this may be a shot in the darK:
The client is generating a lot of threads reading and writing from a blocking socket. It looks like only a single operation can be performed at one time, a thread is blocking on a write or a thread is blocking on a read, since there is only one socket, all threads are operating on that single resource. You could have each thread you spawn open its own own socket connection write to it the read from it, which should address the issue.
For the client, Are socket reads/writes thread safe?? Yes
On the server should the accepted connection be marked as non-blocking??
sockfd, addr = server_socket.accept()
sockfd.setblocking(0)
On the server, do you need to manage when sockets are writable? It looks like sockets are written to as they become readable. I'd imagine that socket.send blocks during the readcall, so new connections aren't being handled during this time. Python module of the week has awesome clear example of using select for non blocking servers
I'm struggling with the .sendall() method for a client/server -setup. Several clients can connect individually and send messages to the server and get the echoed response. However, I expect the messages sent to show up for all the connected clients in a chat-like Environment.
This is the code running on the server application.
def clientHandler(conn, addr):
name = addr[0]
print(addr, " connected to the server")
while True:
data = conn.recv(1024)
if tostr(data)[0:5] == '/name':
print('setting name')
setname(data)
if not data:
break
conn.sendall(str.encode(checkname(addr[0]) + tostr(data)))
print('Received Message: ', tostr(data))
HOST = '' #LOCALHOST
PORT = 1400
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.bind((HOST, PORT))
sock.listen(999999)
print('Server is running' + '-'*10)
for i in range(999999):
conn, addr = sock.accept()
Thread(target = clientHandler, args = (conn, addr)).start()
sock.close()
Thank you in advance
As I had originally said in a comment (but now decided is better said in an answer), sendall sends all bytes to a single connection. You need to add something like:
lock = threading.Lock()
all_clients = []
at the top; in the main loop, where you now have just conn, addr = sock.accept(), have
conn, addr = sock.accept()
with lock:
all_clients.append(conn)
In clientHandler, where you now have just conn.sendall(str.encode(checkname(addr[0]) + tostr(data))), have instead
with lock:
for c in all_clients:
c.sendall(str.encode(checkname(addr[0]) + tostr(data)))
IOW, you need to explicitly code the "broadcast" business logic in your server -- it's absolutely not implicit in sendall (whose semantics you may have misunderstood).
I am having a multi-client server which listens to multiple clients. Now if to one server 5 clients are connected and I want to close the connection between the server and just one client then how am I going to do that.
My server code is:
import socket
import sys
from thread import *
try:
s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
except socket.error,msg:
print "Socket Creation Error"
sys.exit();
print 'Socket Created'
host = ''
port = 65532
try:
s.bind((host, port))
except socket.error,msg:
print "Bind Failed";
sys.exit()
print "Socket bind complete"
s.listen(10)
print "Socket now listening"
def clientthread(conn):
i=0
while True:
data = conn.recv(1024)
reply = 'OK...' + data
conn.send(reply)
print data
while True:
conn, addr = s.accept()
start_new_thread(clientthread,(conn,))
conn.close()
s.close()