I'm having an issue with a Python socket client and server - I want the following exchange:
Client sends message to server.
Server reads message and makes a decision based on it.
Server sends appropriate message back to client.
I'm getting stuck on the last point. No matter what I try, the client won't receive the message sent by the server. Refer to code below:
Client:
import socket, select
ip = ["10.189.165.14", 25565]
# Client class to manage server connections
class Client:
def __init__(self, ip:list):
self.sock = socket.socket() # Create the socket object
ip[1] = int(ip[1]) # Convert the port number provided into an int
self.ip = tuple(ip) # Save the inputted IP and port (sock.connect requires a tuple, so convert it here)
def connect(self):
"""Method to attempt to connect to the server"""
self.sock.settimeout(3) # Set the timeout period that the client will try to connect for
self.sock.connect(self.ip) # Connection to the host and port
self.conn = self.sock # Set self.conn = self.sock, for consistent variable naming with the server code
self.sock.setblocking(False) # Set socket to be non-blocking so it errors with BlockingIOError instead of blocking the connection
print("This is the CLIENT; Connected to server")
return True # Return True to the caller so it knows a successful connection has been established
def recv_info(self):
"""Method to receive information from the server in an x byte string"""
for _ in range(100): # Run this method multiple times, until the incoming variable receives information
incoming, _, _ = select.select([self.conn], [], [], 0.01) # Monitor incoming messages from the socket
if incoming: # If there is an incoming message from the server
dataRecv = []
while True: # Infinite loop to break the received bytes data into chunks and load them individually
packet = b"" # Reset packet to a blank byte string
try:
packet = self.conn.recv(4096) # Receive data from server into packet
except BlockingIOError: # If packet gets nothing from the recv()
break # Break out of this while loop
if not packet:
break # If the packet has received nothing from the connection, break this loop
dataRecv.append(packet) # Add the packet onto dataRecv
return dataRecv # Return the received message
client = Client(ip) # Create the client object
conn = client.connect() # Establish a connection with the server
if conn == True: # If connection is successful
client.conn.sendall(b"Send to server") # Send message to the server
print("Sent to server")
recv = client.recv_info() # Look for message from the server
print(f"Received {recv} from server")
Server:
import socket, select
class Server:
def __init__(self):
self.sock = socket.socket() # Create the socket object upon instantiation
self.ip = (socket.gethostbyname(socket.gethostname()), 25565) # Make machine name, socket and port visible to the outside world
self.sock.bind(self.ip) # Bind the IP and port to the socket
def connect(self):
"""Method to connect to a client socket"""
self.sock.settimeout(5) # Set the timeout period that the server will wait for a connection
self.sock.listen(5) # Number of allowed unaccepted connections before refusing new connections (i.e. connection queue length)
try:
self.conn, addr = self.sock.accept() # Wait to establish connection with an outside client
except socket.timeout: # If the timeout period elapses without a connection
return False # Exit this method
print("This is the SERVER; Connected to client")
self.sock.setblocking(False) # Set socket to be non-blocking so it errors with BlockingIOError instead of blocking the connection
return True # Return True so the caller knows that a connection has been established
def recv_info(self):
"""Method to receive information from the client in an x byte string"""
for _ in range(100): # Run this method multiple times, until the incoming variable receives information
incoming, _, _ = select.select([self.conn], [], [], 0.01) # Monitor incoming messages from the socket
if incoming: # If there is an incoming message from the client
dataRecv = [] # Start dataRecv as an empty list for this _ iteration
while True: # Infinite loop while receiving data
packet = b"" # Reset packet to a blank byte string
try:
packet = self.conn.recv(4096) # Receive data from client into packet
except BlockingIOError: # If self.conn.recv() is blocked
break # Break out of this while loop
if not packet: # If packet gets nothing from the recv()
break # Break out of this while loop
dataRecv.append(packet) # Add the packet onto dataRecv
# self.conn.sendall(b"Send to client")
print(f"Received {dataRecv} from client")
# Decision making logic to decide on what msg should be
msg = b"Send to client"
self.conn.sendall(msg)
print("Sent to client")
break # Break out of the outer incoming attempts loop
server = Server() # Initialise the server
serverRunning = True
while serverRunning == True: # This boolean always starts as True
conn = server.connect() # Check for a client connection; will return True or False
if conn == True:
# When a connection is established, receive information from the client
server.recv_info() # recv_info() contains an infinite loop to receive information
Running this gives the output:
Client:
This is the CLIENT; Connected to server
Sent to server
Received None from server
Server:
This is the SERVER; Connected to client
Sent to client
Received [b'Send to server'] from client
Which is not working as the client has received nothing.
If you uncomment the line
self.conn.sendall(b"Send to client")
in Server.recv_info() then suddenly, it works. But I need to keep the sendall outside of the while True loop I think, in case the incoming message is split over multiple packets? I'm not sure why this would work when a sendall outside the loop does not.
I think that there must be something about sockets I'm not understanding here. I've played around with blocking/not-blocking, closing the socket connection, adding wait times, and read a lot of questions on here (closest I've found is I got problems with making a Python socket server receive commands from a Python socket client), but to no avail. Any ideas would be most welcome.
Related
I have searched a lot on the forums for implementing TCP server using Python. All that I could find is a multi-threaded approach to TCP Server Implementation on a single port for interacting with clients.
I am looking for Server sample code for creating sockets using different ports.I have clients with distinct port numbers.For example, One socket binding IP and portNo-2000 and second one binding IP and another port No. 3000 and so on. Could anybody provide pointers?
import socket
from typing import Union
SERVERS_PORT_LIST = []
def start_server(port) -> Union[socket.socket, None]:
server_socket = socket.socket()
try:
server_socket.bind(("0.0.0.0", port))
server_socket.listen()
return server_socket
except (socket.error, OSError): # in case the port is taken
return None
def accept_client(server_sock: socket.socket) -> Union[tuple[socket.socket, tuple[str, str]], tuple[None, None]]:
# wait 5 seconds for a client to connect, if no one
# tries to connect within 5 seconds it will return None
server_sock.settimeout(5)
try:
client_socket, client_addr = server_sock.accept()
return client_socket, client_addr
except (socket.error, ConnectionRefusedError):
return None, None
def main():
servers_client_sockets = {}
# create all the servers
for port in SERVERS_PORT_LIST:
server_sock = start_server(port)
if server_sock is not None:
servers_client_sockets[(server_sock, port)] = []
else:
print(f"Port {port} is taken.")
# accept 1 client for each server and append the client to the list inside the dict
# that contains {server_sock: [list of clients], server_sock2: [list of clients], ...}
for server_sock, port in servers_client_sockets.keys():
client_socket, client_addr = accept_client(server_sock)
if client_socket is not None: # someone connected
servers_client_sockets[(server_sock, port)] = servers_client_sockets[server_sock].append(client_socket)
else:
print(f"No client on port {port}.")
# handle the client in each server sock
if __name__ == '__main__':
main()
answer to your question:
I have made a little change to the code.
to receive a client data you need to go through the dict servers_client_sockets keys, and search for the port you want to receive from.
the dict is built like the following: {(server_sock, port): [clients], (server_2_sock, port): [clients], ..., (server_x_sock, port): [clients]}
example:
for server_sock, port in servers_client_sockets.keys():
if port == the_port_you_want:
client_list = servers_client_sockets[(server_sock, port)]
now client_list should contains all the clients that connected to the port you were looking for.
if there is only one client in each port you can do this:
data clients_list[1].recv(a_number_that_says_how_many_bytes_to_recv).decode()
# with out '.decode()' if you want to keep the data as bytes
if there is more than one client you will need to do a loop on the code from above (receive the data for each one of the clients)
NOTE:
if you want to accept more than 1 client you will need to add more code, and insert the client socket to the dict servers_client_sockets (I explained above how the dict is built).
use this code to accept 1 more client for each server port
# loops on all the server sockets
for server_sock, port in servers_client_sockets.keys():
# tries to accept a client for each server socket
client_socket, client_addr = accept_client(server_sock)
# if there is a new client it adds it to the dict servers_client_sockets
if client_socket is not None: # someone connected
servers_client_sockets[(server_sock, port)] = servers_client_sockets[server_sock].append(client_socket)
# if there is no new client, it prints no client on port x
else:
print(f"No client on port {port}.")
Using asyncio you can write elegant multi-servers and clients without having to use more than one thread or having to invert the control flow for efficient single-threaded non-blocking I/O.
asyncio allows one to:
write I/O code in straight-forward sequential/blocking manner, which in the past required using one thread per connection;
execute this code using only one thread of execution in non-blocking fashion, which in the past required inverting the control flow of your sequential blocking I/O code into callback-hell based programming model.
You can find example TCP server and client in Python Standard library documentation, asyncio examples.
Server:
import asyncio
class EchoServerProtocol(asyncio.Protocol):
def connection_made(self, transport):
peername = transport.get_extra_info('peername')
print('Connection from {}'.format(peername))
self.transport = transport
def data_received(self, data):
message = data.decode()
print('Data received: {!r}'.format(message))
print('Send: {!r}'.format(message))
self.transport.write(data)
print('Close the client socket')
self.transport.close()
async def main():
# Get a reference to the event loop as we plan to use
# low-level APIs.
loop = asyncio.get_running_loop()
server = await loop.create_server(
lambda: EchoServerProtocol(),
'127.0.0.1', 8888)
async with server:
await server.serve_forever()
asyncio.run(main())
these codes are mostly taken from a yt tutorial.
this code gives a console based chat server .
the problem is I can't use it in two computers.
I can't add more clients from computers other than my computer.
this is server
import socket
import select
HEADER_LENGTH = 10
IP = "127.0.0.1"
PORT = 1234
# Create a socket
# socket.AF_INET - address family, IPv4, some otehr possible are AF_INET6, AF_BLUETOOTH, AF_UNIX
# socket.SOCK_STREAM - TCP, conection-based, socket.SOCK_DGRAM - UDP, connectionless, datagrams, socket.SOCK_RAW - raw IP packets
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# SO_ - socket option
# SOL_ - socket option level
# Sets REUSEADDR (as a socket option) to 1 on socket
server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
# Bind, so server informs operating system that it's going to use given IP and port
# For a server using 0.0.0.0 means to listen on all available interfaces, useful to connect locally to 127.0.0.1 and remotely to LAN interface IP
server_socket.bind((IP, PORT))
# This makes server listen to new connections
server_socket.listen()
# List of sockets for select.select()
sockets_list = [server_socket]
# List of connected clients - socket as a key, user header and name as data
clients = {}
print(f'Listening for connections on {IP}:{PORT}...')
# Handles message receiving
def receive_message(client_socket):
try:
# Receive our "header" containing message length, it's size is defined and constant
message_header = client_socket.recv(HEADER_LENGTH)
# If we received no data, client gracefully closed a connection, for example using socket.close() or socket.shutdown(socket.SHUT_RDWR)
if not len(message_header):
return False
# Convert header to int value
message_length = int(message_header.decode('utf-8').strip())
# Return an object of message header and message data
return {'header': message_header, 'data': client_socket.recv(message_length)}
except:
# If we are here, client closed connection violently, for example by pressing ctrl+c on his script
# or just lost his connection
# socket.close() also invokes socket.shutdown(socket.SHUT_RDWR) what sends information about closing the socket (shutdown read/write)
# and that's also a cause when we receive an empty message
return False
while True:
# Calls Unix select() system call or Windows select() WinSock call with three parameters:
# - rlist - sockets to be monitored for incoming data
# - wlist - sockets for data to be send to (checks if for example buffers are not full and socket is ready to send some data)
# - xlist - sockets to be monitored for exceptions (we want to monitor all sockets for errors, so we can use rlist)
# Returns lists:
# - reading - sockets we received some data on (that way we don't have to check sockets manually)
# - writing - sockets ready for data to be send thru them
# - errors - sockets with some exceptions
# This is a blocking call, code execution will "wait" here and "get" notified in case any action should be taken
read_sockets, _, exception_sockets = select.select(sockets_list, [], sockets_list)
# Iterate over notified sockets
for notified_socket in read_sockets:
# If notified socket is a server socket - new connection, accept it
if notified_socket == server_socket:
# Accept new connection
# That gives us new socket - client socket, connected to this given client only, it's unique for that client
# The other returned object is ip/port set
client_socket, client_address = server_socket.accept()
# Client should send his name right away, receive it
user = receive_message(client_socket)
# If False - client disconnected before he sent his name
if user is False:
continue
# Add accepted socket to select.select() list
sockets_list.append(client_socket)
# Also save username and username header
clients[client_socket] = user
print('Accepted new connection from {}:{}, username: {}'.format(*client_address, user['data'].decode('utf-8')))
# Else existing socket is sending a message
else:
# Receive message
message = receive_message(notified_socket)
# If False, client disconnected, cleanup
if message is False:
print('Closed connection from: {}'.format(clients[notified_socket]['data'].decode('utf-8')))
# Remove from list for socket.socket()
sockets_list.remove(notified_socket)
# Remove from our list of users
del clients[notified_socket]
continue
# Get user by notified socket, so we will know who sent the message
user = clients[notified_socket]
print(f'Received message from {user["data"].decode("utf-8")}: {message["data"].decode("utf-8")}')
# Iterate over connected clients and broadcast message
for client_socket in clients:
# But don't sent it to sender
if client_socket != notified_socket:
# Send user and message (both with their headers)
# We are reusing here message header sent by sender, and saved username header send by user when he connected
client_socket.send(user['header'] + user['data'] + message['header'] + message['data'])
# It's not really necessary to have this, but will handle some socket exceptions just in case
for notified_socket in exception_sockets:
# Remove from list for socket.socket()
sockets_list.remove(notified_socket)
# Remove from our list of users
del clients[notified_socket]
and this is client
import socket
import select
import errno
import sys
HEADER_LENTH = 10
IP = "127.0.0.1"
PORT = 1234
my_username = input("Username : ")
client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client_socket.connect((IP, PORT))
client_socket.setblocking(False)
username = my_username.encode('utf-8')
username_header = f"{len(username):<{HEADER_LENTH}}".encode('utf-8')
client_socket.send(username_header + username)
while True:
message = input(f"{my_username} > ")
if message:
message = message.encode('utf-8')
message_header = f"{len(message) :< {HEADER_LENTH}}".encode('utf-8')
client_socket.send(message_header + message)
try:
while True:
# receive things
username_header = client_socket.recv(HEADER_LENTH)
if not len(username_header):
print("Connection closed by the server")
sys.exit()
username_length = int(username_header.decode('utf-8').strip())
username = client_socket.recv(username_length).decode('utf-8')
message_header = client_socket.recv(HEADER_LENTH)
message_length = int(message_header.decode('utf-8').strip())
message = client_socket.recv(message_length).decode('utf-8')
print(f"{username} > {message}")
except IOError as e:
if e.errno != errno.EAGAIN and e.errno != errno.EWOULDBLOCK:
print('Reading error: {}'.format(str(e)))
sys.exit()
continue
except Exception as e:
print('General error', str(e))
sys.exit()
so this is it guys pls help me .
I need this for my school project.
pls guys.
I am creating a game in Pygame that requires a client-server part for the multiplayer.
First, I am checking if there are less than two connections. If this is the case, the client will be shown a screen that says 'waiting for connections'.
I have got the client to successfully send a '1' message to the server, which will respond with a '1' if the server is not full. Therefore, if the server does not respond with a 1, the server is full, and the client can continue.
However, I am getting this error mentioned in the title.
Server code:
import socket
import sys
import threading
from _thread import *
import time
s=socket.socket(socket.AF_INET, socket.SOCK_STREAM)
host=socket.gethostname()
ip=socket.gethostbyname(host)
port=8000
connections=[]
print('Your local ip address is',ip)
s.bind((host,port))
s.listen(2)
def threaded_client(connection):
while True:
data=connection.recv(2048) #anything we receive
if not data:
break
connection.close()
def checkconnected(connections):
noofconn=len(connections)
while True:
print('Waiting for a connection...')
connection,address=s.accept()
print(address,'has connected to server hosted at port',address[1])
connections.append(address)
data=connection.recv(1024)
received=[]
counter=0
for letter in data:
received.append(data[counter])
counter+=1
received=(chr(received[0]))
if received=='1':#handling initial connections
if len(connections)!=2:
s.sendall(b'1')
if not data:
break
start_new_thread(threaded_client,(connection,))
s.close()
The client code that calls it:
host=socket.gethostname()
ip=socket.gethostbyname(host)
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
address=address
port=8000
if address==ip:
ishost=True
else:
ishost=False
try:
s.connect((address,port))
connectionwaitingmenu()
connected=False
while connected==False:
s.sendall(b'1')
data=s.recv(1024)
received=[]
counter=0
for letter in data:
received.append(data[counter])
counter+=1
received=(chr(received[0]))
if received=='1':
connected=False
elif received!='1':
connected=True
classselection()
The error occurs on the s.sendall(b'1') line in the server code.
There are a few other problems in your code, but the cause of the error in the title is that you're using the wrong socket to send and receive data on the server side.
When a server accepts a new client (conn, addr = server.accept()) this returns a new socket, which represents the channel through which you communicate with the client. All further communication with this client happens by reading and writing on conn. You should not be calling recv() or sendall() on s, which is the server socket.
The code should look something like this:
# Assuming server is a bound/listening socket
conn, addr = server.accept()
# Send to client
conn.sendall(b'hello client')
# Receive from client
response = conn.recv(1024)
# NO
server.send(b'I am not connected')
this_wont_work = server.recv(1024)
I want to create a server that handles a lot of clients at the same time (handles: receiving data from clients and sending data to all clients at the same time!!!)
Actually i'm trying to create a chat box. The program will work like this:
1) There's going to be a server which handles the clients.
2) More than one clients can join the server.
3) Clients send messages (Strings) to the server.
4) The server receive's the message from a client and then it send's it to all
the clients except the one he got it from.
And this is how the clients will communicate each other. No private messages available. When someone hits the enter all the clients will see the message on their screen.
The client module is easy to make, because the client communicates only with one socket (The Server).
The server module from the other hand is really complicated, i don't know how to do it (I also know about threads).
This is my atempt:
import socket, threading
class Server:
def __init__(self, ip = "", port = 5050):
'''Server Constructor. If __init__ return None, then you can use
self.error to print the specified error message.'''
#Error message.
self.error = ""
#Creating a socket object.
self.server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
#Trying to bind it.
try:
self.server.bind( (ip, port) )
pass
#Failed, because socket has been shuted down.
except OSError :
self.error = "The server socket has been shuted down."
return None
#Failed, because socket has been forcibly reseted.
except ConnectionResetError:
self.error = "The server socket has been forcibly reseted."
return None
#Start Listening.
self.server.listen()
#_____Other Variables_____#
#A flag to know when to shut down thread loops.
self.running = True
#Store clients here.
self.clients = []
#_____Other Variables_____#
#Start accepting clients.
thread = threading.thread(target = self.acceptClients)
thread.start()
#Start handling the client.
self.clientHandler()
#Accept Clients.
def acceptClients(self):
while self.running:
self.clients.append( self.server.accept() )
#Close the server.
self.server.close()
#Handle clients.
def clientHandler(self):
while self.running:
for client in self.clients:
sock = client[0]
addr = client[1]
#Receive at most 1 mb of data.
#The problem is that recv will block the loop!!!
data = sock.recv(1024 ** 2)
As you can see, i accept clients using a thread so the server.accept() won't block the program. And then i store the clients into a list.
But the problem is on the clientHandler. How am i going to recv from all
clients at the same time? The first recv will block the loop!!!
I also tried to start new threads (clientHandlers) for every new client
but the problem was the synchronization.
And what about the send? The server must send data to all the clients, so the clientHandler is not yet finished. But if i mix the methods recv and send then the problem become's more complicated.
So what is the proper and best way to do this?
I'd like to give me an example too.
Multithreading is great when the different clients are independant of each other: you write your code as if only one client existed and you start a thread for each client.
But here, what comes from one client must be send to the others. One thread per client will certainly lead to a synchronization nightmare. So let's call select to the rescue! select.select allows to poll a list of sockets and returns as soon as one is ready. Here you can just build a list containing the listening socket and all the accepted ones (that part is initially empty...):
when the listening socket is ready for read, accept a new socket and add it to the list
when another socket is ready for read, read some data from it. If you read 0 bytes, its peer has been shut down or closed: close it and remove it from the list
if you have read something from one accepted socket, loop on the list, skipping the listening socket and the one from which you have read and send data to any other one
Code could be (more or less):
main = socket.socket() # create the listening socket
main.bind((addr, port))
main.listen(5)
socks = [main] # initialize the list and optionaly count the accepted sockets
count = 0
while True:
r, w, x = select.select(socks, [], socks)
if main in r: # a new client
s, addr = main.accept()
if count == mx: # reject (optionaly) if max number of clients reached
s.close()
else:
socks.append(s) # appends the new socket to the list
elif len(r) > 0:
data = r[0].recv(1024) # an accepted socket is ready: read
if len(data) == 0: # nothing to read: close it
r[0].close()
socks.remove(r[0])
else:
for s in socks[1:]: # send the data to any other socket
if s != r[0]:
s.send(data)
elif main in x: # close if exceptional condition met (optional)
break
elif len(x) > 0:
x[0].close()
socks.remove(x[0])
# if the loop ends, close everything
for s in socks[1:]:
s.close()
main.close()
You will certainly need to implement a mechanism to ask the server to stop, and to test all that but it should be a starting place
This my final program and works like a charm.
Server.py
import socket, select
class Server:
def __init__(self, ip = "", port = 5050):
'''Server Constructor. If __init__ return None, then you can use
self.error to print the specified error message.'''
#Error message.
self.error = ""
#Creating a socket object.
self.server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
#Trying to bind it.
try:
self.server.bind( (ip, port) )
pass
#Failed, because socket has been shuted down.
except OSError :
self.error = "The server socket has been shuted down."
#Failed, because socket has been forcibly reseted.
except ConnectionResetError:
self.error = "The server socket has been forcibly reseted."
#Start Listening.
self.server.listen()
#_____Other Variables_____#
#A flag to know when to shut down thread loops.
self.running = True
#Store clients here.
self.sockets = [self.server]
#_____Other Variables_____#
#Start Handling the sockets.
self.handleSockets()
#Handle Sockets.
def handleSockets(self):
while True:
r, w, x = select.select(self.sockets, [], self.sockets)
#If server is ready to accept.
if self.server in r:
client, address = self.server.accept()
self.sockets.append(client)
#Elif a client send data.
elif len(r) > 0:
#Receive data.
try:
data = r[0].recv( 1024 )
#If the client disconnects suddenly.
except ConnectionResetError:
r[0].close()
self.sockets.remove( r[0] )
print("A user has been disconnected forcible.")
continue
#Connection has been closed or lost.
if len(data) == 0:
r[0].close()
self.sockets.remove( r[0] )
print("A user has been disconnected.")
#Else send the data to all users.
else:
#For all sockets except server.
for client in self.sockets[1:]:
#Do not send to yourself.
if client != r[0]:
client.send(data)
server = Server()
print("Errors:",server.error)
Client.py
import socket, threading
from tkinter import *
class Client:
def __init__(self, ip = "192.168.1.3", port = 5050):
'''Client Constructor. If __init__ return None, then you can use
self.error to print the specified error message.'''
#Error message.
self.error = ""
#Creating a socket object.
self.server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
#Trying to bind it.
try:
self.server.connect( (ip, port) )
pass
#Failed, because socket has been shuted down.
except OSError :
self.error = "The client socket has been shuted down."
return
#Failed, because socket has been forcibly reseted.
except ConnectionResetError:
self.error = "The client socket has been forcibly reseted."
return
#Failed, because socket has been forcibly reseted.
except ConnectionRefusedError:
self.error = "The server socket refuses the connection."
return
#_____Other Variables_____#
#A flag to know when to shut down thread loops.
self.running = True
#_____Other Variables_____#
#Start the GUI Interface.
def startGUI(self):
#Initialiazing tk.
screen = Tk()
screen.geometry("200x100")
#Tk variable.
self.msg = StringVar()
#Creating widgets.
entry = Entry( textvariable = self.msg )
button = Button( text = "Send", command = self.sendMSG )
#Packing widgets.
entry.pack()
button.pack()
screen.mainloop()
#Send the message.
def sendMSG(self):
self.server.send( str.encode( self.msg.get() ) )
#Receive message.
def recvMSG(self):
while self.running:
data = self.server.recv(1024)
print( bytes.decode(data) )
#New client.
main = Client()
print("Errors:", main.error)
#Start a thread with the recvMSG method.
thread = threading.Thread(target = main.recvMSG)
thread.start()
#Start the gui.
main.startGUI()
#Close the connection when the program terminates and stop threads.
main.running = False
main.server.close()
The program works fine exactly as i wanted.
But i have some more questions.
r, w, x = select.select(self.sockets, [], self.sockets)
r is a list which contains all the ready sockets.
But i did not undesrand what w and x are.
The first parameter is the sockets list, the second the accepted clients
and the third parameter what is it? Why am i giving again the sockets list?
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()