Python socket.recv with MSG_DONTWAIT - python-3.x

I'm almost always receiving on a socket in blocking mode and this works fine. Very occasionally I don't want to wait - if there is data on the socket I want it now, otherwise I will try again later.
I thought I could do this using the flags argument to socket.recv(), but it seems not to work. I can achieve the effect I want using the socket.setblocking() and socket.settimeout() calls, but this seems clumsy.
From the python socket documentation the flags argument takes the same meanings as for Unix recv:
MSG_DONTWAIT (since Linux 2.2)
Enables nonblocking operation; if the operation would block, the
call fails with the error EAGAIN or EWOULDBLOCK. This provides
similar behavior to setting the O_NONBLOCK flag (via the fcntl(2)
F_SETFL operation), but differs in that MSG_DONTWAIT is a per-call
option, whereas O_NONBLOCK is a setting on the open file description
(see open(2)), which will affect all threads in the calling process
and as well as other processes that hold file descriptors referring
to the same open file description.
I read this to mean I could pass socket.MSG_DONTWAIT to get non-blocking operation on that call only. Possibly this isn't correct - I could also read this as it would always return an error as the call in principle would be blocking. In which case, this is all irrelevant.
Some example code:
A simple blocking call. As expected, this takes about 0.5s.
MSG_DONTWAIT: I hoped would be a non-blocking call, returning very quickly. Actually this also took about 0.5s
Non-blocking by re-configuring the port. This actually takes about 50us, so it clearly isn't blocking in the way the first two calls were.
import socket
import time
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
sock.settimeout(0.5)
starttime = time.time()
try:
m = sock.recv(100)
except socket.timeout as e:
pass
endtime = time.time()
print(f'sock.recv(100) took {endtime-starttime}s') # 0.5s
starttime = time.time()
try:
m = sock.recv(100, socket.MSG_DONTWAIT)
except socket.timeout as e:
pass
endtime = time.time()
print(f'sock.recv(100, socket.MSG_DONTWAIT) took {endtime-starttime}s') # 0.5s
starttime = time.time()
timeout = sock.gettimeout()
sock.setblocking(0)
try:
m = sock.recv(100)
except BlockingIOError as e:
pass
sock.settimeout(timeout)
endtime = time.time()
print(f'sock.recv(100) with non-blocking set took {endtime-starttime}s') # 4.96e-5s
Questions:
Am I just wrong about the use of MSG_DONTWAIT? Should it work in the way I am trying to use it?
Is there a better way to toggle blocking and non-blocking calls to recv()

Regarding "1. Am I just wrong about the use of MSG_DONTWAIT? Should it work in the way I am trying to use it?":
No, you are not wrong, but there is a small issue with the way you test. Specifically, your
MSG_DONTWAIT test is for a blocking socket with a timeout of 0.5s. This is because you have sock.settimeout(0.5) before your first test (perhaps you overlooked that this affects your second test).
If I update the exception type in your MSG_DONTWAIT test (which is a another indication that the socket is blocking) and try in a "clean" session, I get what you expect you would get:
>>> import socket
>>> import time
>>>
>>> sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
>>> starttime = time.time()
>>> try:
... m = sock.recv(100, socket.MSG_DONTWAIT)
... except BlockingIOError as e:
... pass
...
>>> endtime = time.time()
>>> print(f'sock.recv(100, socket.MSG_DONTWAIT) took {endtime-starttime}s')
sock.recv(100, socket.MSG_DONTWAIT) took 0.0007114410400390625s
If I "forget" to exclude sock.settimeout(0.5), I get a socket.timeout exception after 0.5s:
>>> import socket
>>> import time
>>>
>>> sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
>>> sock.settimeout(0.5) # <= see this
>>>
>>> starttime = time.time()
>>> try:
... m = sock.recv(100, socket.MSG_DONTWAIT)
... except socket.timeout as e:
... pass
...
>>> endtime = time.time()
>>>
>>> print(f'sock.recv(100, socket.MSG_DONTWAIT) took {endtime-starttime}s')
sock.recv(100, socket.MSG_DONTWAIT) took 0.501746416091919s
Regarding "2. Is there a better way to toggle blocking and non-blocking calls to recv()": depending on the needs of your application, you may want to take a look at select (and the "Non-blocking Sockets" section in Socket Programming HOWTO and this)

Related

A way to send multiple parameter to async socket function?

After a lot of trials with httpio, requests, socket I'm seeing that no matter how I approach the solution it still behaves like a synchronous program even with an async function. the code is -
import socket
import time
import asyncio
t1 = time.time()
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
target = '72.221.171.130'
#async socket to connect to the target with a given port
async def connect(port):
try:
s.connect((target, port))
print(port)
except Exception as e:
print(e)
loop = asyncio.get_event_loop()
loop.run_until_complete(asyncio.wait([connect(port) for port in range(4100, 4150)]))
#printing time taken to scan ports two decimal places
print('Time taken to scan ports:', round((time.time() - t1),3), 'seconds')
I looking for the open port which is 4145. but this takes around 20 seconds to execute. I'm a beginner in async programming so I would really appreciate your help.

