Sending messages to all clients - python

This is probably very simple socket message exchange programming, but with my flimsy idea on socket and all, I could not really make it work properly so I'm reaching out for help.
Scenario : Two clients send messages. First one sends Halo and Seeya. After the first sends those messages, the second sends Hello and Bye (This client will just time sleep 6 secs to keep this order). To all messages, server replies with (original msg), client (number)!and a reply message is broadcasted to both clients.
So ideally, the result on both clients would look like this :
Halo, client 1!
Seeya, client 1!
Hello, client 2!
Bye, client 2 !
I couldn't make it to numbering each client, but here is my code that works weird.
server
import socket
clients = []
# send msgs to every client
def broadcast(message):
for client in clients :
client.send(message)
# connection part
s = socket.socket()
s.bind(('127.0.0.1', 7070))
s.listen(2)
while True:
c, addr = s.accept()
if c:
clients.append(c)
msg = c.recv(1024).decode()
msg += ', client!'
broadcast(msg.encode())
client1
### here goes connection part ###
s.send(("Halo").encode())
print(f"server = {(s.recv(1024)).decode()}")
# I've tried adding socket closing/connection part here
s.send(("Seeya").encode())
print((s.recv(1024)).decode())
time.sleep(3)
s.close()
client2 - first connected, but waits 6 secs for message order
### here goes connection part ###
time.sleep(6) # waits for message order
s.send(("Hello").encode())
print(f"server = {(s.recv(1024)).decode()}")
# I've tried adding socket closing/connection part here
s.send(("Bye").encode())
print((s.recv(1024)).decode())
time.sleep(3)
s.close()
The result I get is...
# On client 1 side
Halo, client! # no Seeya, Hello, Bye
# connection isn't closed
# On client 2 side
Hello, client! # no Seeya, Bye
Halo, client! # connection is closed

You have several issues going on here. The first and primary one is that your server's main loop is messed up. Each time through the loop, your server wants to accept a connection. So, the first client to connect gets accepted and immediately its first message is received. But the other client hasn't yet been accepted and so will not receive this first message. Then the second client connection is accepted and its first message is then sent to both clients, but then the loop iterates again and no more messages will be sent from the server until a third client connects. Etc.
So you need to separate accepting connections and receiving messages. This can be done in several ways. The most straight-forward way is to use the select function to wait on a number of sockets at once. That is, if you have a list of sockets including the listening socket and previously accepted ones, you'd do something like this:
# List starts with only listening socket
list_of_sockets = [lsock]
...
while True:
# Wait until some socket becomes "readable"
rfds, _wfds, _xfds = select.select(list_of_socks, [], [])
for sock in rfds:
if sock is lsock:
# Listening socket ready. Accept new connection
c, addr = lsock.accept()
print(f"Accepted {c}")
list_of_socks.append(c)
else:
msg = sock.recv(1024)
if msg:
# Received data from connected socket. Send to all
print(f"Got {msg.decode()} from {sock}")
broadcast(msg)
else:
# Got end of file (this client closed). Remove client from list
print(f"Closed {sock}")
list_of_socks.remove(sock)
Another issue with your code that will not be addressed by the server code above: You cannot assume that each message you send will be received as a distinct unit. That is, if the server sends 'Halo' and then it sends 'Hello' before you (a client) have done a recv, then in all likelihood, all the data will be returned in one fell swoop; that is 'HaloHello'.
Generally therefore you will want to put some kind of separator in the data (like a newline [\n] -- but then you'll need to parse the received data) or, better yet, place a fixed-length field in front of each message, giving the length of the subsequent variable-length part, so that you can receive and process exactly one message at a time. (In python, this typically involves using the struct module's pack and unpack functions.) As a result, your current client code will probably not properly sequence messages as you wish.
Also -- though it is less likely to cause a problem -- the same goes for send: you should not assume that send(N) sends exactly N bytes. It might send 1, 2, 3 or N-1 bytes. You can use sendall to ensure that all bytes are sent.

Related

Python sockets, how to receive only last message instead of the whole pending buffer?

I've got a server side socket that sends a message very frequently (it updates the same message with new data). The client side is processing that information as soon as the message is received.
My problem is that while the client is processing the message, the server side might have already send a few messages.
How could the client receive only the last message and drop all the pending packages?
This is the code I use for my client side. I made it non-blocking hoping it would solve my problem, but it didn't, so I don't know if it is even needed now.
import select
import socket
import time
class MyClient:
def __init__(self):
self.PORT = 5055
self.SERVER = socket.gethostbyname(socket.gethostname())
self.client = self.connect()
self.client.setblocking(False)
def connect(self):
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client.connect((self.SERVER, self.PORT))
return client
client = MyClient()
while True:
inputs = [client.client]
while inputs:
readable, writable, exceptional = select.select(inputs, [], inputs, 0.5)
for s in readable:
data = client.client.recv(2048)
print(data)
time.sleep(1)
There is no concept of a message in TCP in the first place, which also means that TCP cannot have any semantic for "last message". Instead TCP is a byte stream. One cannot rely on a 1:1 relation between a single send on one side and a single recv on the other side.
It is only possible to read data from the stream, not to skip data. Skipping data must be done by reading the data and throwing these away. To get the last message an application level protocol must first define what a message means and then the application logic must read data, detect message boundaries and then throw away all messages except the last.

