C++ Networking

How would you write asynchronous, TCP networking code with FRC libraries or add external networking libraries to an FRC project? It can’t use networking tables since it needs to be low latency for our navigation code.
I am aware these already exist in java, I am looking for C++.

Is this for communication between processes, threads, or different computers? A thread-safe queue works well for interthread comms. IPC can use a shared memory buffer or sockets. Different machines basically limits you to sockets.

Does it need to be asynchronous? At FRC-scale, I feel like you’re going to have a simpler time writing synchronous code. Creating a thread per-connection is fine for a small number of connections.

1 Like

Two different machines

The concern is that a) synchronous networking code may block the main thread because of how BSD sockets work or B) spwans a ton of unnecessary threads and does some disgusting things to work between them. We’ve already tried both of these methods and neither work very well and are easy to debug.

It’s hard to advise further without more information about the project.

Two devices: The RIO and the NVIDIA Jetson Nano. The jetson is connected to a real sense camera using SLAM which gets piped through some math running on the GPU that (afaik) calculates a spline and where the robot is to send to the RIO where the robot needs to go. My job is to connect the RIO and the jetson with minimal latency to make the robot as accurate as possible.

Just to double check… Have you confirmed that network tables running at 20ms is not sufficient to fulfill the requirements?

2 Likes

My intuition is that you can still take a synchronous approach. I’m going to assume decent BSD sockets knowledge.

On the Jetson side, you could have a thread accepting connections and adding the remote sockets to a vector. In the processing thread, iterate over that vector and send the position data to each socket. If you get an ECONNRESET, remove the socket from the vector. You can use a mutex to control access to the vector between the connection accepting thread and the processing thread. The concurrency primitives shouldn’t be too disgusting.

You can probably do something non-blocking on the RIO side. I’m guessing you want to periodically use the data from the Jetson to do something. If you connect to the Jetson with the SOCK_NONBLOCK flag, you can recv until you get an EGAIN or EWOULDBLOCK. Whatever the most recent data you receive before you get one of those errors will be your most up-to-date data. This should be pretty trivial to do if your position data has a fixed size. It should also happen pretty quickly as you’re essentially draining the system’s queue of position data that built up in-between the periodic action.

You could probably drop in a third party async solution on the Jetson side if you reallllllly want.

Setting it to async returns ewouldblock 99% of the time, which was another approach we tried. I would write async bsd sockets but I fancy having ungouged eyes.

If you’re getting EWOULDBLOCK then you don’t have any new data coming in. I guess I’m not understanding the problem? Maybe I can write some sample code this evening to verify my assumptions.

Thanks for the tip. That, embarrassingly, was the issue.

Also I didn’t see your question, the issue is not that it isn’t sufficient, the issue is that the faster it is the more accurate our odometry is, so it is worth putting in effort to write custom networking code.

For your use case (coprocessor to Rio), NetworkTables can have nearly as low of latency as raw sockets–just call NetworkTablesInstance.flush() after updating the NetworkTables values. That will immediately send any queued data, and should reliably result in <1 ms transmit latency.

FYI, if you don’t want to write raw async sockets code yourself, we do also have libuv packaged with the C++ wpiutil library.

5 Likes

Thank you! This is exactly what I was looking for.

This topic was automatically closed 365 days after the last reply. New replies are no longer allowed.