aberant / osc-ruby

Open Sound Control Library for Ruby
MIT License
94 stars 16 forks source link

Teach server how to act as a client #7

Closed samaaron closed 5 years ago

samaaron commented 11 years ago

This essentially allows the server to act as a client. This is extremely useful in the situations where you wish to send a message to an external OSC server and then listen to responses from it (which it sends back down the UDPSocket).

For example, when communicating with the SuperCollider server, it's useful to send the "/notify" command, which then allows you to receive information from the server.

server = OSC::Server.new 5440

server.add_method "/done" do |m|
  puts "done!"
end

Thread.new {server.run}

scsynth_port = 6555
server.send(OSC::Message.new("/notify", 1).encode, "127.0.0.1", scsynth_port))
aberant commented 11 years ago

How is this any different than instantiating a client object like this and then sending a message?

client = OSC::Client.new( "127.0.0.1", 6555 )
client.send( OSC::Message.new( "/notify" , "1" ))
samaaron commented 11 years ago

Hi @aberant,

How would you handle bidirectional comms via your approach? It's very typical for OSC servers to reply by sending messages back via the channel it received something. The approach I took was to create a server which listens for these replies and then added client abilities so it could send messages (in order to receive replies). Do you see an alternative approach?

aberant commented 11 years ago

@samaaron, I'm confused here, because you are specifying a different port for the client which leads me to believe they aren't the same socket.

can you provide me with an example of the supercollider app you are interfacing with so i can test my approach?

samaaron commented 11 years ago

As far as I understood, the client port is the port the remote endpoint is listening on. My understanding of these things isn't completely 100%, so I'm happy to be educated!

I'm just interfacing with a vanilla SuperCollider server. Just start it up and fire messages at it. In my case the SuperCollider server was listening on port 6555.

samaaron commented 11 years ago

As far as I understand it, all UDP packets have a source port and destination port as part of their header. For clients just firing out UDP packets, the source port is either 0 or an ephemeral port number - these packets are essentially one way as it's impossible to reply back to them. If the source port has been bound to and is listened to, then that may be used by the server receiving the UDP packet to reply back to. By explicitly creating an OSC server and using that to send the messages, this correctly sets the source port of the outgoing UDP packet to that of the OSC server, which can be used to read replies to that packet.

aberant commented 11 years ago

I've never programmed in SuperCollider, so i'm not sure what a vanilla server is. If I can get this code, it would give me a greater understanding of your use case.

samaaron commented 11 years ago

There's no need to program any SuperCollider - just start the server from the command line :-)

scsynth -u 4555

aberant commented 11 years ago

let me mess with this a bit.. i'll probably have something by this weekend

aberant commented 11 years ago

okay.. so i learned a bit about scsynth and how it uses /notify and the udp source port problem. let me think up a solution to this.

aberant commented 11 years ago

so here's an example of what i'm thinking. In my idea, i've got a new client class that also accepts a socket that the server provides. BiDirectionalClient is pretty wordy, any ideas on a better name?

https://github.com/aberant/osc-ruby/compare/client_can_accept_socket

samaaron commented 11 years ago

Why the need to create a separate class?

aberant commented 11 years ago

I tried putting the "optional socket argument" in the OSC::Client class, but wasn't happy with all the conditionals i needed in that class if a socket was passed in or not. Typically, numerous conditionals mean i'm trying to do too much with one class.

samaaron commented 11 years ago

I still don't understand why you can't just treat a server as a client. UDP makes no such distinction. I think adding a send method to the Server class is far cleaner.

aberant commented 11 years ago

UDP makes no such distinction

Right, but these client/server classes here are operating at the OSC abstraction level not UDP. I want to keep the client/server jobs distinct at the OSC level.

I'm in the middle of an OSC1.1 spike where i have to support transports such as TCP and SLIP in addition to UDP. At such point, i'll have a better abstraction for a communication channel that i can pass around, that isn't just a plain socket object. I'll probably have even more clients then, depending on the transport mechanism. It probably means i'll need to create some sort of factory to simplify client creation, but i'm getting ahead of myself here.

maximillion90 commented 10 years ago

aberant i tried your additions to osc-ruby as it seems like exactly what I need, and while it is fixing the sending port which is what i need it appears to alter the message encoding, when i compare it to the original OSC::Message send i get different packet results and only the old version returns data from the endpoint. Any advice?

maximillion90 commented 10 years ago

Apologies, i found the issue it was me missing something.