pyliblo
pyliblo copied to clipboard
Connection problems with string URIs
If multiple connections are made between a client and a server using a string as the address only the first message will be sent.
The example code below illustrates the issue, when the line is commented out it is broken (the server only receives 1 message), when enabled it works (receiving 5).
I believe this is to do with the fact that connections are cached on the lo_address, which in C you would free to break the connection and allow new ones to form. There's no way to do this from pyliblo at present, and in any case it wouldn't be very pythonic.
A possible solution would be to not expose the Address object in pyliblo and to handle the connection caching in the cython.
Server:
import liblo
s = liblo.Server(9992, liblo.TCP)
s.add_method(
None, None, lambda *a, **k: print('msg', a, k))
for _ in range(5):
s.recv()
Client:
import liblo
s = liblo.Server(9991, liblo.TCP)
a = 'osc.tcp://localhost:9992'
#a = liblo.Address(a)
for i in range(5):
s.send(a, '/shutup', i)
I can reproduce the problem you're describing. Interestingly the messages seem to be received/dispatched eventually if I call recv() often enough. That is, if I replace the for-loop on the server side with a while True, after several hundred calls to recv() (which return True, but don't dispatch any messages) the messages actually do arrive.
I may have just been lucky though, because if I insert small delays (e.g. time.sleep(0.01)) between calls to recv(), some of the messages are again lost... It looks like something is still very broken in liblo's TCP implementation.
If I'm not mistaken even liblo's C API has no way to explicitly disconnect a TCP connection. I guess it makes (some) sense to have the connection's lifetime tied to a lo_address, but if possible I'd like to avoid implementing something in pyliblo that relies on undocumented behavior in liblo.
When the last reference to a liblo.Address object goes away, lo_address_free() will be called immediately. So you should be able to force a disconnect by doing del a, assuming there are no references to the same address object elsewhere (pyliblo doesn't keep any, as far as I can tell).
When liblo.send() is called with a string address it creates a temporary Address object, that is destroyed when the function returns. Presumably that's why a new TCP connection must be established for each message.
Tried porting something similar to C and using raw liblo. Very similar issues are seen with 0.28, however with HEAD it behaves much better.
If I'm not mistaken even liblo's C API has no way to explicitly disconnect a TCP connection.
I'd be happy to implement something, but not sure of the best way to indicate which connection should be disconnected. I'd love to know if this is still a problem and if it can be reproduced using liblo in C.