how to handle variable length message and close the socket automatically? - python

I have the following case:
SERVER
sock = socket.socket()
sock.bind((hostaddr, port))
sock.listen(backlog)
print(f'Server listenning on {hostaddr}:{port}')
while True:
client_sock, client_address = self.sock.accept()
print(f'Incoming connection from {client_address[0]}:{client_address[1]}')
while True:
data = client_socket.recv(buffer_size)
if not data:
break
print(f'Received "{data.decode()}" from {client_address[0]}:{client_address[1]}')
reply = f'Server: I got the message "{data.decode()}"'.encode()
client_socket.sendall(reply)
client_socket.close()
CLIENT
sock = socket.socket()
sock.connect(server_address)
sock.sendall('Lorem Ipsum'.encode())
while True:
data = sock.recv(buffer_size)
if not data:
break
print(data.decode())
sock.close()
I first start the server, then I start the client, and I get the following logs:
SERVER
Server listening on 172.16.0.110:8081
Incoming connection from 172.16.0.110:62388
Received "Lorem Ipsum" from 172.16.0.110:62388
CLIENT
Server reply: I got the message "Lorem Ipsum"
I wanted to get the server reply and then the client should finish, but both server and client enter an infinite loop and keep running forever. Why and how to fix that? I'm using Python 3.6.0 on Windows 10 x64 in an IPv4 network.

You must define a protocol, which is just the rules for how messages are exchanged and formatted, and how message boundaries are communicated. It appears you simply want the client to send some data and read the server response. You can achieve this by closing the write half of the client connection, in your case by calling sock.shutdown(socket.SHUT_WR), after the sock.sendall(...).
On the server side this is the read half of the same connection, and the servers detects this as EOF, causing socket.recv() to return a zero-length bytes object.
For more complicated protocols for which you want to send multiple messages on the same connection, a different strategy must be used. One simple example for a binary protocol would be to send 4 bytes representing the length in bytes of the message, then send that many bytes for the subsequent message itself.

One way is to set a timeout for the socket so it doesn't block forever when waiting for reply using socket.settimeout() as the following:
sock = socket.socket()
sock.connect(server_address)
sock.sendall('Lorem Ipsum'.encode())
sock.settimeout(5.0) # sets timeout to 5 seconds
while True:
data = sock.recv(buffer_size)
if not data:
break
print(data.decode())
sock.close()

Related

Ruby client and Python server not receiving the full message while exchanging

I am trying to establish a client-server communication. The client is written in Ruby whereas the server is written in Python.
client.rb
require 'socket'
hostname = 'localhost'
port = 7778
s = TCPSocket.open(hostname, port)
s.write("2020-06-25T11:11:00+00:00 5 127.0.0.1 printer: event")
while line = s.gets
puts line.chop
end
s.close()
The ruby client sends a log to the Python server and tries to receive it back.
server.py
import socket
#Create a TCP/IP socket
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
#bind the socket to the port - tuple
server_address = ('localhost', 7778)
print('starting up on %s port %s' %server_address)
sock.bind(server_address)
#Listen for incoming connections
sock.listen(1)
while True:
print('waiting for a connection')
connection, client_address = sock.accept()
while True:
data = connection.recv(1024)
print('received "%s"' % data)
if data:
print('sending data back to the client')
connection.send(data)
else:
print('no more data from', client_address)
break
connection.close()
The log is sent to the python server and when the python server sends it back. When the ruby client receives it, it doesn't receive the full log.
example:
2020-06-25T11:11:00+00:00 5 127.0.0.1 printer: eve
I think this comes from the fact that TCP is a streaming protocol and we never know if we can get the full message each time.
Could you propose me a solution for both the client and the server so I can be sure they always receive the full message between each other? I would really appreciate it if anyone would help.
So the issue is that you're assuming the data received has a new line character - however the data you're sending is not terminated by a new line.
s.write("2020-06-25T11:11:00+00:00 5 127.0.0.1 printer: event") will not write the string with a new line character - you should use puts IO#puts
s.gets will return the data because the socket is closed by the python server after it has sent the data. So even getssays it will read the next line from the socket, in reality its just reading what remained in the buffer after the socket was closed.
line.chop will remove the last character, and you're using it here to strip a newline character (assuming that it has one from gets). However since there is no newline character it will remove the last character instead.
So the fix would be to replace in the ruby client s.write with s.puts.