Python avoid partial writes with non-blocking write to named pipe

I am running python3.8 on linux.
In my script, I create a named pipe, and open it as follows:
import os
import posix
import time
file_name = 'fifo.txt'
os.mkfifo(file_name)
f = posix.open(file_name, os.O_RDWR | os.O_NONBLOCK)
os.set_blocking(f, False)
Without yet having opened the file for reading elsewhere ( for instance, with cat), I start to write to the file in a loop.
base_line = 'abcdefghijklmnopqrstuvwxyz'
s = base_line * 10000 + '\n'
while True:
try:
posix.write(f, s.encode())
except BlockingIOError as e:
print("Exception occurred: {}".format(e))
time.sleep(.5)
When I then go to read from the named pipe with cat, I find that there was a partial-write that took place.
I am confused how I can know how many bytes were written in this instance. Since the exception was thrown, I do not have access to the return value (num bytes written). The documentation suggests that BlockingIOError has a property called characters_written, however when I try to access this field an AttributeError is raised.
In summary: How can I either avoid this partial write in the first place, or at least know how much was partially written in this instance?
os.write performs an unbuffered write. The docs state that BlockingIOError only has a characters_written attribute when a buffered write operation would block.
If any bytes were successfully written before the pipe became full, that number of bytes will be returned from os.write. Otherwise, you'll get an exception. Of course, something like a drive failure will also cause an exception, even if some bytes were written. This is no different from how POSIX write works, except instead of returning -1 on error, an exception is raised.
If you don't like dealing with the exception, you can use a wrapper around the file descriptor, such as a io.FileIO object. I've modified your code since it tries to write the entire buffer every time you looped back to the os.write call (if it failed once, it will fail every time):
import io
import os
import time
base_line = 'abcdefghijklmnopqrstuvwxyz'
data = (base_line * 10000 + '\n').encode()
file_name = 'fifo.txt'
os.mkfifo(file_name)
fd = os.open(file_name, os.O_RDWR | os.O_NONBLOCK)
# os.O_NONBLOCK makes os.set_blocking(fd, False) unnecessary.
with io.FileIO(fd, 'wb') as f:
written = 0
while written < len(data):
n = f.write(data[written:])
if n is None:
time.sleep(.5)
else:
written += n
BTW, you might use the selectors module instead of time.sleep; I noticed a slight delay when trying to read from the pipe because of the sleep delay, which shouldn't happen if you use the selectors module:
with io.FileIO(fd, 'wb') as f:
written = 0
sel = selectors.DefaultSelector()
sel.register(f, selectors.EVENT_WRITE)
while written < len(data):
n = f.write(data[written:])
if n is None:
# Wait here until we can start writing again.
sel.select()
else:
written += n
sel.unregister(f)
Some useful information can also be found in the answer to POSIX named pipe (fifo) drops record in nonblocking mode.

socket ssl error when using threading with python3.7

I have so been working on something for the past few days, and i now am in the final steps which is adding multiprocessing or multithreading. After seeing that pickling SSLSocket objects in multiprocessing is not easy, i decided to go with multithreading ( i chose this also because its for making web requests which is I/O). I added the threading part to my code, and if i only start one single thread, it works fine, but after adding 2 threads, it starts throwing errors at me that i have never seen before.
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s = ssl.wrap_socket(s, keyfile=None, certfile=None, server_side=False, cert_reqs=ssl.CERT_NONE, ssl_version=ssl.PROTOCOL_SSLv23)
t1 = threading.Thread(target=check, args=(s,))
t1.start()
t2 = threading.Thread(target=check, args=(s,))
t2.start()
this is in the if name_== main portion of my code. I put this here so when i called my other functions i could pass the socket into the function and reuse the connection. Here is my function:
def check(socket):
for x in range(5):
uid_data = []
socket.settimeout(.5)
socket.send()
while True:
try:
response = socket.recv(4094)
uid_data.append(response)
except Exception as e:
break
let me start of by saying that this code works perfectly without threading/processes. So i know its not my code. I dont really know whats going on because it works for around 3-4 attempts then itll error. Here is the traceback:
return self._sslobj.write(data)
OSErrorreturn self._sslobj.write(data):
[Errno 0] ErrorOSError
: [Errno 0] Error
This is from the line socket.send(), (its what the traceback says). Why is it doing this when i try to run multiple threads?

Python3 ZMQ, Interrupt function and calling another on each new message received

