Reputation: 2488
I'm playing around with socket programming in Python 2 and trying out select()
for the server script. When I have the following code for the server:
print('Server started in port {}.'.format(self.port))
server_socket = socket.socket()
server_socket.bind((self.address, self.port))
server_socket.listen(5)
client_sockets = [server_socket]
while True:
for s in client_sockets:
if s is server_socket:
client_socket, address = server_socket.accept()
client_sockets.append(client_socket)
print('Connection received.')
else:
data = s.recv(200)
if data:
print('Received: {}'.format(data.decode().strip()))
else:
client_sockets.remove(s)
s.close()
The server only receives the first message from the client. However, the second and later messages will only be received when the client is restarted. This baffles me (of which I attribute to my inadequate knowledge in networking). The data seems to be buffered. Why does this happen?
I did try this:
client_sockets = [server_socket]
while True:
readable, writable, errored = select.select(client_sockets, [], [])
for s in readable:
if s is server_socket:
...
And finally, the server can now receive the second and later messages from the client.
Here is the code for the client:
class BasicClient(object):
def __init__(self, name, address, port):
self.name = name
self.address = address
self.port = int(port)
self.socket = socket.socket()
def connect(self):
self.socket.connect((self.address, self.port))
self.socket.send(self.name)
def send_message(self, message):
self.socket.send(message.encode())
args = sys.argv
if len(args) != 4:
print "Please supply a name, server address, and port."
sys.exit()
client = BasicClient(args[1], args[2], args[3])
client.connect()
while True:
message = raw_input('Message: ')
# We pad the message by 200 since we only expect messages to be
# 200 characters long.
num_space_pads = min(200 - len(message), 200)
message = message.ljust(num_space_pads, ' ')
client.send_message(message)
Upvotes: 0
Views: 391
Reputation: 12205
There are many problems here.
The key problem is that socket.accept()
is a blocking call. You accept your client connection, then read from it, but then your for loop processes back to the server socket and your code is stuck in accept. Only when you reconnect to the socket does the server code move forward, hence it appears you only receive one message.
The better way to do this is to use threads. In your main thread wait for connections (always waiting in accept). When a connection appears, accept it then spawn a thread and handle all client traffic there. The thread then blocks waiting only for the client socket and eventually terminates on connection closed. Much simpler and less prone to errors.
There is also the problem of the concept of "message", as TCP sockets do not know anything about your message structure. They just transmit a stream of data, whatever that is. When you send a "message" and read from a TCP socket, one of the following happens on the server side:
You need to always cater for cases 3-5 (or use zeromq or something else that understands the concept of a message if you do not want to implement this yourself), not only 1 and 2.
When reading data from a TCP socket, it is always necessary to validate it. Have a "process" buffer and append what you read to that. Then start parsing from the beginning and process as many complete messages as you can find, delete these from the buffer and leave the remainder there. The assumption then is the partial message will eventually be completed.
Upvotes: 2