Python Bidirectional TCP Socket Hanging on socket.recv

Referencing this example (and the docs): https://pymotw.com/2/socket/tcp.html I am trying to achieve bidirectional communication with blocking sockets between a client and a server using TCP.
I can get one-way communication to work from client->server or server->client, but the socket remains blocked or "hangs" when trying to receive messages on both the server and client. I am using a simple algorithm(recvall), which uses recv, to consolidate the packets into the full message.
I understand the sockets remain blocked by design until all the data is sent or read(right?), but isn't that what sendall and recvall take care of? How come disabling recv on either the client or server "unblocks" it and causes it to work? And ultimately what am I doing wrong that is causing the socket to stay blocked?
Here is my code, the only fundamental difference really being the messages that are sent:
recvall(socket)(shared between client and server):
def recvall(socket):
data = ''
while True:
packet = socket.recv(16)
if not packet: break
data += packet
return data
server.py (run first):
import socket
host = 'localhost'
port = 8080
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind((host, port))
s.listen(5)
while True:
(client, address) = s.accept()
print 'client connected'
try:
print recvall(client)
client.sendall('hello client')
finally:
client.close()
client.py:
import socket
s = socket.create_connection((args.ip, args.port))
try:
s.sendall('hello server')
print recvall(s)
finally:
s.close()
From my understanding (epiphany here), the main problem is that recv inside recvall is only concerned with retrieving the stream (in the same way send is only concerned with sending the stream), it has no concept of a "message" and therefore cannot know when to finish reading. It read all the bytes and did not return any additional bytes, but that is NOT a signal that the message is finished sending, there could be more bytes waiting to be sent and it would not be safe to assume otherwise.
This requires us to have an explicit indicator for when to stop reading. recv and send are only concerned with managing the stream and therefore have no concept of a message (our "unit"). This article has some great solutions to this problem. Since I am sending fixed-length messages, I opted to check that the length is as expected before finishing recv. Here is the updated version of recvall, note MSG_LENGTH must be defined and enforced in order for recvall to not block the socket.
def recvall(socket):
data = ''
while len(data) < MSG_LENGTH:
packet = socket.recv(BUFFER_SIZE)
if not packet: break
data += packet
return data
Bidirectional communication now works, the only catch being the client and server must know the length of the message they will receive, again this is not an issue in my case. This is all new to me so someone please correct me on terminology and concepts.

Why client socket connection is not closed after all data received from server?

I'm learning socket programming in python,
Server Code:
import socket
srvsock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
srvsock.bind(('', 23000))
srvsock.listen(5)
while True:
clisock, (rem_host, rem_port) = srvsock.accept()
print "conection established with host %s and port %s" % (rem_host, rem_port)
while True:
strg = clisock.recv(20)
if not strg:
print 'conection closed'
clisock.close()
break
clisock.send(strg)
Client Code:
import socket
clisock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
clisock.connect(('', 23000))
clisock.send("Hello World rrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrr dsadsadsa tttttt\n")
while True:
data = clisock.recv(20)
print type(data)
if not data:
clisock.close()
break
print data
I'm sending data stream from client to server and at the same time receiving data from server, after successful data transmission, the server not closing client connection. Did I miss any thing ?
The issue is caused because the server keeps reading data from the client until it reads no data. This only happens when the connected client closes its connection. Until then, the server socket will block (i.e. temporarily suspend operations) until the client sends more data.
Bottom line: either the client or the server has to indicate that it no longer intends to send data over the connection.
You can fix the client by adding the line
clisock.shutdown(socket.SHUT_WR)
before the for loop in the client. This indicates that no more data will be sent.
server code:
while True:
clisock, (rem_host, rem_port) = srvsock.accept()
print "conection established with host %s and port %s" % (rem_host, rem_port)
while True:
strg = clisock.recv(20)
print '[message from client:] %s'%strg
clisock.send(strg)
print 'about to close with client'
clisock.close()
print 'connection closed with client'
break
client code :
clisock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
clisock.connect(('', 23000))
clisock.send("Hello World rrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrr dsadsadsa tttttt\n")
while True:
data = clisock.recv(20)
print '[Message from server :]%s'%data
print 'about to close server connection'
clisock.close()
print 'server connection closed'
break
This will work out in your case, holdenweb has proper a proper answer why your code is not behaving as expected in above code client only sends one message and closes the connection as well as server listens only for one message per client and closes connection to that client single client -- single connection ---- single message