Is there any way to send a message to everyone except the sender?

This question is similar to this one, but that was for JavaScript whereas mine is for Python.
How do I send a message to every connected client from the server except a selected client in Python using the sockets library?
I am making a simple game, where I want to detect the first person to press a button among three clients, and then notify the other two clients that they lost while notifying the winner that they won.
Usually, to send information to a client you do (on a new thread):
connected_client.sendall(data)
To receive, you do:
data = socket.recv()
But from what I searched, I couldn't find a way to send data to every connected client except a certain client.
I thought I could get around this by creating an 'identifying name' for each thread which ran the receiving function, but I couldn't find a good way to do this due to which I decided to search for a better option.
How can I do this?
Inserting them into a list can help. For example...
For the server side:
import socket
import threading
# This is where you store all of your Client IP's
client_list = []
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_ip = "yourip"
server_port = 8888
server.bind((server_ip, server_port))
def check_client(client_ip):
while True:
data = client_ip.recv(1024).decode()
if "condition" in data:
for ip in client_list:
if ip != client_ip:
ip.send("something".encode())
def check_connection():
server.listen()
while True:
client_ip, client_address = server.accept()
client_list.append(client_ip)
threading.Thread(target=check_client, args=(client_ip,), daemon=True).start()
check_connection()
So what happens is you call the check_connection function to check for incoming connections. After it receives one, it appends the connection inside the client_list variable. At the same time, it creates a thread to the current connection, check_client, which checks for any info being sent. If there's an info being sent by one of your clients, it checks if the "condition" string is inside your sent data. If so, it sends "something" string into all of your clients with exception to itself. Take note that when you send data, it must be in bytes.
For the client side:
import socket
import threading
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_ip = "serverip"
server_port = 8888
server.connect((server_ip, server_port))
def receive_info():
while True:
data = server.recv(1024).decode()
if "something" in data:
print("Someone already sent something")
threading.Thread(target=receive_info, daemon=True).start()
while True:
user_input = input("Type 'condition': ")
server.send(user_input.encode())
What this only does is, it sends your input into the server. If you typed "condition" on your input, it will send "something" on the other clients except you. So you need to setup 2 more clients in order to see the results.
Don't forget to set server_ip and server_port's values!

Python server get recv from all clients conncted and not just from the first one that conncted

The title explain everything. I made Java code that sends messgaes for the python server, but evry time, just the first message is sends because every time, java conneced again to server, and the server keeps waiting to next message from the first client that I send in the first time.
How can the server get message from all clients are connectd? and not just from one?
My python server:
server = socket.socket()
server.bind((socket.gethostname(), 4786))
server.listen(5)
(client, (ipNum, portNum)) = server.accept()
print("Client connected")
while True:
message = str(client.recv(64).decode()) # Check if client send message. I want to change it to check all clients
if(message != ""):
print("Client: " + message)
else:
time.sleep(0.1)
Summary
The server.accept() must be called inside the loop.
TLDR
The socket server returned from the call socket.socket() is a 'listening' socket. It is not used for any data transfer but just for listening incoming connections. When the server is willing to accept incoming connection then calls server.accept(). This call waits till a client connects. When a client connects the accept wakes up and returns a socket that represents a connection to one client. This socket is then used for data send and received and should be closed when the communication with this specific client is done.
When server wants to accept connection from another client it must call server.accept() again to wait for connection from another client and use the unique client socket for each connected client.
If it sufficient to handle client sequentially then you can just move the call accept onto the loop. Furthermore you should close the client socket when the communication with the client is done.
If multiple clients can be connected in parallel then slightly more complicated design is needed. You can start a new thread for each client after accepting the connection. The thread can call recv in a loop and terminates when the client disconnects. See Multi Threaded TCP server in Python for example.

UDP Client sends ping once a second, and also prints anything sent to it?

