I am trying to make a server-client program using threads to handle each client, but the server will only accept one client at a time. If the first client disconnects, then the second client is accepted. Furthermore, each client can only send data once, then the program fails to send any more.
Prior to posting, I have looked at MANY other Stack Overflow posts, including the following:
how can I make a server communicate with more than 1 client at the same time?
python multithreaded server
My Python socket server can only receive one message from the client
But through looking at these posts I have found no solution.
Here is the server code:
import socket
from threading import *
def main():
s=socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind(('172.20.3.62', 5000))
s.listen(1)
clients = []
print("listening")
def clienthandler(c):
clients.append(c)
try:
while True:
data = c.recv(1024).decode("UTF-8")
if not data:
break
else:
print(data)
for client in clients:
client.send(data.encode("UTF-8"))
except:
clients.remove(c)
c.close()
while True:
c, addr = s.accept()
print("accepted a client")
Thread(target=clienthandler(c)).start()
if __name__ == '__main__':
main()
Here is the client code:
import socket
from threading import *
def main():
s=socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('172.20.3.62', 5000))
print("connected")
def send():
msg = input("ENTER:")
s.send(msg.encode("UTF-8"))
def receive():
while True:
data = s.recv(1024).decode("UTF-8")
if data:
print(data)
else:
break
Thread(target=send).start()
Thread(target=receive).start()
if __name__ == '__main__':
main()
Thanks to user Rawing. His/Her solution was: Thread(target=clienthandler(c)) -> Thread(target=clienthandler, args=(c,))
This allowed for more than one thread, and I was able to solve the only one message problem by putting the client send block in a while loop.
Related
I'm writing a simple console chat with server and client. When receiving a message from the first client server should send it to the second client and vice versa. But when first client sends a message to the server it returns back and doesn't reach the second client. Maybe there is a problem in receiving() function.
Here is my client.py:
import socket
from _thread import *
def recieving(clientSocket):
while True:
encodedMsg = clientSocket.recv(1024)
decodedMsg = encodedMsg.decode('utf-8')
print(decodedMsg)
def chat(clientSocket, name):
msg = input()
encoded_msg = f'[{name}] {msg}'.encode('utf-8')
clientSocket.send(encoded_msg)
def main():
serverAddress = (socket.gethostname(), 4444)
clientSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
clientSocket.connect(serverAddress)
name = input('Enter your name: ')
start_new_thread(recieving, (clientSocket,))
while True:
chat(clientSocket, name)
if __name__ == "__main__":
main()
And server.py:
import time
import socket
from _thread import *
def listen(clientSocket, addr):
while True:
encodedMsg = clientSocket.recv(1024)
decodedMsg = encodedMsg.decode('utf-8')
currTime = time.strftime("%Y-%m-%d-%H.%M.%S", time.localtime())
for client in clients:
if addr != client:
clientSocket.sendto(encodedMsg, client)
print(f'[{currTime}] {decodedMsg}')
def main():
serverAddress = (socket.gethostname(), 4444)
global clients
clients = []
serverSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
serverSocket.bind(serverAddress)
serverSocket.listen(2)
while True:
clientSocket, addr = serverSocket.accept()
if addr not in clients:
clients.append(addr)
print(f'{addr} joined chat')
start_new_thread(listen, (clientSocket, addr))
if __name__ == '__main__':
main()
sendto doesn't work as expected if its socket is connected. It just sends to the connected socket, not the specified address.
Therefore, listen needs to be able to access the open socket of each client in order to write to it.
Currently clients is a list of addresses, but you could change it to a dict of address to socket mappings:
def main():
global clients
clients = {}
Then when you get a new client connection, save address and socket:
clientSocket, addr = serverSocket.accept()
if addr not in clients:
clients[addr] = clientSocket
print(f'{addr} joined chat')
start_new_thread(listen, (clientSocket, addr))
Finally, in listen, write to each other client's socket, not the connected clientSocket for that listen thread:
for client in clients:
if addr != client:
print(f"sending message from {addr} to {client}")
clients[client].send(encodedMsg)
There's a number of other problems with your code.
Sockets are not thread safe. So there is a race condition if 2 clients happen to write the same thing at the same time; the writes could be interpolated and the messages munged up.
If a client disconnects, the server doesn't handle the disconnection well. If the server disconnects, the clients go into an infinite loop as well.
The idea is to create a server for sending and receiving files for backup, right now the server receives 1 msg from a client in python and another in C++, the problem is, the python client manages to send 1 string and then the server kinda looks, and I have to end the connection, that's for the python client, when I'm trying to send data from the c++ client i got nothing
I'm using Websockets, but my problem seems to be on the try: statement, honestly cant figure it out wheres my problem
sidenote: I'm using quit() to stop my program, but every time I used it I got way too many errors so I had to comment it
Here's my Server.py code
import asyncio
import websockets
import socket
import sqlite3
import sys
def get_ip(): # returns primary private IP only
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
try:
# doesn't even have to be reachable
s.connect(('10.255.255.255', 1))
IP = s.getsockname()[0]
except Exception:
IP = '127.0.0.1'
finally:
s.close()
return IP
async def handle_connectio(websocket, path): # recive and handle connection from client, would handle json or file data
while True:
try:
async for name in websocket:
#name = await websocket.recv()
print(f"<<< {name}")
#break
except websockets.exceptions.ConnectionClosed:
print (f"Coneecion terminada")
#quit()
break
else:
print (f"algo paso")
#quit()
break
print ("Iniciando el Server webSocket")
print ("Current Ip: " + get_ip())
servidor = websockets.serve(handle_connectio, get_ip(), 8000)
#loop = asyncio.get_event_loop()
#loop.run_until_complete(servidor)
asyncio.get_event_loop().run_until_complete(servidor)
asyncio.get_event_loop().run_forever()
#async def main(): # main function
# print ("Iniciando Server websocket")
# print("Current Ip: " + get_ip())
# async with websockets.serve(handle_connectio, get_ip(), 8000):
# await asyncio.Future()
#if __name__ == '__main__':
# asyncio.run(main())
edit: I did try to simplify my code and it manages to receive the msg and show when the connection is closed - still the main problem persists.
async def handle_connectio(websocket, path): # recive and handle connection from client, would handle json or file data
try:
while True:
#async for data in websocket:
data = await websocket.recv()
print(f"<<< {data}")
await asyncio.sleep(1)
except websockets.exceptions.ConnectionClosed:
print (f"Coneecion terminada")
edit2: heres my client code, if this donst work i would switch to sockets
import asyncio
import websockets
async def client():
direc = "ws://192.168.1.69:8000"
async with websockets.connect(direc) as web:
while True:
nombre = input("Introduce el mensaje >>> ")
await web.send(nombre)
asyncio.get_event_loop().run_until_complete(client())
From looking at and running the example code at https://websockets.readthedocs.io/en/stable/, it's apparent that your connection handler shouldn't loop forever (while True:) but exit after handling all the messages supplied by the websocket. It will be called again when another message arrives.
Edit:
The original server code works fine. The problem is that the client is using the input() function, which blocks asyncio from running, which prevents websocket protocol from running correctly and blocks messages from sending. A small delay after send (await asyncio.sleep(1)) works, although ideally the input() and the asyncio comm logic would be separated, to avoid an arbitrary delay.
Ok for some odd reason websockets wont work/behave properly so i had to switch to Sockets and now i can send the data back and forth, i would post my client and server code for anyone on the future.
Server.py
import socket
# socket.SOCK_STREAM -> TCP
# socket.SOCK_DGRAM -> UDP
def get_ip(): # returns primary private IP only
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
try:
# doesn't even have to be reachable
s.connect(('10.255.255.255', 1))
IP = s.getsockname()[0]
except Exception:
IP = '127.0.0.1'
finally:
s.close()
return IP
def servidor():
print (f"Iniciando el Servidor Sockets")
print (f"Current IP Addres: " + get_ip())
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.bind((get_ip(), 8000))
server.listen(1)
conn, address = server.accept() # Accept the Client connection
while True:
#1024 is the bandwidth bits
try:
msg = conn.recv(1024).decode() # Recive the msg and trasform it from Binary to String
print("<<< " + msg)
except:
print (f"coneccion terminada")
break
if __name__ == "__main__":
servidor()
Client.py
import socket
print ('Iniciando cliente')
conn_client = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
conn_client.connect( ('192.168.1.68', 8000))
while True:
try:
msg = (f">>> ")
conn_client.sendall(msg.encode())
except:
print (f"Connection Close")
break
#recibido = conn_client.recv(1024)
#print (recibido.decode())
conn_client.close()
So I am writing this program that will allow me to run commands from a different computer on the same network (my own version of ssh) in Python. I want the client program to run in the background of the target which I've already figured out the logistics to. What I would like to do is start the client program and never have to start it again but after I close the server program on the host computer, I get tons of errors. What I would like to do is after I close the host program, the client will continue to try to connect to the server program until I run it again. The code for my client program is here:
import socket
import os
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
def connect():
try:
s.connect(('localhost', 1234))
except:
connect()
while True:
connect()
while True:
try:
msg = s.recv(1024)
os.system(msg.decode("utf-8"))
except:
s.connect('localhost', 1234)
The code for my host program is here:
import socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind(('localhost', 1234))
s.listen(5)
def main():
while True:
clientsocket, address = s.accept()
while address != None:
message = input("Enter Message: ")
messageb = message.encode("utf-8")
clientsocket.send(messageb)
main()
Note: I will change the address from localhost when I put this on a different computer.
Never do the following to start over a function without understanding the consequences of recursion. A function calling itself is called recursion and uses stack space. This will crash if the stack hits the recursion limit.
def connect():
try:
s.connect(('localhost', 1234))
except:
connect()
Here's a solution.
client.py:
import socket
import time
while True:
s = socket.socket()
try:
print('Trying to connect...')
s.connect(('localhost',8000))
print('Connected.')
try:
while True:
data = s.recv(1024)
if not data: break # server closed connection if nothing received.
print(data)
finally:
s.close()
print('Disconnected.')
except ConnectionError: # Any type of connection error, e.g. refused, aborted, reset.
time.sleep(1)
server.py:
import socket
import time
s = socket.socket()
s.bind(('',8000))
s.listen()
while True:
c,a = s.accept()
print(f'Connected: {a}')
try:
while True:
c.send(b'message')
time.sleep(1)
except ConnectionError:
c.close()
print(f'Disconnected: {a}')
Note also that TCP is a streaming protocol. It has no concept of messages. Take the time.sleep(1) out and the messages will all run together. A proper implementation will have a protocol to extract complete messages from the byte stream such as fixed sized messages, size transmitted followed by message, delimiter bytes like newlines between messages, etc.
I'm new to socket programming in Python and I'm trying to write a chatroom application, but I have a problem which is it each client should press enter in order to receive messages from other clients.
#my client side code
import socket
import sys
client_sock = socket.socket()
port = int(sys.argv[1])
client_sock.connect(('127.0.0.1', port))
print("Connected to server. start sending messages")
while True:
sending_message = input('> ')
if sending_message:
client_sock.send(sending_message.encode())
receiving_message = client_sock.recv(1024)
if receiving_message:
print(receiving_message.decode())
input pauses your program. Thus, either you can't use input blindly, or you have to use threads. Using threads is easier than the alternative (using select to figure out what to do next). Have one thread for input and sending, one thread for receiving and printing.
Here's a trivial rewrite of your code:
import threading
import socket
import sys
client_sock = socket.socket()
port = int(sys.argv[1])
client_sock.connect(('127.0.0.1', port))
print("Connected to server. start sending messages")
def sender():
while True:
sending_message = input('> ')
if sending_message:
client_sock.send(sending_message.encode())
def receiver():
while True:
receiving_message = client_sock.recv(1024)
if receiving_message:
print(receiving_message.decode())
sender_thread = threading.Thread(target=sender)
receiver_thread = threading.Thread(target=receiver)
sender_thread.start()
receiver_thread.start()
sender_thread.join()
receiver_thread.join()
I am trying to make a group chat program, where an unlimited amount of clients may join the server using the same script, it'll work by the server receiving the clients message and sending it to all the connected clients including the sender. I have only managed to make it so that the sender only gets his own message back, but not what another client sends.
I was thinking of storing all the connected client IP's in a list, and sending it to each IP, but I do not know how to change the recpient of socket.send
Server code:
from threading import *
import socket
s = socket.socket()
host = socket.gethostbyname(socket.gethostname())
port = 1337
s.bind((host, port))
s.listen(5)
print("Server host is", host)
def getMainThread():
for thread in enumerate():
if thread.name == 'MainThread':
return thread
return None
class client(Thread):
def __init__(self, socket, address):
Thread.__init__(self)
self.socket = socket
self.address = address
self.start()
def run(self):
main = getMainThread()
while main and main.isAlive():
print(self.address, "has connected!")
message = self.socket.recv(8192).decode('utf-8')
self.socket.send(bytes(message, 'UTF-8'))
self.socket.close()
while True:
c, addr = s.accept()
client(c, addr)
clients = [] #list for all client IP's
clients.append(addr)
Also, is there a way so that the client can establish a connection with the server so it doesn't keep poping up on the server.py that client has connected each time it sends a message?
Client code:
import socket
import os
import sys
host = '25.154.84.23'
print("""
=======================================================
=Welcome to Coder77's local internet messaging service=
=======================================================
The current soon to be encrypted server is {0}
You can enter the command /help for a list of commands available
""".format(host))
#username = input("Enter username: ")
username = 'Smile'
print("Now connecting to {0}....".format(host))
def printhelp():
print("""
The following commands are in the current version of this program:
/clear to clear the screen
/username to change your username
/exit to exit
/help for a list of commands
""")
def main():
global username
global host
sock = socket.socket()
try:
sock.connect((host, 1337))
while True:
message2 = input("{0}: ".format(username))
message = ('{0}: {1}'.format(username,message2))
if '/quit' in message:
sys.exit()
if '/clear' in message:
os.system('cls')
if '/help' in message:
printhelp()
if '/username' in message:
username = input("What would you like as your new username? ")
sock.send(bytes(message, 'UTF-8'))
received = sock.recv(8192).decode('utf-8')
print(received)
except socket.error:
print("Host is unreachable")
if __name__ == '__main__':
main()
#
Corrected Server code:
import sys
print(sys.version)
from threading import *
import socket
s = socket.socket()
host = socket.gethostbyname(socket.gethostname())
port = 1337
s.bind((host, port))
s.listen(5)
print("Server host is", host)
def getMainThread():
for thread in enumerate(): # Imported from threading
if thread.name == 'MainThread':
return thread
return None
class Client(Thread):
def __init__(self, socket, address):
Thread.__init__(self)
self.socket = socket
self.address = address
self.start()
def run(self):
main = getMainThread()
print(self.address, "has connected!")
while main and main.isAlive():
message = self.socket.recv(8192).decode('utf-8')
print(message)
self.socket.send(bytes(message, 'UTF-8'))
for each_client in clients:
each_client.socket.send(bytes(message, 'UTF-8'))
while True:
c, addr = s.accept()
this_client = Client(c, addr)
clients = []
clients.append(this_client)
The new code, adapted by gravetii is causing a lot of format errors. What happens now, is the user gets back what he sent, he does not get back what other users send and the user gets back what his previous message was, its terribly confusing. Please run the code, and you'll see as it's very hard to explain.
Example
In your server code, you are doing only a self.socket.send(bytes(message, 'UTF-8')). How can you then expect the server to send the message to all the clients? To do that you would need to iterate through the list of clients and call the send() method on each of their sockets.
while True:
c, addr = s.accept()
client(c, addr)
clients = [] #list for all client IP's
clients.append(addr)
In this code, you are creating a client object but never adding it to the list, then what's the point of creating one?
I think what you want is this:
while True:
c, addr = s.accept()
this_client = client(c, addr)
clients = [] #list for all client IP's
clients.append(this_client)
Then, you can send the message to all the clients by modifying the relevant part of your server code:
def run(self):
main = getMainThread()
while main and main.isAlive():
print(self.address, "has connected!")
message = self.socket.recv(8192).decode('utf-8')
self.socket.send(bytes(message, 'UTF-8'))
for each_client in clients:
each_client.socket.send(bytes(message, 'UTF-8'))
Also, why are you closing the connection after sending just one message? I believe your intention is to send more than one message to the server, and in that case, you don't need to close the connection.
Also, it's a better idea to create a class with its name starting with an upper case letter. So you may want to use Client instead of client for the class name.
Now coming to the issue of the message popping up everytime a client says something in your server.py, look at the run() method for the client thread:
def run(self):
main = getMainThread()
while main and main.isAlive():
print(self.address, "has connected!")
message = self.socket.recv(8192).decode('utf-8')
self.socket.send(bytes(message, 'UTF-8'))
The thread starts executing as soon as you create the client object, and so the first time when it connects to the server, it is right in showing that message. But then it's incorrect to place the print(self.address, "has connected!") in the while loop. So everytime the client says something, the server sends it back to the client and then the loop runs again, thus displaying the message back again. You need to modify it like so:
def run(self):
print(self.address, "has connected!")
main = getMainThread()
while main and main.isAlive():
message = self.socket.recv(8192).decode('utf-8')
self.socket.send(bytes(message, 'UTF-8'))
Hope this helps!