Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add TCP port information to NetAddr #12

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions rfcs/0000-add-tcp-port-information-netaddr.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
- Title: Add TCP port information to NetAddr
- Date proposed: 2020-07-20
- RFC PR: https://github.com/supercollider/rfcs/pull/0012

# Summary

Currently it is not possible to directly access an open TCP socket from sclang. This RFC proposes a way to access the metadata of the socket created when calling NetAddr.connect method so the user can listen to messages coming back from that socket.

# Motivation

One of the differences between TCP and UDP is that in UDP the connecton is usually not kept alive. The sender of a message simply sends the UDP package and does not bother if the package arrives, is actually sent, or even if the host exists. In TCP a connection between both points must be made before any packages are sent, and this connection is kept open until one of the ends closes it. This usually means that the sender opens a random port locally to receive incoming data from the other end of the TCP connection. One of the advantages of TCP is that a host behind a NAT or a complex network routing can open a connection to a public IP on the internet and, because the connection is two-way, the host receiving the connection can send messages back through the opened channel without the need to know the route or how to solve NAT routing and so on. This is not possible with UDP, when both ends need to know the IP of each other to exchange messages.
Copy link
Member

@telephon telephon Jul 20, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When receiving a UDP message, we also receive the sender address:

OSCdef(\x, { |msg, time, addr| addr.postln }, '/test').add;
NetAddr("127.0.0.1", NetAddr.langPort).sendMsg('/test');

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the problem is if both peers are not in the same network or if the IP of the sender is not directly accessible. example:

I'm behind a router with internet IP 194.42.123.98, my computer local IP is 192.168.8.42 and I send a UDP package to a public IP / host (for example if there is a server listening on scsynth.org port 57120).

The package arrives there with my router IP (194.42.123.98) the software running on scsynth.org tries to send me an UDP message back using that IP, but my router has no idea about where it should send this package to inside the network unless I configure Port Forward in the router (telling it that any packages arriving on 57120 should be sent to 192.168.8.42).

With TCP the connection is kept on and the route between scsynth.org and my computer inside the network is still reachable.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd suggest not making assumptions about peoples NAT situation. As an aside, in normally working NAT replies should be correctly delivered because of the routing table. It's making initial connections to computers behind NAT that usually requires configuration.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, but a UDP reply (as in the scenario I described) is basically making an initial connection to a computer behind NAT.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, normally when an internal address and port sends to an external one, replies are expected and routed accordingly. Otherwise no external to internal messages would be possible.

We did a remote recording for NIME this week with a shared server and Utopia Registrar that worked exactly like this. Port forwarding was only needed on the NAT at the server end as all clients sent out a /notify message. It's possible something else is going on in your situation (like a very short translation timeout), and I know there are some exotic forms of NAT, but normally the translation table just takes care of this.

TCP on its own of course will not solve the NAT problem.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, you are correct @muellmusik that the translation will occur and I didn't know that sendMsg will open that port locally. So I tried now and it works:

OSCdef(\x, { |msg, time, addr| addr.postln }, '/test').add

and then on my machine:

NetAddr("bgo.la", 57120).sendMsg('/test');

and on bgo.la I get:

a NetAddr(95.91.208.191, 9435)

If I send back from bgo.la to that address in port 9435 I am able to get it back on my machine. So, somehow NetAddr is opening the port to listen. I guess the issue with TCP then is that once you do NetAddr.connect it won't register OSCFunc/Def to that TCP port?


While it is possible to open TCP connections from sclang using `NetAddr.connect` currently it is not possible to get any information about the opened socket, making it difficult to listen back to messages sent on that channel. For example in the [OSCRouterClient Quark](https://github.com/aiberlin/HyperDisCo/blob/master/Classes/OSCRouterClient.sc#L95) it needs to call `this.addOSCRecvFunc` with a generic function that will listen to any incoming message on any port, and add some information on the application protocol layer (in this case the randomId assigned when the first call to the other end of the connection is made) so we can check back and assign the correct TCP port to that socket.

The proposal in this RFC is to expose the metadata of the socket, especially the port it listens to, so we can do things like `n = NetAddr(addr, port); OSCFunc({}, n.tcpRecvPort);` and directly listen to messages received in that that opened socket.

# Specification

Adds the instance attribute `tcpRecvPort` to `NetAddr`, an integer representing the locally opened port for a TCP connection. It is `nil` by default and is only set after `.connect` or `.tryConnectTCP` is called.

# Drawbacks


# Unresolved Questions

Are there other information to be added to `NetAddr` that might be useful in the context of TCP sockets?