Here is my problem : I have 2 programs communicating thanks to zmq on an arbitrary tcp port.
When the #1 receives message from #2 he has to call some function.
If #1 receives a message before the current function ends, I'd like #1 to interrupt the current function and call the new one.
I tried to use threading.Event to interrupt function.
I don't know if zmq is the right option for my needs or if the socket types fine.
To simplify I show the simplest version possible,here is what I tried :
p1.py
import zmq
from threading import Event
port_p2 = "6655"
context = zmq.Context()
socket = context.socket(zmq.PAIR)
socket.connect("tcp://localhost:%s" % port_p2)
print("port 6655")
__exit1 = Event()
__exit2 = Event()
def action1():
__exit1.clear()
__exit2.set()
while not __exit1.is_set():
for i in range(1, 20):
print(i)
time.sleep(1)
__exit1.set()
def action2():
__exit2.clear()
__exit1.set()
while not __exit2.is_set():
for i in range(1, 20):
print(i * 100)
time.sleep(1)
__exit2.set()
if __name__ == "__main__":
try:
while True:
try:
string = socket.recv(flags=zmq.NOBLOCK)
# message received, process it
string = str(string, 'utf-8')
if "Action1" in string:
action1()
if "Action2" in string:
action2()
except zmq.Again as e:
# No messages waiting to be processed
pass
time.sleep(0.1)
except(KeyboardInterrupt, SystemExit):
print("exit")
and p2.py
import time
import random
port_p1 = "6655"
context = zmq.Context()
socket_p1 = context.socket(zmq.PAIR)
socket_p1.bind("tcp://*:%s" % port_p1)
print("port 6655")
if __name__ == "__main__":
while True:
i = random.choice(range(1, 10))
print(i)
try:
if random.choice([True, False]):
print("Action 1")
socket_p1.send(b'Action1')
else:
socket_p1.send(b'Action2')
print("Action 2")
except zmq.Again as e:
pass
time.sleep(i)
For my purpose I didn't want / can't use system signals
I'd appreciate any input and don't hesitate to ask for precision, I have to confess that I had trouble writing this down.
Thank you
Q : …like #1 to interrupt the current function…
Given you have forbidden to use signals, #1 can but passively signal (be it over the present ZeroMQ infrastructure or not) the function, not to continue further and return in a pre-mature fashion ( so the fun() has to get suitably modified for doing that active re-checking, best in some reasonably granular progressive fashion, regularly checking actively the #1 if did passively signal ( "tell" the fun() ) to RET early, due to whatever reason and way the #1 had and used to do that ).
The other chance is to extend the already present ZeroMQ infrastructure ( the Context()-instance(s) ) with a socket-monitor artillery and make the fun() .connect()-directly to the socket-monitor resources to actively learn about any new message arriving to #1 ( i.e. autonomously, without #1's initiative ) and deciding to return in a pre-mature fashion, in those cases, where feasible according to your application logic.
For the socket-monitor case, the API documentation has all details needed for implementation, which would otherwise go way beyond the scope of the Stack Overflow post.

How to set timeout for a block of code which is not a function python3

After spending a lot of hours looking for a solution in stackoverflow, I did not find a good solution to set a timeout for a block of code. There are approximations to set a timeout for a function. Nevertheless, I would like to know how to set a timeout without having a function. Let's take the following code as an example:
print("Doing different things")
for i in range(0,10)
# Doing some heavy stuff
print("Done. Continue with the following code")
So, How would you break the for loop if it has not finished after x seconds? Just continue with the code (maybe saving some bool variables to know that timeout was reached), despite the fact that the for loop did not finish properly.
i think implement this efficiently without using functions not possible , look this code ..
import datetime as dt
print("Doing different things")
# store
time_out_after = dt.timedelta(seconds=60)
start_time = dt.datetime.now()
for i in range(10):
if dt.datetime.now() > time_started + time_out:
break
else:
# Doing some heavy stuff
print("Done. Continue with the following code")
the problem : the timeout will checked in the beginning of every loop cycle, so it may be take more than the specified timeout period to break of the loop, or in worst case it maybe not interrupt the loop ever becouse it can't interrupt the code that never finish un iteration.
update :
as op replayed, that he want more efficient way, this is a proper way to do it, but using functions.
import asyncio
async def test_func():
print('doing thing here , it will take long time')
await asyncio.sleep(3600) # this will emulate heaven task with actual Sleep for one hour
return 'yay!' # this will not executed as the timeout will occur early
async def main():
# Wait for at most 1 second
try:
result = await asyncio.wait_for(test_func(), timeout=1.0) # call your function with specific timeout
# do something with the result
except asyncio.TimeoutError:
# when time out happen program will break from the test function and execute code here
print('timeout!')
print('lets continue to do other things')
asyncio.run(main())
Expected output:
doing thing here , it will take long time
timeout!
lets continue to do other things
note:
now timeout will happen after exactly the time you specify. in this example code, after one second.
you would replace this line:
await asyncio.sleep(3600)
with your actual task code.
try it and let me know what do you think. thank you.
read asyncio docs:
link
update 24/2/2019
as op noted that asyncio.run introduced in python 3.7 and asked for altrnative on python 3.6
asyncio.run alternative for python older than 3.7:
replace
asyncio.run(main())
with this code for older version (i think 3.4 to 3.6)
loop = asyncio.get_event_loop()
loop.run_until_complete(main())
loop.close()
You may try the following way:
import time
start = time.time()
for val in range(10):
# some heavy stuff
time.sleep(.5)
if time.time() - start > 3: # 3 is timeout in seconds
print('loop stopped at', val)
break # stop the loop, or sys.exit() to stop the script
else:
print('successfully completed')
I guess it is kinda viable approach. Actual timeout is greater than 3 seconds and depends on the single step execution time.

Resources