We're trying to establish a TCP connection in Python 3.8, where we want each machine to become a client and a server at the same time, so that each machine can send and receive messages. With this code, we can send and receive data (sometimes), but we have noticed that each machine can send messages to the first ip address in the IPs list, but the rest are neglected.
Perhaps there's a better way than this to establish a TCP Connection, so we can send data to each other?
All three sections (Server, Client & Sharing Distribution) of codes are attached to a python program that each machine is running.
Start Distribution:
Here server socket is initiated, and we start threads for each ip-address we want to connect to.
def start_distributed_sharing(self):
self.serv = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.serv.bind(('0.0.0.0', 44445))
self.serv.listen(5)
MAX_CONNECTION = 5
IPs = ['{ip-address}', '{ip-address}', '{ip-address}', ]
client_threads = []
for ip in IPs:
client_threads.append(threading.Thread(target=self.client, args=(ip,)))
for i in range(0, len(client_threads)):
client_threads[i].start()
print("Clients is running")
while True:
conn, addr = self.serv.accept()
server_thread = threading.Thread(target=self.server, args=(conn, addr,))
server_thread.start()
print("New connection to server created!")
Server:
Each Machine starts their own server, and waits for a client to connect
def server(self, conn, addr):
while True:
data = ''
try:
data = conn.recv(4096)
except Exception:
print("Server: Lost a connection... Retrying...")
time.sleep(5)
break
if not data: break
try:
data = json.loads(data.decode('utf-8'))
print(data)
except Exception:
print("Server: Could not decode message: ", data)
conn.close()
print('Server: client disconnected')
Client:
Here the client attempts to connect to the server with the given ip-addresses
def client(self, ip):
# print(ip)
self.cli = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
while True:
connected = False
while not connected:
try:
print("Client: Connecting to, ", ip)
self.cli.connect((ip, 44445))
connected = True
except Exception:
print('Client: Could not connect to: ', ip, '. Retrying...')
time.sleep(5)
while True:
time.sleep(2)
try:
print("Client: Sending a msg to, ", ip)
self.cli.send(json.dumps({"id": "{PC1}", "height": self.nodes[self.current_leader].node_stats.lastBlockHeight, "latency": self.nodes[self.current_leader].avgLatencyRecords}).encode('utf-8'))
except Exception:
print("Client: Could not send more data to, ", ip)
break
if I understood correctly you want only one server for each (machine / program)? In this case, I think you need a unique port for each server. Or if you want each client to behave like a client / server to communicate with the main server, you can use your client's recv method.
Exemple 1 (send message to server and wait response):
def client(self, ip):
self.cli = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
while True:
connected = False
while not connected:
try:
print("Client: Connecting to, ", ip)
self.cli.connect((ip, 44445))
connected = True
except Exception:
print('Client: Could not connect to: ', ip, '. Retrying...')
time.sleep(5)
while True:
time.sleep(2)
try:
print("Client: Sending a msg to, ", ip)
self.cli.send(json.dumps({"id": "{PC1}", "height": self.nodes[self.current_leader].node_stats.lastBlockHeight, "latency": self.nodes[self.current_leader].avgLatencyRecords}).encode('utf-8'))
except Exception:
print("Client: Could not send more data to, ", ip)
break
# Waiting for server response
response = self.cli.recv(1024)
Now if you want a server message event, you can create a message handler like this (it is not very clean code it is for the example):
def on_message(self, msg):
print(msg)
def message_handle(self):
while True:
# Waiting for server message
msg = self.cli.recv(1024)
# check is message if valid
self.on_message(msg)
def client(self, ip):
# print(ip)
self.cli = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
while True:
connected = False
while not connected:
try:
print("Client: Connecting to, ", ip)
self.cli.connect((ip, 44445))
connected = True
except Exception:
print('Client: Could not connect to: ', ip, '. Retrying...')
time.sleep(5)
# Connection established start message handler
handler = threading.Thread(target=self.message_handle))
handler.start()
while True:
time.sleep(2)
try:
print("Client: Sending a msg to, ", ip)
self.cli.send(json.dumps({"id": "{PC1}", "height": self.nodes[self.current_leader].node_stats.lastBlockHeight, "latency": self.nodes[self.current_leader].avgLatencyRecords}).encode('utf-8'))
except Exception:
print("Client: Could not send more data to, ", ip)
break
In this example, you only had one server and clients on each machine. Then you have to manage the redirection of messages to target clients at the server level (identify the client sending the message and the target clients to address the messages to the right clients).
Related
I wrote a chat server with python and socket. Clients can connect to it via the local network but i need the clients to be able to connect to the server from another networks. I tried using 0.0.0.0 for the host IP in the server and I got this error message when trying to connect to it via another network
TimeoutError: [WinError 10060] A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond
This is my code for the server
import threading
import socket
host = "0.0.0.0"
port = 55555
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.bind((host, port))
server.listen()
clients = []
usernames = []
print("Server is online and running......")
def broadcast(message):
for client in clients:
client.send(message)
def handle(client):
while True:
try:
message = client.recv(1024)
broadcast(message)
except:
index = clients.index(client)
clients.remove(client)
client.close()
user = usernames[index]
broadcast(f"{user} left the chat!".encode("ascii"))
usernames.remove(user)
break
def receive():
while True:
client, address = server.accept()
print(f"Connected With {str(address)}")
client.send("NICK".encode("ascii"))
username = client.recv(1024).decode("ascii")
usernames.append(username)
clients.append(client)
print(f"Username - {username}")
broadcast(f"{username} just Joined the chat!".encode("ascii"))
client.send("connected to the server!".encode("ascii"))
thread = threading.Thread(target=handle, args=(client,))
thread.start()
receive()
And this is the code for the client
import socket
import threading
username = input("Your username : ")
host = "172.28.0.2"
port = 12344
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client.connect((host, port))
def receive():
while True:
try:
message = client.recv(1024).decode("ascii")
if message == "NICK":
client.send(username.encode("ascii"))
else:
print(message)
except:
print("An error occurred!")
client.close()
break
def write():
while True:
message = f"{username}: {input('')}"
client.send(message.encode("ascii"))
receive_thread = threading.Thread(target=receive)
receive_thread.start()
write_thread = threading.Thread(target=write)
write_thread.start()
Basically I need the clients to be able connect to the server that is running on my computer from their computers without a local network.
The easist way is that your server can apply for a public IP in WLAN, not a private internal IP behind some router or NAT devices. It can also works if here is a relay server in public.
If you can't, then you need to do NAT traverse to pounch a hole, so that external clients can get in touch with the server which is behind router. For this, you can google and use TURN/STUN/ICE.
I am making a script that will allow multiple clients to see live camera footage from the server script, this all works fine until one of the client scripts is closed, then a ConnectionResetError is raised, To avoid this I used a try and except block to catch the ConnectionResetError but the same error is raised every time after a connection is lost. Using just socket.recv stops the ConnectionResetError but socket.recv does not return the senders address which is required for the script to send the video stream back to the client.
Server:
host = "0.0.0.0"
port = 5000
buffer_size = 1024
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.bind(("", port))
listeners = list() # the addresses of the clients that want the video stream
def handle_queue(sock):
while True:
try:
message, address = sock.recvfrom(buffer_size) # block the thread until a packet arrives
print(address)
message = str(message, "utf-8") # decode the message
if message == "join":
listeners.append(address) # add the list of listeners
else:
print("unknown queue msg: ", message)
except ConnectionResetError:
print("The connection was forcefully quit")
queue_handler_thread = Thread(target=handle_queue, args=(sock,), daemon=True)
queue_handler_thread.start() # start the queue
the script then uses sock.sendto() for each address in the listeners list
Client:
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.sendto(bytes("join","utf-8"), (host, port))
while True:
data, address = sock.recvfrom(max_length) # block main thread until a packet is received
I believe what you are looking for is socket.getpeername().
This will return the remote address to which the socket is connected.
Your approach in handling the data in the wire whilst the connection is closed/lost is the right way. Handling via try/catch or using a recv() or recvfrom() method to wait for a response before closing the socket.
Since posting this i have discovered that it is infact TCP and this will not work for the project I am trying to do. Any more guidance and help would be appreciated, I cant seem to find any examples of Multiclient UDP Servers that run through one socket.
I partially resolved my system, after using the Example provided by AztCrw4282.
I was able to find a solution, I instead used the socket.accept() aproach, I am unsure as to whether this is UDP or TCP (I want UDP but with the connect system I think that is the handshake for TCP, but I am not 100% sure), but it works for now.
The client has to connect to the server then the server will accept or deny the connection, upon accepting a thread will be created for that clients connection to be managed. If any errors are thrown when interacting with that client their connection will be closed.
Server
try:
ServerSocket.bind((host, port))
except socket.error as e:
print(str(e))
print('Waiting for a Connection..')
ServerSocket.listen(5)
connections = list()
def threaded_client(connection):
connection.send(str.encode('Welcome to the Server\n'))
while True:
try:
data = str(connection.recv(2048),"utf-8") # this needs to be try catched
print("Packet Recv: ", data)
if data == "join":
print("Client Joined")
if data == "quit":
break
if not data:
break
except ConnectionResetError:
break
print("Closing a connection") # need to handle leaving the stream
connection.close()
def handle_stream():
for connection in connections:
try:
connection.send(bytes(json.dumps(frame_info) ,"utf-8"))
except:
print("Packet send failure, kicking client")
connections.remove(connection)
while True:
Client, address = ServerSocket.accept()
print('Connected to: ' + address[0] + ':' + str(address[1]))
connections.append(Client)
Thread(target=threaded_client, args=(Client, ), daemon=True).start()
ThreadCount += 1
print('Thread Number: ' + str(ThreadCount))
The only part that changes for the client is the part where it connects to the server
try:
ClientSocket.connect((host, port))
except socket.error as e:
print(str(e))
I am trying to create a simple chat program using Python and Socket. The server handles the connection between two clients. Whenever I run the program, for one of the clients, every message after the first one does not send. The message does not even reach the server.
This is on Windows, Python3. I've integrated threading so messages can be sent and received at the same time; however, the problem persists.
Server.py
conns = [] # list that stores both connections
def accept(sk):
while True:
conn, addr = sk.accept()
conns.append(conn)
print(addr, "has connected")
t = threading.Thread(target=accept, args=(sk,))
t.start() #start accept thread
def send(conn):
while True:
message = conn.recv(2048)
print(message)
for conn in conns:
conn.send(message) #sends message to every connection
print("Sent message")
t = threading.Thread(target=send, args=(conn,))
t.start() #start threading for send
Client.py
def recvMessages(s):
while True:
message = s.recv(2048)
print(message)
message = message.decode()
messages.append(message)
os.system("cls")
for message in messages:
print(message)
def sendMessage(s):
while True:
message = input()
message = message.encode()
s.send(message)
s = socket.socket()
host = socket.gethostname()
port = 8080
s.connect((host, port))
messages = []
print("Connected")
connected = True
threading.Thread(target=sendMessage, args=(s,)).start()
threading.Thread(target=recvMessages, args=(s,)).start()
All the messages should be sent from both clients, but one client can never send multiple messages, the other works fine.
Your server code is missing it's socket, and accept is not being run in your example, you also have invalid indentation as James pointed out, next time provide a minimal reproducible example.
I also cleaned up your files a bit, as you followed some bad practice, specifically with broadcasting the messages to all clients "def send" which actually receives, avoid confusing naming :)
in your server code you also sent only to one connection (which in your example doesn't exist) which it should be running the receive and send each time a new message is received
server.py
import socket
import threading
conns = [] # list that stores both connections
def accept(sk):
while True:
conn, addr = sk.accept()
conns.append(conn)
print(addr, "has connected")
# receive from new client
receive_t = threading.Thread(target=receive, args=(conn,))
receive_t.start() # start threading for send
def send(s, msg):
# change message ..
s.send(msg)
def broadcast(msg):
for conn in conns:
send(conn, msg)
def receive(conn):
try:
while True:
message = conn.recv(2048)
if not message: # detects if socket is dead, by testing message
print("client sent Nothing, removing")
conns.remove(conn)
break
broadcast(message) # send to all clients
except ConnectionResetError as e:
print('Could not send must be disconnected ')
conns.remove(conn) # remove dead socket
# added missing socket
sock = socket.socket()
sock.bind(('127.0.0.1', 8080))
sock.listen(1) # needs to bind and listen
t = threading.Thread(target=accept, args=(sock,))
t.start() # start accept thread
client.py
import os
import socket
import threading
messages = []
def recvMessages(s):
while True:
message = s.recv(2048)
message = message.decode()
print('new message= ', message)
messages.append(message)
os.system("cls")
for message in messages:
print(message)
def send_message(s):
while True:
message = input()
message = message.encode()
s.send(message)
s = socket.socket()
host = '127.0.0.1'
port = 8080
s.connect((host, port))
print("Connected")
connected = True
# added missing receive
receive = threading.Thread(target=recvMessages, args=(s,)) # missed receive thread in example
receive.start()
send_message(s)
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()
My client initially communicates using persistent HTTP with my own server. I am now trying to insert a web proxy in between them, so ideally the proxy will maintain 2 seperate persistent connections, one with the client and one with the server. How can I create a python web proxy that does that? I've only been able to create a non-persistent one so far, how would I expand it to do persistent connections?
Code so far:
def main():
# host and port info.
host = '' # blank for localhost
port = 80
try:
# create a socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# associate the socket to host and port
s.bind((host, port))
# listenning
s.listen(BACKLOG)
except socket.error, (value, message):
if s:
s.close()
print "Could not open socket:", message
sys.exit(1)
# get the connection from client
while 1:
print "Proxy running..."
conn, client_addr_port = s.accept()
# create a thread to handle request
thread.start_new_thread(proxy_thread, (conn, client_addr_port))
s.close()
def proxy_thread(conn, client_addr_port):
print "received something...creating new thread"
global threadcount
client_addr = client_addr_port[0]
client_port = client_addr_port[1]
# Check if this is an new video flow (assumption now is that 1 client has 1 video flow, and the video does not terminate)
if client_addr not in client_vid_flows:
print "New client detected", client_addr
client_vid_flows[client_addr] = 0 # Expand to save video rate
# ctrl_msg_timer(client_addr) # Start timer that sends a ctrl_msg to the switch at a certain frequency
with lock:
threadcount = threadcount + 1
print "Thread number:", threadcount
# get the request from browser
request_text = conn.recv(MAX_DATA_RECV)
request = HTTPRequest(request_text)
if not request:
sys.exit(1)
if request.error_code:
sys.exit(1)
host = request.headers['host']
port_pos = host.find(":") # find the port pos (if any)
if (port_pos==-1): # default port
webserver = host
port = 80
else: # specific port
webserver = host.split(":")[0]
port = host.split(":")[1]
print "Connect to: %s:%i" % (webserver, port)
try:
# create a socket to connect to the web server
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((webserver, port))
s.send(request_text) # send request to webserver
while 1:
# receive data from web server
data = s.recv(MAX_DATA_RECV)
print data
if (len(data) > 0):
# send to browser
conn.send(data)
print 'more to send, len(data)={}'.format(len(data))
else:
print 'end of send'
# s.close()
# conn.close()
except socket.error, (value, message):
if s:
s.close()
if conn:
conn.close()
print "Runtime Error:", message
sys.exit(1)
print "--------------------------------"
#********** END PROXY_THREAD ***********
if __name__ == '__main__':
main()
From wireshark, I see that a request packet is being sent to the proxy. However, the proxy is not picking this up.