I've been looking and dealing with this issue for a week. I have client code that causes select() to return a socket that has actually closed from external reasons throwing an error 9 BAD FILE DESCRIPTOR, however I tested the code from a different python file and CANNOT get it to error. Ive tried a million things. heres a snippet from the server:
NOTE: This will work for a few iterations and then suddenly break, it errors out in the message_queue as key error due to the file descriptor breaking even tho a message/no message has a key for that socket present.
#Create the socket to communicate with uWSGI applications
server_address = ('localhost', 10001)
server = create_server_socket(server_address)
#Sockets which we expect to read on from select()
input_sockets = [server]
#Sockets which we expect to write to from select()
output_sockets = []
#Message buffer dicitonary for outgoing messages
message_queue = {}
#Now wait for connections endlessly
while input_sockets:
print >> sys.stderr, "Waiting for the next event..."
readable, writable, exceptional = select.select(input_sockets, output_sockets, input_sockets)
#Handle input_sockets
for s in readable:
#Server socket is available for reading now
if s is server:
#Create a connection and address object when incoming request is recieved
connection, client_addr = s.accept()
print >> sys.stderr, "Connection recieved from %s!" % (client_addr,)
#Set client connection to non blocking as well
connection.setblocking(0)
#Add this socket to input sockets as it will read for client data
input_sockets.append(connection)
#Give connection a queue for sending messages to it
message_queue[connection] = Queue.Queue()
#A client has sent data so we can handle its request
else:
#Pull data from the client
data = ""
try:
while True:
message = s.recv(1024)
if not message:
break
data += message
except Exception as e:
print str(e)
if data:
#Readable client socket has data
print >> sys.stderr, 'Recieved "%s" from %s' % (data, s.getpeername())
message_queue[s].put(data)
#Add output channel now to send message
if s not in output_sockets:
output_sockets.append(s)
#There is no data to be read, socket must be closed
else:
print >> sys.stderr, 'Closing', client_addr,'after recieving no data.'
#Stop listening for input on the socket
if s in output_sockets:
output_sockets.remove(s)
input_sockets.remove(s)
#Close the connection
s.close()
del message_queue[s]
#Handle writable connections
for s in writable:
if s:
try:
next_message = message_queue[s].get_nowait()
except:
print >> sys.stderr, 'No data to send for', s.getpeername()
output_sockets.remove(s)
else:
try:
print >> sys.stderr, 'Sending "%s" to %s' % (next_message, s.getpeername())
s.sendall(next_message)
except:
print >> sys.stderr, 'No data to send for', s.getpeername()
output_sockets.remove(s)
#s.sendall('EOF:!##$:EOF')
#Now handle any exceptions
for s in exceptional:
print >> sys.stderr, 'Handling exception on ', s.getpeername()
input_sockets.remove(s)
if s in output_sockets:
output_sockets.remove(s)
s.close()
#Remove any messages
del message_queue[s]
client:
messages = [ 'This is the message. ',
'It will be sent ',
'in parts.',
]
server_address = ('localhost', 10001)
# Create a TCP/IP socket
socks = [ socket.socket(socket.AF_INET, socket.SOCK_STREAM),
socket.socket(socket.AF_INET, socket.SOCK_STREAM),
]
# Connect the socket to the port where the server is listening
print >>sys.stderr, 'connecting to %s port %s' % server_address
for s in socks:
s.connect(server_address)
for message in messages:
# Send messages on both sockets
for s in socks:
print >>sys.stderr, '%s: sending "%s"' % (s.getsockname(), message)
s.send(message)
# Read responses on both sockets
for s in socks:
data = s.recv(1024)
print >>sys.stderr, '%s: received "%s"' % (s.getsockname(), data)
if not data:
print >>sys.stderr, 'closing socket', s.getsockname()
s.close()
NOTE: This client side is only to test and start passing messages.
There is a race in your code when a socket is returned as both readable and writable and you close the socket because the read returned 0 bytes. In this case you remove the socket from input_sockets, output_sockets and message_queue but the closed socket is still in writable and it will thus try to write to it inside the same iteration of the select loop.
I have no idea if this is the race you'll see because you neither show debug output not did you say where you stumble over this EBADF. To track similar problems down I recommend to augment your code with more debug information on where you close a socket and where you try to process a socket because it is readable or writable so that you actually find the exact place of the race when looking at the debug output.
Related
So I'm trying to get an erlang server started, which will echo back from my python client. I can see the connection is made, however the echo doesn't actually happen. Can anybody point me to the right solution?
I'm using python3 as my client driver.
Here is my erlang server: I start it with echo:accept(6000).
-module(echo).
-export([accept/1]).
%% Starts an echo server listening for incoming connections on
%% the given Port.
accept(Port) ->
{ok, Socket} = gen_tcp:listen(Port, [binary, {active, true}, {packet, line}, {reuseaddr, true}]),
io:format("Echo server listening on port ~p~n", [Port]),
server_loop(Socket).
%% Accepts incoming socket connections and passes then off to a separate Handler process
server_loop(Socket) ->
{ok, Connection} = gen_tcp:accept(Socket),
Handler = spawn(fun () -> echo_loop(Connection) end),
gen_tcp:controlling_process(Connection, Handler),
io:format("New connection ~p~n", [Connection]),
server_loop(Socket).
%% Echoes the incoming lines from the given connected client socket
echo_loop(Connection) ->
receive
{tcp, Connection, Data} ->
gen_tcp:send(Connection, Data),
echo_loop(Connection);
{tcp_closed, Connection} ->
io:format("Connection closed ~p~n", [Connection])
end.
here is my python 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 where the server is listening
server_address = ('localhost', 6000)
print(sys.stderr, 'connecting to %s port %s' % server_address)
sock.connect(server_address)
try:
# Send data
message = 'This is the message. It will be repeated.'
convertedString = message.encode('utf-8')
print(sys.stderr, 'sending "%s"' % message)
sock.sendall(convertedString)
# Look for the response
amount_received = 0
amount_expected = len(message)
while amount_received < amount_expected:
data = sock.recv(16).decode('utf-8')
amount_received += len(data)
print(sys.stderr, 'received "%s"' % data)
finally:
print(sys.stderr, 'closing socket')
sock.close()
I think the problem is it just hangs after sending, and now it's waiting for a response, I think I might not be receiving the string in the right way.
One issue is that you have {packet, line} and the message does not include a new line, so the echo server keeps waiting for the message to be completed before sending it to the handler.
Also, you should be careful with the active option, as any data that is received during the controlling_process/2 call will remain in the previous handler. You should start the accepted socket with {active, false} and then set it to true | pos_integer() when the socket is managed by the handler.
I have a script that has successfully run for a long time. Recently one of my servers was upgraded into Server 2012 and the script is failing to run. It's purpose is to connect to a port and receive some data. The problem here looks like some weird characters that I receive back which I don't know where are they coming from. Any help would much appreciated.
FAILED:
"C:\Python27\python.exe" "C:\gui_update_client.py" hostname/IP address
10000 Filename Server: hostname/IP address, Port: 10000, Master:
Filename Connected to nn.nnn.nnn.nn on port 10000 GUI update request
sent: Filename Data received: €$Y{gï Data received: Socket error:
No data received.
Succesfull:
"C:\Python27\python.exe" "C:\gui_update_client.py" hostname/IP address
10000 Filename Server: hostname/IP address, Port: 10000, Master:
Filename Connected to hostname/IP address on port 10000 GUI update
request sent: Filename Data received: Keep alive
Keep alive received Data received: GUI update completed
Response read: GUI update completed
# Script Arguments
import sys
# Network Client
import socket
# Logging
import logging
# Other
import time
import os
TIMEOUT = 60
DELIM = '\n'
KEEP_ALIVE_MSG = 'Keep alive'
NO_UPDATES_FOUND = 'No GUI updates found'
UPDATE_COMPLETED = 'GUI update completed'
# Check required arguments
try:
server = sys.argv[1]
port = int(sys.argv[2])
master = sys.argv[3]
except:
print 'Usage: {0} server port master'.format(sys.argv[0])
sys.exit(1)
print 'Server: {0}, Port: {1}, Master: {2}'.format(server, port, master)
# Create a TCP/IP socket
sock = socket.create_connection((server, port))
sock.settimeout(TIMEOUT)
print 'Connected to {0} on port {1}'.format(server, str(port))
# Send request and get response
response = ''
try:
# Send GUI update request
sock.sendall(master + DELIM)
print 'GUI update request sent: {0}'.format(master)
# Read the response
data = ''
while response == '':
# Read the response
split = ''
while split != DELIM:
# Buffer the data in small chunks
datum = sock.recv(1024)
print 'Data received: {0}'.format(datum)
if datum == '':
raise socket.error('No data received.')
data += datum
(response, split, remainder) = data.partition(DELIM)
# Check for keep alive response
while response == KEEP_ALIVE_MSG:
print 'Keep alive received'
data = remainder
(response, split, remainder) = data.partition(DELIM)
if split != DELIM:
response = ''
# Response received
print 'Response read: {0}'.format(response)
except socket.timeout as err:
print 'Timeout error: {0}'.format(str(err))
except socket.error as err:
print 'Socket error: {0}'.format(str(err))
finally:
# Clean up the connection
sock.close()
logging.info('Connection to {0} on port {1} closed.'\
.format(server, str(port)))
if response == UPDATE_COMPLETED or response == NO_UPDATES_FOUND:
os.environ['ERRORLEVEL'] = "0"
exit()
else:
os.environ['ERRORLEVEL'] = "1"
exit(1)
Thank you in advance.
Thank you for having a look. I have found what the issue is here. The script is fine. The problem here is that windows listening port is on a different network. When the server was upgraded to 2012 it took as a default a different network card for the service running. That's why the reply comes with weird characters.
I'm trying to implement a timeout that terminates a python script when no connections are receiving for a defined time interval. So far I manage to implement the timeout using the following code:
import sys
import socket
# Create a TCP/IP socket
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# Bind the socket to the port
server_address = ('192.168.43.112', 5001)
print >>sys.stderr, 'starting up on %s port %s' % server_address
sock.bind(server_address)
# Listen for incoming connections
sock.listen(1)
while True:
try:
# Wait for a connection
print >>sys.stderr, 'waiting for a connection'
connection, client_address = sock.accept()
try:
print >>sys.stderr, 'connection from', client_address
# Receive the data in small chunks and retransmit it
while True:
data = connection.recv(16)
print >>sys.stderr, 'received "%s"' % data
if data:
print >>sys.stderr, 'Do stuff here'
else:
print >>sys.stderr, 'no more data from', client_address
sock.settimeout(5)
break
finally:
# Clean up the connection
connection.close()
except socket.timeout:
break
The code is working correctly in the sense that after establishing a connection and ending that very same connection, after 5 seconds the script terminates. However, if during the timeout window I try to make another connection I have the following error:
starting up on 192.168.43.112 port 5001
waiting for a connection
connection from ('192.168.43.1', 47550)
received "Data 0
"
Do stuff here
received ""
no more data from ('192.168.43.1', 47550)
waiting for a connection
connection from ('192.168.43.1', 39010)
---------------------------------------------------------------------------
error Traceback (most recent call last)
/Users/location/Desktop/sandbox/data_fetcher.py in <module>()
24 # Receive the data in small chunks and retransmit it
25 while True:
---> 26 data = connection.recv(16)
27 print >>sys.stderr, 'received "%s"' % data
28 if data:
error: [Errno 35] Resource temporarily unavailable
I'm not entirely sure how you want this all to work, and I find it a bit surprising that it happens this way right now (I didn't expect the timeout to have this effect), but based on the EAGAIN error (errno 35), what's happening is that the timeout on the main socket—which gets set only once you've had a first connection—is causing the second-accepted socket to be in non-blocking mode as well. This means that when you call connection.recv and there's no data immediately, you get that OSError raised.
I suspect some of this might vary a bit between OSes, but I was able to reproduces this on FreeBSD (you're probably running on Linux).
A minimal change that works around it—I don't think this is necessarily the best way to code this, but it does work—is to explicitly set the accepted socket to blocking:
# Wait for a connection
print >>sys.stderr, 'waiting for a connection'
connection, client_address = sock.accept()
connection.setblocking(1)
With this, the code behaves much better (I added a small test framework that spins off your code as a separate process, then makes several connections with varying delays).
I have a twisted server script listening on a unix socket and it receives the data when the client is in twisted but it doesn't work if i send it via a vanilla python socket code.
class SendProtocol(LineReceiver):
"""
This works
"""
def connectionMade(self):
print 'sending log'
self.sendLine(self.factory.logMessage)
if __name__ == '__main__':
address = FilePath('/tmp/test.sock')
startLogging(sys.stdout)
clientFactory = ClientFactory()
clientFactory.logMessage = 'Dfgas35||This is a message from server'
clientFactory.protocol = SendProtocol
port = reactor.connectUNIX(address.path, clientFactory)
reactor.run()
But this doesn't (server doesn't get any data)
sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
sock_addr = '/tmp/test.sock'
try:
sock.connect(sock_addr)
except socket.error, msg:
print >> sys.stderr, msg
sys.exit(1)
sock.setblocking(0) # not needed though tried both ways
print 'connected %s' % sock.getpeername()
print 'End END to abort'
while True:
try:
line = raw_input('Enter mesg: ')
if line.strip() == 'END':
break
line += '\n'
print 'sending'
sock.sendall(line)
finally:
sock.close()
Your two client programs send different data. One sends \r\n-terminated lines. The other sends \n-terminated lines. Perhaps your server is expecting \r\n-terminated lines and this is why the latter example doesn't appear to work. Your non-Twisted example also closes the socket after the first line it sends but continues with its read-send loop.
I have a problem, whenever I try to run this python script on an Raspberry PI:
import socket
import sys
# Create a TCP/IP socket
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# Bind the socket to the port
server_address = ('localhost', 10000)
print >>sys.stderr, 'starting up on %s port %s' % server_address
sock.bind(server_address)
# Listen for incoming connections
sock.listen(1)
while True:
# Wait for a connection
print >>sys.stderr, 'waiting for a connection'
connection, client_address = sock.accept()
try:
print >>sys.stderr, 'connection from', client_address
# Receive the data in small chunks and retransmit it
while True:
data = connection.recv(16)
print >>sys.stderr, 'received "%s"' % data
if data:
print >>sys.stderr, 'sending data back to the client'
connection.sendall(data)
else:
print >>sys.stderr, 'no more data from', client_address
break
finally:
# Clean up the connection
connection.close()
I get this error:
File "server.py", line 20
try:
^
IndentationError: unexpected indent
Could you please tell me what's wrong here? The script is supposed to create a simple TCP/IP server, and I have no such problems with the client, so I really don't understand where is my mistake/s...
One of the unfortunate side-effects of Python's use of whitespace for denoting blocks is that sometimes you get scripts that have tabs and spaces mixed up throughout the source code.
Since this script is pretty small, you could try deleting the whitespace preceding each line's code and then reindent it properly.