FRC allows the use of TCP ports 5800-5810 for our team use. I would like to use them to send arbitrary data from my driver station laptop, to the roboRIO. This may sound trivial at first, write a TCP client on the driver station, write a server on the roboRIO, and start sending data.
There is a problem with this though:
The address of the roboRIO is determined by DCHP. This makes it impossible to know for sure what address to connect to from the laptop. I think the driver station software gets around this by using mDNS, but there’s no support for mDNS in something like winsock.
Along with this, I can’t find anywhere in the documentation how to run code asynchronously, which is required for blocking network code.
I also can’t find any WPI support for networking, although there seems to be some standard Linux networking that works.
Here is my code on both ends, both are taken almost entirely from examples:
Client:
#define WIN32_LEAN_AND_MEAN
#include <WinSock2.h>
#include <WS2tcpip.h>
#include <iostream>
#include <string>
// link with Ws2_32.lib
#pragma comment(lib, "Ws2_32.lib")
#define DEFAULT_PORT "5805"
#define DEFAULT_BUFFER_LENGTH 512
class Remote {
public:
Remote(char* servername)
{
szServerName = servername;
ConnectSocket = INVALID_SOCKET;
}
bool Start() {
WSADATA wsaData;
// Initialize Winsock
int iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
if (iResult != 0)
{
printf("WSAStartup failed: %d
", iResult);
return false;
}
printf("WSA Started up
");
struct addrinfo *result = NULL,
*ptr = NULL,
hints;
ZeroMemory(&hints, sizeof(hints));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
// Resolve the server address and port
iResult = getaddrinfo(szServerName, DEFAULT_PORT, &hints, &result);
if (iResult != 0)
{
printf("getaddrinfo failed: %d
", iResult);
WSACleanup();
return false;
}
printf("Got the address...
");
ptr = result;
// Create a SOCKET for connecting to server
ConnectSocket = socket(ptr->ai_family, ptr->ai_socktype, ptr->ai_protocol);
if (ConnectSocket == INVALID_SOCKET)
{
printf("Error at socket(): %d
", WSAGetLastError());
freeaddrinfo(result);
WSACleanup();
return false;
}
printf("Made a socket
");
// Connect to server
iResult = connect(ConnectSocket, ptr->ai_addr, (int)ptr->ai_addrlen);
if (iResult == SOCKET_ERROR)
{
closesocket(ConnectSocket);
ConnectSocket = INVALID_SOCKET;
}
freeaddrinfo(result);
if (ConnectSocket == INVALID_SOCKET)
{
printf("Unable to connect to server!
");
WSACleanup();
return false;
}
printf("Connected to the server
");
return true;
};
// Free the resouces
void Stop() {
int iResult = shutdown(ConnectSocket, SD_SEND);
if (iResult == SOCKET_ERROR)
{
printf("shutdown failed: %d
", WSAGetLastError());
}
closesocket(ConnectSocket);
WSACleanup();
};
// Send message to server
bool Send(char* szMsg)
{
int iResult = send(ConnectSocket, szMsg, strlen(szMsg), 0);
if (iResult == SOCKET_ERROR)
{
printf("send failed: %d
", WSAGetLastError());
Stop();
return false;
}
return true;
};
// Receive message from server
bool Recv()
{
char recvbuf[DEFAULT_BUFFER_LENGTH];
int iResult = recv(ConnectSocket, recvbuf, DEFAULT_BUFFER_LENGTH, 0);
if (iResult > 0)
{
char msg[DEFAULT_BUFFER_LENGTH];
memset(&msg, 0, sizeof(msg));
strncpy(msg, recvbuf, iResult);
printf("Received: %s
", msg);
return true;
}
return false;
}
private:
char* szServerName;
SOCKET ConnectSocket;
};
Server:
#define MAX_SIZE 50
// Two socket descriptors which are just integer numbers used to access a socket
int sock_descriptor, conn_desc;
// Two socket address structures - One for the server itself and the other for client
struct sockaddr_in serv_addr, client_addr;
// Buffer to store data read from client
char buff[MAX_SIZE];
// Create socket of domain - Internet (IP) address, type - Stream based (TCP) and protocol unspecified
// since it is only useful when underlying stack allows more than one protocol and we are choosing one.
// 0 means choose the default protocol.
sock_descriptor = socket(AF_INET, SOCK_STREAM, 0);
// A valid descriptor is always a positive value
if (sock_descriptor < 0)
printf("Failed creating socket
");
// Initialize the server address struct to zero
bzero((char *) &serv_addr, sizeof(serv_addr));
// Fill server's address family
serv_addr.sin_family = AF_INET;
// Server should allow connections from any ip address
serv_addr.sin_addr.s_addr = INADDR_ANY;
// 16 bit port number on which server listens
// The function htons (host to network short) ensures that an integer is interpretted
// correctly (whether little endian or big endian) even if client and server have different architectures
serv_addr.sin_port = htons(5805);
// Attach the server socket to a port. This is required only for server since we enforce
// that it does not select a port randomly on it's own, rather it uses the port specified
// in serv_addr struct.
if (bind(sock_descriptor, (struct sockaddr *) &serv_addr, sizeof(serv_addr))
< 0)
printf("Failed to bind
");
// Server should start listening - This enables the program to halt on accept call (coming next)
// and wait until a client connects. Also it specifies the size of pending connection requests queue
// i.e. in this case it is 5 which means 5 clients connection requests will be held pending while
// the server is already processing another connection request.
listen(sock_descriptor, 5);
SmartDashboard::PutNumber("Waiting for connection...",1);
printf("Waiting for connection...
");
unsigned int size = sizeof(client_addr);
// Server blocks on this call until a client tries to establish connection.
// When a connection is established, it returns a 'connected socket descriptor' different
// from the one created earlier.
conn_desc = accept(sock_descriptor, (struct sockaddr *) &client_addr,
&size);
if (conn_desc == -1)
printf("Failed accepting connection
");
else
printf("Connected
");
// The new descriptor can be simply read from / written up just like a normal file descriptor
if (read(conn_desc, buff, sizeof(buff) - 1) > 0)
printf("Received %s", buff);
else
printf("Failed receiving
");
Thanks!