Communicating with multiple clients using one TCP socket python

I am using TCP sockets to communicate between my server and clients. The server code and socket code are as below:
server:
from socket import *
HOST = 'xx.xx.xx.xx'
PORT = 1999
serversocket = socket(AF_INET,SOCK_STREAM)
serversocket.bind((HOST,PORT))
print 'bind success'
serversocket.listen(5)
print 'listening'
while True:
(clientsocket, address) = serversocket.accept()
print ("Got client request from",address)
#clientsocket.send('True')
data = clientsocket.recv(1024)
print data
clientsocket.send('True')
clientsocket.close()
client:
import socket
import sys
# Create a TCP/IP socket
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# Connect the socket to the port on the server given by the caller
server_address = ('xx.xx.xx.xx', 1999)
print >>sys.stderr, 'connecting to %s port %s' % server_address
sock.connect(server_address)
try:
message = 'This is the message. It will be repeated.'
print >>sys.stderr, 'sending'
for x in range (0,1):
name=raw_input ('what is ur name')
print type(name)
sock.send(name)
print sock.recv(1024)
finally:
sock.close()
I am able to communicate with the server from client and able to send and receive data. But the problem I am facing is that I am not able to send and receive data continuously from the server. I have to restart my client code on my laptop to send and receive data again from the server. The way the above client code is working is that when I give a keyboard input, then the socket sends data to server and server responds back. But in the client code, in the for loop if I do two iterations, for the second iteration the data I enter from keyboard is not reaching server. I need to restart my client code to send data again. How do I fix this ?
Also, when once client is connected to the server, the other cannot connect to the server. Any ideas on how to do this ?
You need to design and implement a protocol that specifies what each side is supposed to do and then implement that protocol. You're expecting it to work by magic.
For example:
data = clientsocket.recv(1024)
I suspect you are expecting this to receive a "message". But TCP has no notion of messages. If you need to send and receive messages, you need to define precisely what a "message" is for your protocol and write code to send and receive them.
It may be helpful to look at the specifications for other protocols that use TCP such as HTTP, FTP, or IRC. It really is worth the time to write out a specification of your protocol before you write any code. It will save a lot of pain.

Python client disconnect if server closes connection

I have server and client code in python in which client sends a request message to server and wait for the response. I have the server code to close the connection when the client doesn't send a proper request. When the server closes the request, the client still is listening forever for the response.
Below is the code
server.py
s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s.bind((host, port))
s.listen(1)
while True:
c, addr = s.accept()
# Receive request
data = c.recv(1024)
if data != 'something'
c.close()
else
c.sendall("message"+"End")
c.close()
s.close()
Client.py
End='End'
def recv_end(the_socket):
# Snippet from http://code.activestate.com/recipes/408859-socketrecv-three-ways-to-turn-it-into-recvall/
total_data=[];data=''
while True:
data=the_socket.recv(8192)
if End in data:
total_data.append(data[:data.find(End)])
break
total_data.append(data)
if len(total_data)>1:
#check if end_of_data was split
last_pair=total_data[-2]+total_data[-1]
if End in last_pair:
total_data[-2]=last_pair[:last_pair.find(End)]
total_data.pop()
break
return ''.join(total_data)
s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s.connect((host, port))
s.send("some_request")
data = s.recv_end(1024)
print "<---- " + str(data)
s.close()
I'm new to python and wondering if there is a way for the client to know that the server closed the connection and terminate.
I understand that the client would disconnect if I use normal s.recv(1024). But in my case, I need to send large data to the client so I have used a different function I found from http://code.activestate.com/recipes/408859-socketrecv-three-ways-to-turn-it-into-recvall/.
Is it like the server sends a empty string reply during the close of a connection and in my case, it would send a empty string without the end limiter and hence the client is listening forever ?
When you have a loop with recv or anything that reads from a socket or a pipe, you should stop reading as soon as you get a buffer with len 0 :
while True:
data=the_socket.recv(8192)
if len(data) == 0: break
...

Categories