Good afternoon everyone reading this, I am new to programming with sockets, as well as new to asynchronous coding (I feel async may be part of the solution to my problem), so forgive me for any silly mistakes I make.
To start, I have a UDP Echo server that acts as a game server. Anytime it gets a ping sent to it, it adds the source ip and port to a list of "connected clients", and sends that exact ping out to everyone on the list, excluding the sender. This works fairly well, because it reacts upon receiving a message, so it can always just listen. The problem with the client however, is that I need to be constantly sending pings, while also listening.
This is currently what my client looks like:
import socket
from time import sleep
from contextlib import contextmanager
UDP_IP_ADDRESS = "127.0.0.1"
UDP_PORT_NO = 14004
Message = b"Hello World, From Client B"
#contextmanager
def socket_ctx():
""" Context Manager for the socket. Makes sure the socket will close regardless of why it exited."""
my_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
# Assign IP address and a RANDOM available port number to socket
my_socket.bind(('127.0.0.1', 0))
try:
# Let the rest of the app use the socket and wait for it to finish
yield my_socket
finally:
my_socket.close()
def send_data(client_sock):
client_sock.sendto(Message, (UDP_IP_ADDRESS, UDP_PORT_NO))
def listen(client_sock):
print(client_sock.recvfrom(100))
with socket_ctx() as sock:
while True:
send_data(sock)
listen(sock)
sleep(2)
Currently, it sends a ping once, then just idles as it presumably is listening. If it does happen to get a ping back, say, another client send a ping to the server, and the server sent the ping to this client, it hears it, prints it, and starts the loop again. The issue is, without another client sending something to jolt this one out of the listen, it doesn't send it's pings.
I think async might be my solution, but I would have no clue how to go about that. Does anyone have a solution for this problem?
Here's how I would implement a server with "receive and handle incoming UDP sockets, plus do some packet-sending once per second" behavior. Note that this uses the select() function to multiplex the two tasks, rather than asynchronous I/O; hopefully that is okay.
import socket
import select
import time
UDP_IP_ADDRESS = "127.0.0.1"
UDP_PORT_NO = 14004
Message = b"Hello World, From Client B"
udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
udp_socket.bind(('127.0.0.1', 0))
print "UDP socket is listening for incoming packets on port", udp_socket.getsockname()[1]
# When we want to send the next periodic-ping-message out
nextPingTime = time.time()
while True:
secondsUntilNextPing = nextPingTime - time.time();
if (secondsUntilNextPing < 0):
secondsUntilNextPing = 0
# select() won't return until udp_socket has some data
# ready-for-read, OR until secondsUntilNextPing seconds
# have passed, whichever comes first
inReady, outReady, exReady = select.select([udp_socket], [], [], secondsUntilNextPing)
if (udp_socket in inReady):
# There's an incoming UDP packet ready to receive!
print(udp_socket.recvfrom(100))
now = time.time()
if (now >= nextPingTime):
# Time to send out the next ping!
print "Sending out scheduled ping at time ", now
udp_socket.sendto(Message, (UDP_IP_ADDRESS, UDP_PORT_NO))
nextPingTime = now + 1.0 # we'll do it again in another second

Simple TELNET in Python - loop paused if no data coming

I'm trying to build a very simple TELNET client in Python and I'm getting problem on the last part: sending/receiving data to/from the server.
With the code I have, if no data arrives at the very beginnig, the loop get paused and I can't even send commands.
Here the interested part of the code:
# Infinite cycle that allows user to get and send data from/to the host
while True:
incoming_data = my_socket.recv(4096)
if not incoming_data:
print('Problem occurred - Connection closed')
my_socket.close()
sys.exit()
else:
# display data sent from the host trough the stdout
sys.stdout.write(incoming_data)
# Commands sent to the host
command = sys.stdin.readline()
my_socket.send(command)
(I think the program kinda of works if I try to connect to some hosts that send data at the beginning.)
The idea would be have two loops, running at the same time, getting data or sending data, but I can't get it to work.
I can't use the telnet library and I don't want to use the select library (only sys and socket).
You want to use the threading library.
The following program runs the receiving in one thread and the sending in another:
import socket
from threading import Thread
def listen(conn):
while True:
received = conn.recv(1024).decode()
print("Message received: " + received)
def send(conn):
while True:
to_send = input("Input message to send: ").encode()
conn.sendall(to_send)
host = "127.0.0.1"
port = 12345
sock = socket.socket()
sock.connect((host, port))
Thread(target=listen, args=[sock]).start()
Thread(target=send, args=[sock]).start()
This program is for Python 3. Python 2 is very similar, except print() works differently, and you don't need to encode() and decode() everything being sent through a socket.
The listen and send functions are run in parallel, so that as soon as data arrives, it is printed, but you can also send data at any time. Practically, you would probably want to make some changes so that the data isn't just printed over the input prompt. However, this would be hard just in a command line application.
Research queues for control over data passing between threads.
Let me know if you have any more questions.

Categories

Resources