WPINet provides networking utilities built on libuv, including HTTP servers, WebSocket support, and multicast service discovery.
Overview
WPINet offers:
HTTP web server with WebSocket support
Network service announcement and discovery
TCP/UDP client and server utilities
Port forwarding
Network stream abstractions
WPINet is primarily a C++ library used internally by WPILib components like CSCore, NetworkTables, and the camera server.
Core Components
Web Server HTTP server with WebSocket support
WebSockets Bidirectional network communication
Service Discovery mDNS service announcement and resolution
Port Forwarding Forward ports between network interfaces
Web Server
Create HTTP web servers with WebSocket support.
Basic HTTP Server
#include <wpinet/WebServer.h>
#include <wpinet/HttpServerConnection.h>
class MyServer : public wpi :: HttpServerConnection {
public:
void ProcessRequest () override {
// Get request info
wpi ::StringRef path = GetUrl (). path ;
wpi ::StringRef method = GetMethod ();
if (path == "/" ) {
// Send response
SendResponse ( 200 , "OK" , "text/html" ,
"<html><body>Hello World</body></html>" );
} else if (path == "/api/data" ) {
// JSON response
SendResponse ( 200 , "OK" , "application/json" ,
"{ \" value \" : 42}" );
} else {
// 404 Not Found
SendResponse ( 404 , "Not Found" , "text/plain" , "Not Found" );
}
}
};
// Create and start server
auto server = std :: make_shared < wpi :: WebServer < MyServer >>( 8080 );
server -> Start ();
Serving Static Files
#include <wpinet/WebServer.h>
#include <wpi/FileSystem.h>
void ProcessRequest () override {
wpi ::StringRef path = GetUrl (). path ;
// Serve files from directory
fs ::path filePath = fs :: path ( "/www" ) / path . str ();
if ( fs :: exists (filePath) && fs :: is_regular_file (filePath)) {
// Read file
std ::error_code ec;
auto mapped = wpi :: MappedFileRegion :: Open (
filePath . string (),
wpi :: MappedFileRegion ::readonly,
ec);
if ( ! ec) {
// Determine content type
std ::string contentType = GetContentType ( filePath . extension ());
// Send file
SendResponse ( 200 , "OK" , contentType,
wpi :: StringRef ( mapped -> const_data (), mapped -> size ()));
} else {
SendResponse ( 500 , "Internal Error" , "text/plain" , "Error reading file" );
}
} else {
SendResponse ( 404 , "Not Found" , "text/plain" , "Not Found" );
}
}
std :: string GetContentType ( const std :: string & ext ) {
if (ext == ".html" ) return "text/html" ;
if (ext == ".css" ) return "text/css" ;
if (ext == ".js" ) return "application/javascript" ;
if (ext == ".json" ) return "application/json" ;
if (ext == ".png" ) return "image/png" ;
if (ext == ".jpg" || ext == ".jpeg" ) return "image/jpeg" ;
return "application/octet-stream" ;
}
WebSockets
WebSocket support for bidirectional communication.
WebSocket Server
#include <wpinet/HttpWebSocketServerConnection.h>
class MyWebSocket : public wpi :: HttpWebSocketServerConnection {
protected:
void ProcessWsUpgrade () override {
wpi ::StringRef protocol = GetProtocol ();
// Accept WebSocket upgrade
if (protocol == "my-protocol" ) {
AcceptWs ();
fmt :: print ( "WebSocket connected \n " );
} else {
RejectWs ( 400 , "Bad Request" );
}
}
void ProcessWsText ( std :: string_view data ) override {
fmt :: print ( "Received text: {} \n " , data);
// Echo back
SendWsText (data);
}
void ProcessWsBinary ( std :: span < const uint8_t > data ) override {
fmt :: print ( "Received {} bytes \n " , data . size ());
// Echo back
SendWsBinary (data);
}
void ProcessWsClose ( uint16_t code , std :: string_view reason ) override {
fmt :: print ( "WebSocket closed: {} - {} \n " , code, reason);
}
};
// Create WebSocket server
auto server = std :: make_shared < wpi :: WebServer < MyWebSocket >>( 8080 );
server -> Start ();
WebSocket Client
#include <wpinet/WebSocket.h>
class MyClient {
public:
void Connect () {
auto ws = std :: make_shared < wpi :: WebSocket >();
// Set callbacks
ws -> text . connect ([]( std :: string_view data , bool isLast ) {
fmt :: print ( "Received: {} \n " , data);
});
ws -> binary . connect ([]( std :: span < const uint8_t > data , bool isLast ) {
fmt :: print ( "Received {} bytes \n " , data . size ());
});
ws -> closed . connect ([]( uint16_t code , std :: string_view reason ) {
fmt :: print ( "Closed: {} \n " , reason);
});
// Connect
ws -> Connect ( "ws://10.12.34.2:8080/ws" , "my-protocol" );
// Send data
ws -> SendText ( "Hello Server" );
}
};
Service Discovery
Multicast DNS service announcement and discovery.
Service Announcer
#include <wpinet/MulticastServiceAnnouncer.h>
// Create announcer
wpi ::MulticastServiceAnnouncer announcer;
// Start announcing service
announcer . Start ();
announcer . Add (
"My Robot Camera" , // Service name
"_camera._tcp" , // Service type
8080 , // Port
{ // TXT records
{ "path" , "/stream" },
{ "format" , "mjpeg" }
}
);
// Stop announcing
announcer . Stop ();
Service Resolver
#include <wpinet/MulticastServiceResolver.h>
// Create resolver
wpi ::MulticastServiceResolver resolver;
// Set callback for service events
resolver . serviceData . connect (
[]( const wpi :: MulticastServiceResolver :: ServiceData & data ) {
fmt :: print ( "Service: {} at {}:{} \n " ,
data . serviceName , data . hostName , data . port );
for ( const auto & [key, value] : data . txt ) {
fmt :: print ( " {}: {} \n " , key, value);
}
});
// Start resolving
resolver . Start ( "_camera._tcp" );
// Stop resolving
resolver . Stop ();
Port Forwarding
Forward TCP ports between network interfaces.
#include <wpinet/PortForwarder.h>
wpi ::PortForwarder forwarder;
// Forward local port 5800 to remote 10.12.34.2:5800
forwarder . Add (
5800 , // Local port
"10.12.34.2" , // Remote host
5800 // Remote port
);
// Remove forwarding
forwarder . Remove ( 5800 );
Network Streams
TCP Client
#include <wpinet/NetworkStream.h>
#include <wpinet/raw_socket_istream.h>
#include <wpinet/raw_socket_ostream.h>
// Connect to server
std ::error_code ec;
auto stream = wpi :: NetworkStream :: connect (
"10.12.34.2" ,
8080 ,
wpi :: Logger :: GetInstance (),
ec);
if ( ! ec) {
// Wrap socket in streams
wpi ::raw_socket_istream input{ stream . get ()};
wpi ::raw_socket_ostream output{ stream . get ()};
// Write data
output << "GET / HTTP/1.1 \r\n " ;
output << "Host: 10.12.34.2 \r\n " ;
output << " \r\n " ;
output . flush ();
// Read response
std ::string line;
while ( std :: getline (input, line)) {
fmt :: print ( "{} \n " , line);
}
}
UDP Client
#include <wpinet/UDPClient.h>
// Create UDP client
wpi ::UDPClient client{ "10.12.34.2" , 5800 };
// Send data
std ::string message = "Hello UDP" ;
client . send ( message . data (), message . size ());
// Receive data
char buffer [ 1024 ];
ssize_t received = client . receive (buffer, sizeof (buffer));
if (received > 0 ) {
fmt :: print ( "Received: {} \n " ,
std :: string_view (buffer, received));
}
URL Parsing
#include <wpinet/UrlParser.h>
// Parse URL
wpi ::UrlParser parser;
if ( parser . Parse ( "http://10.12.34.2:8080/path?query=value" )) {
fmt :: print ( "Scheme: {} \n " , parser . GetScheme ());
fmt :: print ( "Host: {} \n " , parser . GetHost ());
fmt :: print ( "Port: {} \n " , parser . GetPort ());
fmt :: print ( "Path: {} \n " , parser . GetPath ());
fmt :: print ( "Query: {} \n " , parser . GetQuery ());
}
HTTP Utilities
MIME Types
#include <wpinet/MimeTypes.h>
// Get MIME type from file extension
std ::string_view mime = wpi :: GetMimeTypeFromPath ( "/path/to/file.json" );
// Returns: "application/json"
URL Encoding
#include <wpinet/HttpUtil.h>
// URL encode
std ::string encoded = wpi :: UrlEncode ( "hello world" );
// Returns: "hello%20world"
// URL decode
std ::string decoded = wpi :: UrlDecode ( "hello%20world" );
// Returns: "hello world"
// Escape HTML
std ::string html = wpi :: EscapeHtml ( "<script>alert('xss')</script>" );
Network Acceptor
Accept incoming TCP connections.
#include <wpinet/NetworkAcceptor.h>
class MyConnectionHandler {
public:
void operator () ( uv :: Stream & stream ) {
fmt :: print ( "New connection \n " );
// Handle connection
auto conn = std :: make_shared < MyConnection >( stream . shared_from_this ());
}
};
// Create acceptor
wpi :: NetworkAcceptor < MyConnectionHandler > acceptor (
8080 ,
wpi :: Logger :: GetInstance ());
acceptor . Start ();
Event Loop
Event Loop Runner
#include <wpinet/EventLoopRunner.h>
// Create event loop
wpi ::EventLoopRunner loop;
// Execute on loop
loop . ExecAsync ([]( uv :: Loop & loop ) {
// Code runs on event loop thread
fmt :: print ( "Running on loop \n " );
});
DS Client
Driver Station network protocol client.
#include <wpinet/DsClient.h>
// Create DS client
wpi ::DsClient client;
// Set callbacks
client . controlWord . connect ([]( const wpi :: ControlWord & word ) {
fmt :: print ( "Enabled: {} \n " , word . GetEnabled ());
fmt :: print ( "Autonomous: {} \n " , word . GetAutonomous ());
});
client . allianceStation . connect ([]( wpi :: AllianceStation station ) {
fmt :: print ( "Alliance: {} \n " , static_cast < int > (station));
});
// Start client
client . Start ();
Hostname Resolution
#include <wpinet/hostname.h>
// Get hostname
std ::string hostname = wpi :: GetHostname ();
fmt :: print ( "Hostname: {} \n " , hostname);
WPINet is built on libuv and supports:
Linux (including Raspberry Pi)
macOS
Windows
roboRIO (NI Linux RT)
Common Use Cases
Camera Streaming
// CSCore uses WPINet for camera streaming
#include <cscore.h>
cs ::MjpegServer server{ "server" , 8080 };
server . SetSource (camera);
// Uses WPINet HTTP server internally
NetworkTables Communication
// NetworkTables uses WPINet for network communication
#include <networktables/NetworkTableInstance.h>
nt ::NetworkTableInstance inst = nt :: NetworkTableInstance :: GetDefault ();
inst . StartClient4 ( "client" );
inst . SetServerTeam ( 1234 );
// Uses WPINet TCP/UDP internally
Source Code
View the full source code on GitHub:
CSCore Camera streaming using WPINet
NetworkTables Network communication protocol
WPIUtil Common utilities and data structures