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!
I have previously managed to implement a client-server socket script which relays messages between a single client and the server and I'm now trying to implement a multiple-client system.
More specifically, I would like to use the server as some sort of medium between two clients which retrieves information from one client and relays it to the other. I had tried to attach and send the port number of the receiving client and then extract it from the message on the server side. After that, I would try and send it to whatever socket with that port number but I ran into some trouble (as port numbers are determined at the point of sending I believe?) so now I am simply just trying to relay the sent message back to all clients. However, the problem is that the message is only being sent to the server and not being relayed to the desired client.
I had previously tried to implement a peer-to-peer system but I ran into trouble so I decided to take a step back and do this instead.
Server.py:
import socket, _thread, threading
import tkinter as tk
SERVERPORT = 8600
HOST = 'localhost'
class Server():
def __init__(self):
self.Connected = True
self.ServerSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.ServerSocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR,1)
self.ServerSocket.bind((HOST, SERVERPORT))
self.ServerSocket.listen(2)
self.Clients = []
def Listen(self):
print('Server is now running')
while self.Connected:
ClientSocket, Address = self.ServerSocket.accept()
self.Clients.append(Address)
print('\nNew user connected', Address)
t = threading.Thread(target=self.NewClient, args=(ClientSocket,
Address))
t.daemon = True
t.start()
self.Socket.close()
def NewClient(self, ClientSocket, Address):
while self.Connected:
if ClientSocket:
try:
ReceivedMsg = ClientSocket.recv(4096)
print('Message received from', Address, ':', ReceivedMsg)
self.Acknowledge(ClientSocket, Address)
if ReceivedMsg.decode('utf8').split()[-1] != 'message':
ReceiverPort = self.GetSendPort(ReceivedMsg)
self.SendToClient(ClientSocket,ReceivedMsg,ReceiverPort)
except:
print('Connection closed')
raise Exception
ClientSocket.close()
def Acknowledge(self, Socket, Address):
Socket.sendto(b'The server received your message', Address)
def GetSendPort(self, Msg):
MsgDigest = Msg.decode('utf8').split()
return int(MsgDigest[-1])
def SendToClient(self, Socket, Msg, Port):
Addr = (HOST, Msg)
for Client in self.Clients:
Socket.sendto(Msg, Client)
def NewThread(Func, *args):
if len(args) == 1:
t = threading.Thread(target=Func, args=(args,))
elif len(args) > 1:
t = threading.Thread(target=Func, args=args)
else:
t = threading.Thread(target=Func)
t.daemon = True
t.start()
t.join()
Host = Server()
NewThread(Host.Listen)
And the Client(.py):
import socket, threading
import tkinter as tk
Username = 'Ernest'
PORT = 8601
OtherPORT = 8602
SERVERPORT = 8600
HOST = '127.0.0.1'
class Client():
def __init__(self, Username):
self.Connected, self.Username = False, Username
self.Socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
def Connect(self):
print('Trying to connect')
try:
self.Socket.connect((HOST, SERVERPORT))
self.Connected = True
print(self.Username, 'connected to server')
Msg = MsgUI(self.Username)
Msg.Display()
except Exception:
print('Could not connect to server')
raise Exception
def SendMsg(self):
if self.Connected:
Msg = '{} sent you a message {}'.format(self.Username, OtherPORT)
self.Socket.sendall(bytes(Msg, encoding='utf8'))
self.GetResponse()
def GetResponse(self, *args):
AckMsg = '\n{} received the message'.format(self.Username)
NMsg = '\n{} did not receive the message'.format(self.Username)
if self.Connected:
Msg = self.Socket.recv(4096)
print(Msg)
if Msg:
self.Socket.sendall(bytes(AckMsg, encoding='utf8'))
else:
self.Socket.sendall(bytes(NMsg, encoding='utf8'))
class MsgUI():
def __init__(self, Username):
self.Username = Username
self.entry = tk.Entry(win)
self.sendbtn = tk.Button(win, text='send', command=Peer.SendMsg)
def Display(self):
self.entry.grid()
self.sendbtn.grid()
win.mainloop()
win = tk.Tk()
Peer = Client(Username)
Peer.Connect()
I want a message to be sent whenever the user presses the send button in the tkinter window, but at the same time, it is continually 'listening' to see if it received any messages.
I also previously tried to run the GetResponse method in the Client in another thread and instead of if self.Connected I used while self.Connected and it still didn't work.
UPDATE
After some helpful comments, I have edited the two files as such:
The server now holds the two sockets for each client which is run first. The server file is imported into the client file as a module. Each client file is then run and each client runs a function in the server file, requesting to use the socket. If the request is allowed (i.e. no error was thrown), the socket is connected, added to a set of clients stored in the server file and then returned to the client file. The client then uses this socket to send and receive messages.
Server.py
import socket, _thread, threading
import tkinter as tk
SERVERPORT = 8600
HOST = 'localhost'
class Server():
def __init__(self):
self.Connected = True
self.ServerSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.ServerSocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR,1)
self.ServerSocket.bind((HOST, SERVERPORT))
self.ServerSocket.listen(2)
self.Clients = {}
def ConnectClient(self, Username, Port):
Socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.Clients[Username] = [Socket, Port, False]
try:
self.Clients[Username][0].connect((HOST, SERVERPORT))
self.Clients[Username][2] = True
print('Opened port for user', Username)
return Socket
except Exception:
print('Could not open port for user', Username)
raise Exception
def Listen(self):
print('Server is now running')
while self.Connected:
ClientSocket, Address = self.ServerSocket.accept()
print('\nNew user connected', Address)
t = threading.Thread(target=self.NewClient, args=(ClientSocket,
Address))
t.daemon = True
t.start()
self.Socket.close()
def NewClient(self, ClientSocket, Address):
while self.Connected:
if ClientSocket:
try:
ReceivedMsg = ClientSocket.recv(4096)
if b'attempting to connect to the server' in ReceivedMsg:
ClientSocket.send(b'You are now connected to the server')
else:
print('Message received from', Address, ':',ReceivedMsg)
#self.Acknowledge(ClientSocket, Address)
ReceiverPort = self.GetSendPort(ReceivedMsg)
if ReceiverPort != None:
self.SendToClient(ClientSocket,ReceivedMsg,
ReceiverPort)
except:
print('Connection closed')
raise Exception
ClientSocket.close()
def Acknowledge(self, Socket, Address):
Socket.sendto(b'The server received your message', Address)
def GetSendPort(self, Msg):
MsgDigest = Msg.decode('utf8').split()
try:
Port = int(MsgDigest[-1])
except ValueError:
Port = None
return Port
def SendToClient(self, Socket, Msg, Port):
Addr = (HOST, Port)
Receiver = None
for Client, Vars in self.Clients.items():
if Vars[1] == Port:
Receiver = Client
self.Clients[Receiver][0].sendto(Msg, Addr)
def NewThread(Func, *args):
if len(args) == 1:
t = threading.Thread(target=Func, args=(args,))
elif len(args) > 1:
t = threading.Thread(target=Func, args=args)
else:
t = threading.Thread(target=Func)
t.daemon = True
t.start()
t.join()
Host = Server()
if __name__ == '__main__':
NewThread(Host.Listen)
And Client.py
import socket, threading, Server
import tkinter as tk
Username = 'Ernest'
PORT = 8601
OtherPORT = 8602
SERVERPORT = 8600
HOST = '127.0.0.1'
class Client():
def __init__(self, Username):
self.Connected, self.Username = False, Username
def Connect(self):
print('Requesting to connect to server')
try:
self.Socket = Server.Host.ConnectClient(self.Username, PORT)
self.Connected = Server.Host.Clients[self.Username][2]
Msg = '{} is attempting to connect to the server'.format(self.Username)
self.Socket.sendall(bytes(Msg, encoding='utf8'))
ReceivedMsg = self.Socket.recv(4096)
print(ReceivedMsg)
Msg = MsgUI(self.Username)
Msg.Display()
except Exception:
print('Could not connect to server')
raise Exception
def SendMsg(self):
try:
if self.Connected:
Msg = '{} sent you a message {}'.format(self.Username,OtherPORT)
self.Socket.sendall(bytes(Msg, encoding='utf8'))
self.GetResponse()
except Exception:
print('Connection closed')
raise Exception
def GetResponse(self, *args):
AckMsg = '\n{} received the message'.format(self.Username)
NMsg = '\n{} did not receive the message'.format(self.Username)
if self.Connected:
Msg = self.Socket.recv(4096)
print(Msg)
if Msg:
self.Socket.sendall(bytes(AckMsg, encoding='utf8'))
else:
self.Socket.sendall(bytes(NMsg, encoding='utf8'))
class MsgUI():
def __init__(self, Username):
self.Username = Username
self.entry = tk.Entry(win)
self.sendbtn = tk.Button(win, text='send', command=Peer.SendMsg)
def Display(self):
self.entry.grid()
self.sendbtn.grid()
win.mainloop()
win = tk.Tk()
Peer = Client(Username)
Peer.Connect()
Now the problem is more of a python and scope problem. When trying to relay the message back to the client, I was getting a KeyError as the Clients dictionary was still empty. When making the function call to the server in the client file, it's clear that the update to the dictionary happens in the client file rather than the server file - which is in a different instance. I need a method of changing the contents of the Clients dictionary that is called to action by the client file but takes effect in the server file.
Are you committed to multithreading? Threads don't run concurrently in python ( due to the GIL), and while they are one way to handle concurrent operations, they aren't the only way and usually they're not the best way, unless they're the only way. Consider this code, which doesn't handle failure cases well, but seems to work as a starting point.
import socket, select, Queue
svrsock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
svrsock.setblocking(0)
svrsock.bind(('', 17654))
svrsock.listen(16)
client_queues = {}
write_ready=[] # we'll update this for clients only that have things in the queue
while client_queues.keys() + [svrsock] :
readable, writable, exceptional = select.select(client_queues.keys() + [svrsock] , write_ready, [])
for rd in readable:
if rd is svrsock: # reading listening socket == accepting connection
conn, addr = svrsock.accept()
print("Connection from {}".format(addr))
conn.setblocking(0)
client_queues[conn] = Queue.Queue()
else:
data = rd.recv(1024)
if data:
# TODO: send to all queues
print("Message from {}".format(rd.getpeername()))
for sock, q in client_queues.iteritems():
q.put("From {}: {}".format( rd.getpeername(), data))
if sock not in write_ready:
write_ready.append(sock)
for rw in writable:
try:
data = client_queues[rw].get_nowait()
rw.send(data)
except Queue.Empty:
write_ready.remove(rw)
continue
The concept is pretty simple. The server accepts connections; each connection (socket) is associated with a queue of pending messages. Each socket that's ready for reading is read from, and its message is added to each client's queue. The recipient client is added into the write_ready list of clients with data pending, if it's not already in there. Then each socket that's ready for writing has its next queued message written to it. If there are no more messages, the recipient is removed from the write_ready list.
This is very easy to orchestrate if you don't use multithreading because all coordination is inherent in the order of the application. With threads it would be more difficult and a lot more code, but probably not more performance due to the gil.
The secret to handling multiple I/O streams concurrently without multithreading is select. In principle it's pretty easy; we pass select() a list of possible sockets for reading, another list of possible sockets for writing, and a final list that for this simplified demo I completely ignore . The results of the select call will include one or more sockets that are actually ready for reading or writing, which allows me to block until one or more sockets are ready for activity. I then process all the sockets ready for activity every pass ( but they've already been filtered down to just those which wouldn't block).
There's a ton still to be done here. I don't cleanup after myself, don't track closed connections, don't handle any exceptions, and so on. but without having to worry about threading and concurrency guarantees, it's pretty easy to start addressing these deficiencies.
Here it is "in action". Here for the client side I use netcat, which is perfect for layer 3 testing without layer 4+ protocols ( in other words, raw tcp so to speak). It simply opens a socket to the given destination and port and sends its stdin through the socket and sends its socket data to stdout, which makes it perfect for demoing this server app!
I also wanted to point out, coupling code between server and client is inadvisable because you won't be able to roll out changes to either without breaking the other. It's ideal to have a "contract" so to speak between server and client and maintain it. Even if you implement the behavior of server and client in the same code base, you should use the tcp communications contract to drive your implementation, not code sharing. Just my 2 cents, but once you start sharing code you often start coupling server/client versions in ways you didn't anticipate.
the server:
$ python ./svr.py
Connection from ('127.0.0.1', 52059)
Connection from ('127.0.0.1', 52061)
Message from ('127.0.0.1', 52061)
Message from ('127.0.0.1', 52059)
Message from ('127.0.0.1', 52059)
First client ( 52059):
$ nc localhost 17654
hello
From ('127.0.0.1', 52061): hello
From ('127.0.0.1', 52059): hello
From ('127.0.0.1', 52059): hello
Second client:
$ nc localhost 17654
From ('127.0.0.1', 52061): hello
hello
From ('127.0.0.1', 52059): hello
hello
From ('127.0.0.1', 52059): hello
If you need more convincing on why select is way more compelling than concurrent execution, consider this: Apache is based on a threading model, in other words, the connections each get a worker thread . nginx is based on a select model, so you can see how much faster that can potentially be. Not to say that nginx is inherently better, as Apache benefits from the threading model because of its heavy use of modules to extend capabilities ( mod_php for example), whereas nginx doesn't have this limitation and can handle all requests from any thread. But the raw performance of nginx is typically considered far higher and far more efficient, and a big reason for this is that it avoids almost all the cpu context switches inherent in apache. It's a valid approach!
A word on scaling. Obviously, this wouldn't scale forever. Neither would a threading model; eventually you run out of threads. A more distributed and high throughput system would likely use a Pub/Sub mechanism of some kind, offloading the client connection tracking and message queueing from the server to a pub/sub data tier and allowing connections to be restored and queued data to be sent, as well as adding multiple servers behind a load balancer. Just throwing it out there. You might be pleasantly surprised how well select can scale ( cpu is so much faster than network anyway that it's likely not the bottleneck).
I just started programming Python.
My goal is to built a digital Picture Frame with three Screens. Therefore I use 3 Raspis, one for each Monitor.
For the communication of these Raspis I need to program a server and a Client.
For a first test I want to built a server which is able to send and receive messages to/from multiple clients.
So I started with a few socket tutorials an created the following program.
Server Class (TcpServer.py)
class TcpServer:
clients = []
serverIsRunning = 0
port = 0
def __init__(self, port):
self.port = port
self.serverIsRunning = 0
self.serverRunning = 0
def startServer (self):
print("start Server...")
self.server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.server.bind(("", self.port))
self.server.listen(1)
self.serverRunning = 1
while self.serverRunning:
read, write, oob = select.select([self.server] + self.clients, [], [])
for sock in read:
if sock is self.server:
client, addr = self.server.accept()
self.clients.append(client)
print ("+++ Client ", addr[0], " verbunden")
else:
nachricht = sock.recv(1024)
ip = sock.getpeername()[0]
if nachricht:
print (ip, nachricht)
else:
print ("+++ Verbindung zu ", ip , " beendet")
sock.close()
self.clients.remove(sock)
for c in self.clients:
c.close()
self.clients.remove(c)
self.server.close()
def send(self, message):
message = message.encode()
self.server.send(message)
Client class (TcpClient.py)
import socket
class TcpClient:
def __init__(self, ip, port):
self.serverAdress = (ip, port)
self.connected = 0
self.connection = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.connection.connect(self.serverAdress)
print ("connectet to ", self.serverAdress)
def send(self, message):
message = message.encode()
self.connection.send(message)
Server:
import threading
import TcpServer
tcpServer = TcpServer.TcpServer(50000)
threadTcpServer = threading.Thread(target = tcpServer.startServer)
threadTcpServer.start()
while True:
tcpServer.send(input("Nachricht eingeben: "))
Client:
import threading
import TcpClient
tcpClient = TcpClient.TcpClient("192.168.178.49", 50000)
while True:
tcpClient.send(input("Nachricht eingeben: "))
I can send messages from the Client to the server, but when I want to send a Message from the server to the client it generates the following error:
BrokenPipeError: [Errno 32] Broken pipe
I assume it is because the server thread blocks the socket while waiting of a incoming message. But I have no idea how to handle this.
How can I program a server who can send and receive messages? Can you recommend a tutorial? I didn't found a tutorial who describes a solution for my problem.
Edit:
Now I tried to solve the problem with the socketserver library, but I still can't solve may problem.
here is my new code for the server:
import socketserver
import threading
import time
class MyTCPHandler(socketserver.BaseRequestHandler):
"""
The RequestHandler class for our server.
It is instantiated once per connection to the server, and must
override the handle() method to implement communication to the
client.
"""
def handle(self):
# self.request is the TCP socket connected to the client
self.data = self.request.recv(1024).strip()
print("{} wrote:".format(self.client_address[0]))
print(self.data)
# just send back the same data, but upper-cased
self.request.sendall(self.data.upper())
if __name__ == "__main__":
HOST, PORT = "localhost", 9999
# Create the server, binding to localhost on port 9999
server = socketserver.TCPServer((HOST, PORT), MyTCPHandler)
# Activate the server; this will keep running until you
# interrupt the program with Ctrl-C
threadTcpServer = threading.Thread(target = server.serve_forever)
threadTcpServer.start()
print("server started")
time.sleep(10)
print("sending Data")
server.request.sendall("Server is sending...")
it generates the error:
AttributeError: 'TCPServer' object has no attribute 'request'
My goal is to write a server with a thread who receives Data and still be able to send data from a other thread.
Is this even possible with only one socket?
You should use the provided socketserver rather than writing all the handling of sockets and select etc.
There are multiple problems with your code -
1 - The server is trying to write to the listening socket!! The client communication socket is the one that you get from the accept() call and that is the one you have to use for reading and writing.
2 - The client is sending the data and completing immediately, but it should really wait for getting a response. Otherwise, the python / OS will close the client socket as soon as the program completes and it will mostly be before the server gets a chance to respond.
I believe with the Handler code you are able to receive the data sent by the client on the server and are also able to send some data back from the Handler to the client? You must have understood that the server cannot send any data back unless there is a client connected to it?
Now, to send data to the client (or clients) from "another" thread, you will need a way to make the handler objects or the client sockets (available inside the Handler object as self.request) available to the "another" thread.
One way is to override the def __init__(self, request, client_address, server): method and save this object's reference in a global list. Remember to do the below as the last line of the overridden init -
# BaseRequestHandler __init__ must be the last statement as all request processing happens in this method
socketserver.BaseRequestHandler.__init__(self, request, client_address, server)
Once you have all the client handlers in the global list, you can easily write to all the clients from any thread as per your needs. You must read about synchronization (Locks) and understand that using same object / socket from multiple threads can create some logical / data issues with your application.
Another thing that you have to worry about and code for is cleaning up this global list whenever a client closes the connection.
I have python script with only one socket object that is connect to a java server.
I started a thread for sending heart beat message to server per 5 secs.
And another thread for receiving message from server.
BTW, all the data send/recv is in protobuffer format.
# socket_client.py
def recv_handler():
global client_socket
while True:
try:
# read 4 bytes first
pack_len = client_socket.recv(4)
pack_len = struct.unpack('!i', pack_len)[0]
# read the rest
recv_data = client_socket.recv(pack_len)
# decode
decompressed_data = data_util.decompressMessage(recv_data)
sc_pb_message = data_util.decodePBMessage(decompressed_data)
sc_head = data_util.parseHead(sc_pb_message)
except:
print 'error'
def heart_handler():
global client_socket
while True:
if client_socket:
message = data_util.makeMessage('MSG_HEART_BEAT')
compressed_data = data_util.compressMessage(message)
send_data = data_util.makeSendData(compressed_data)
try:
client_socket.send(send_data)
except:
print 'except'
pass
time.sleep(5)
def connect(address, port):
global client_socket
client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client_socket.connect((address, port))
# thread sending heart beat message
th = threading.Thread(target = heart_handler)
th.start()
# thread recving message
tr = threading.Thread(target = recv_handler)
tr.start()
The code above works just fine. The script will send a heart beat message per 5 secs and receive the message from server and the message can be decoded successfully.
And here comes the trigger part than I do not know how to implement.
My python script need to receive input from the browser at the same time, so I started a BaseHTTPServer, to handle the POST request from the browser.
When a request come, I would like to call the client_socket.send method to send a specific message to the server and of course I need to return the data from server back to the browser.
# http_server.py
def do_POST(self):
# ...
result = socket_client.request(message)
self.send_response(200)
self.end_headers()
self.wfile.write(...)
And here is what I tried to do in request:
def request(message):
global client_socket
client_socket.send(message)
pack_len = client_socket.recv(4)
pack_len = struct.unpack('!i', pack_len)[0]
recv_data = client_socket.recv(pack_len)
return recv_data
The problem I am having is the data I received in the request method after calling the send method seems to be disturbed by the data of heart beat in the thread.
If I comment out the heart beat thread and the receive thread, than the request method will work just fine. The data from server can decoded with no error and it can be sent back to the browser successfully.
My solution now might be wrong and I really do not know how to get this work.
Any advice will be appreciated, thanks :)
socket object in Python is not thread-safe, you need to access the shared resources (in this case the client_socket object) with the help of some synchronization primitives, such as threading.Lock in Python 2. Check here for a similar problem: Python: Socket and threads?
I am using a REQ/REP type socket for ZMQ communication in python. There are multiple clients that attempt to connect to one server. Timeouts have been added in the client script to prevent indefinite wait.
The problem is that when the server is not running, and a client attempts to establish connection, it's message gets added to the queue buffer, which should not even exist at this moment ideally. When the script starts running and a new client connects, the previous client's data is taken in first by the server. This should not happen.
When the server starts, it assumes a client is connected to it since it had tried to connect previously, and could not exit cleanly (since the server was down).
In the code below, when the client tries the first time, it gets ERR 03: Server down which is correct, followed by Error disconnecting. When server is up, I get ERR 02: Server Busy for the first client which connects. This should not occur. The client should be able to seamlessly connect with the server now that it's up and running.
Server Code:
import zmq
def server_fn():
context = zmq.Context()
socket = context.socket(zmq.REP)
socket.bind("tcp://192.168.1.14:5555")
one=1
while one == 1:
message = socket.recv()
#start process if valid new connection
if message == 'hello':
socket.send(message) #ACK
#keep session alive until application ends it.
while one == 1:
message = socket.recv()
print("Received request: ", message)
#exit connection
if message == 'bye':
socket.send(message)
break
#don't allow any client to connect if already busy
if message == 'hello':
socket.send ('ERR 00')
continue
#do all data communication here
else:
socket.send('ERR 01: Connection Error')
return
server_fn()
Client Code:
import zmq
class client:
def clientInit(self):
hello='hello'
#zmq connection
self.context = zmq.Context()
print("Connecting to hello world server...")
self.socket = self.context.socket(zmq.REQ)
self.socket.connect("tcp://192.168.1.14:5555")
#RCVTIMEO to prevent forever block
self.socket.setsockopt(zmq.RCVTIMEO, 5000)
#SNDTIME0 is needed since script may not up up yet
self.socket.setsockopt(zmq.SNDTIMEO, 5000)
try:
self.socket.send(hello)
except:
print "Sending hello failed."
try:
echo = self.socket.recv()
if hello == echo:
#connection established.
commStatus = 'SUCCESS'
elif echo == 'ERR 00':
#connection busy
commStatus = "ERR 00. Server busy."
else:
#connection failed
commStatus="ERR 02"
except:
commStatus = "ERR 03. Server down."
return commStatus
def clientQuit(self):
try:
self.socket.send('bye')
self.socket.recv()
except:
print "Error disconnecting."
cObj = client()
commStatus=cObj.clientInit()
print commStatus
cObj.clientQuit()
PS - I have a feeling the solution may lie in the correct usage of socket.bind and socket.connect.
Answering my own question-
The problem is that the first client sends a message which the server accepts when it starts running, regardless of the status of the client.
To prevent this, 2 things have to be done. The most important thing is to use socket.close() to close the client connection. Secondly, the LINGER parameter can be set to a low value or zero. This clears the buffer after the timeout value from the time the socket is closed.
class client:
def clientInit(self):
...
self.socket.setsockopt(zmq.LINGER, 100)
...
def clientQuit(self):
try:
self.socket.send('bye')
self.socket.recv()
except:
print "Error disconnecting."
self.socket.close()