Move common utility classes to wpiutil library. (#79)

This is a breaking change to dependencies that use the static ntcore
library.  Unless the wpiutil library is also linked, linker errors will
result.  This does not affect the shared ntcore library.
This commit is contained in:
Peter Johnson
2016-09-25 17:23:39 -07:00
committed by GitHub
parent 80e546b79f
commit f6b700ea97
55 changed files with 16 additions and 6 deletions

View File

@@ -0,0 +1,31 @@
/*----------------------------------------------------------------------------*/
/* Copyright (c) FIRST 2015. All Rights Reserved. */
/* Open Source Software - may be modified and shared by FRC teams. The code */
/* must be accompanied by the FIRST BSD license file in the root directory of */
/* the project. */
/*----------------------------------------------------------------------------*/
#include "tcpsockets/SocketError.h"
#ifdef _WIN32
#include <windows.h>
#else
#include <string.h>
#endif
namespace wpi {
std::string SocketStrerror(int code) {
#ifdef _WIN32
LPSTR errstr = nullptr;
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
0, code, 0, (LPSTR)&errstr, 0, 0);
std::string rv(errstr);
LocalFree(errstr);
return rv;
#else
return strerror(code);
#endif
}
} // namespace wpi

View File

@@ -0,0 +1,189 @@
/*
TCPAcceptor.cpp
TCPAcceptor class definition. TCPAcceptor provides methods to passively
establish TCP/IP connections with clients.
------------------------------------------
Copyright © 2013 [Vic Hargrave - http://vichargrave.com]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#include "tcpsockets/TCPAcceptor.h"
#include <cstdio>
#include <cstring>
#ifdef _WIN32
#include <WinSock2.h>
#pragma comment(lib, "Ws2_32.lib")
#else
#include <arpa/inet.h>
#include <netinet/in.h>
#include <unistd.h>
#include <fcntl.h>
#endif
#include "llvm/SmallString.h"
#include "support/Logger.h"
#include "tcpsockets/SocketError.h"
using namespace wpi;
TCPAcceptor::TCPAcceptor(int port, const char* address, Logger& logger)
: m_lsd(0),
m_port(port),
m_address(address),
m_listening(false),
m_logger(logger) {
m_shutdown = false;
#ifdef _WIN32
WSAData wsaData;
WORD wVersionRequested = MAKEWORD(2, 2);
WSAStartup(wVersionRequested, &wsaData);
#endif
}
TCPAcceptor::~TCPAcceptor() {
if (m_lsd > 0) {
shutdown();
#ifdef _WIN32
closesocket(m_lsd);
#else
close(m_lsd);
#endif
}
#ifdef _WIN32
WSACleanup();
#endif
}
int TCPAcceptor::start() {
if (m_listening) return 0;
m_lsd = socket(PF_INET, SOCK_STREAM, 0);
if (m_lsd < 0) {
WPI_ERROR(m_logger, "could not create socket");
return -1;
}
struct sockaddr_in address;
std::memset(&address, 0, sizeof(address));
address.sin_family = PF_INET;
if (m_address.size() > 0) {
#ifdef _WIN32
llvm::SmallString<128> addr_copy(m_address);
addr_copy.push_back('\0');
int size = sizeof(address);
WSAStringToAddress(addr_copy.data(), PF_INET, nullptr, (struct sockaddr*)&address, &size);
#else
inet_pton(PF_INET, m_address.c_str(), &(address.sin_addr));
#endif
} else {
address.sin_addr.s_addr = INADDR_ANY;
}
address.sin_port = htons(m_port);
int optval = 1;
setsockopt(m_lsd, SOL_SOCKET, SO_REUSEADDR, (char*)&optval, sizeof optval);
int result = bind(m_lsd, (struct sockaddr*)&address, sizeof(address));
if (result != 0) {
WPI_ERROR(m_logger, "bind() failed: " << SocketStrerror());
return result;
}
result = listen(m_lsd, 5);
if (result != 0) {
WPI_ERROR(m_logger, "listen() failed: " << SocketStrerror());
return result;
}
m_listening = true;
return result;
}
void TCPAcceptor::shutdown() {
m_shutdown = true;
#ifdef _WIN32
::shutdown(m_lsd, SD_BOTH);
// this is ugly, but the easiest way to do this
// force wakeup of accept() with a non-blocking connect to ourselves
struct sockaddr_in address;
std::memset(&address, 0, sizeof(address));
address.sin_family = PF_INET;
llvm::SmallString<128> addr_copy;
if (m_address.size() > 0)
addr_copy = m_address;
else
addr_copy = "127.0.0.1";
addr_copy.push_back('\0');
int size = sizeof(address);
if (WSAStringToAddress(addr_copy.data(), PF_INET, nullptr,
(struct sockaddr*)&address, &size) != 0)
return;
address.sin_port = htons(m_port);
fd_set sdset;
struct timeval tv;
int result = -1, valopt, sd = socket(AF_INET, SOCK_STREAM, 0);
if (sd < 0) return;
// Set socket to non-blocking
u_long mode = 1;
ioctlsocket(sd, FIONBIO, &mode);
// Try to connect
::connect(sd, (struct sockaddr*)&address, sizeof(address));
// Close
::closesocket(sd);
#else
::shutdown(m_lsd, SHUT_RDWR);
int nullfd = ::open("/dev/null", O_RDONLY);
if (nullfd >= 0) {
::dup2(nullfd, m_lsd);
::close(nullfd);
}
#endif
}
std::unique_ptr<NetworkStream> TCPAcceptor::accept() {
if (!m_listening || m_shutdown) return nullptr;
struct sockaddr_in address;
#ifdef _WIN32
int len = sizeof(address);
#else
socklen_t len = sizeof(address);
#endif
std::memset(&address, 0, sizeof(address));
int sd = ::accept(m_lsd, (struct sockaddr*)&address, &len);
if (sd < 0) {
if (!m_shutdown)
WPI_ERROR(m_logger, "accept() failed: " << SocketStrerror());
return nullptr;
}
if (m_shutdown) {
#ifdef _WIN32
closesocket(sd);
#else
close(sd);
#endif
return nullptr;
}
return std::unique_ptr<NetworkStream>(new TCPStream(sd, &address));
}

View File

@@ -0,0 +1,189 @@
/*
TCPConnector.h
TCPConnector class definition. TCPConnector provides methods to actively
establish TCP/IP connections with a server.
------------------------------------------
Copyright © 2013 [Vic Hargrave - http://vichargrave.com]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License
*/
#include "tcpsockets/TCPConnector.h"
#include <errno.h>
#include <fcntl.h>
#include <cstdio>
#include <cstring>
#ifdef _WIN32
#include <WinSock2.h>
#include <WS2tcpip.h>
#else
#include <netdb.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/select.h>
#include <unistd.h>
#endif
#include "tcpsockets/TCPStream.h"
#include "llvm/SmallString.h"
#include "support/Logger.h"
#include "tcpsockets/SocketError.h"
using namespace wpi;
static int ResolveHostName(const char* hostname, struct in_addr* addr) {
struct addrinfo hints;
struct addrinfo* res;
hints.ai_flags = 0;
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = 0;
hints.ai_addrlen = 0;
hints.ai_addr = nullptr;
hints.ai_canonname = nullptr;
hints.ai_next = nullptr;
int result = getaddrinfo(hostname, nullptr, &hints, &res);
if (result == 0) {
std::memcpy(addr, &((struct sockaddr_in*)res->ai_addr)->sin_addr,
sizeof(struct in_addr));
freeaddrinfo(res);
}
return result;
}
std::unique_ptr<NetworkStream> TCPConnector::connect(const char* server,
int port, Logger& logger,
int timeout) {
#ifdef _WIN32
struct WSAHelper {
WSAHelper() {
WSAData wsaData;
WORD wVersionRequested = MAKEWORD(2, 2);
WSAStartup(wVersionRequested, &wsaData);
}
~WSAHelper() { WSACleanup(); }
};
static WSAHelper helper;
#endif
struct sockaddr_in address;
std::memset(&address, 0, sizeof(address));
address.sin_family = AF_INET;
if (ResolveHostName(server, &(address.sin_addr)) != 0) {
#ifdef _WIN32
llvm::SmallString<128> addr_copy(server);
addr_copy.push_back('\0');
int size = sizeof(address);
if (WSAStringToAddress(addr_copy.data(), PF_INET, nullptr, (struct sockaddr*)&address, &size) != 0) {
WPI_ERROR(logger, "could not resolve " << server << " address");
return nullptr;
}
#else
inet_pton(PF_INET, server, &(address.sin_addr));
#endif
}
address.sin_port = htons(port);
if (timeout == 0) {
int sd = socket(AF_INET, SOCK_STREAM, 0);
if (sd < 0) {
WPI_ERROR(logger, "could not create socket");
return nullptr;
}
if (::connect(sd, (struct sockaddr*)&address, sizeof(address)) != 0) {
WPI_ERROR(logger, "connect() to " << server << " port " << port << " failed: " << SocketStrerror());
#ifdef _WIN32
closesocket(sd);
#else
::close(sd);
#endif
return nullptr;
}
return std::unique_ptr<NetworkStream>(new TCPStream(sd, &address));
}
fd_set sdset;
struct timeval tv;
socklen_t len;
int result = -1, valopt, sd = socket(AF_INET, SOCK_STREAM, 0);
if (sd < 0) {
WPI_ERROR(logger, "could not create socket");
return nullptr;
}
// Set socket to non-blocking
#ifdef _WIN32
u_long mode = 1;
ioctlsocket(sd, FIONBIO, &mode);
#else
long arg;
arg = fcntl(sd, F_GETFL, nullptr);
arg |= O_NONBLOCK;
fcntl(sd, F_SETFL, arg);
#endif
// Connect with time limit
if ((result = ::connect(sd, (struct sockaddr*)&address, sizeof(address))) <
0) {
int my_errno = SocketErrno();
#ifdef _WIN32
if (my_errno == WSAEWOULDBLOCK || my_errno == WSAEINPROGRESS) {
#else
if (my_errno == EWOULDBLOCK || my_errno == EINPROGRESS) {
#endif
tv.tv_sec = timeout;
tv.tv_usec = 0;
FD_ZERO(&sdset);
FD_SET(sd, &sdset);
if (select(sd + 1, nullptr, &sdset, nullptr, &tv) > 0) {
len = sizeof(int);
getsockopt(sd, SOL_SOCKET, SO_ERROR, (char*)(&valopt), &len);
if (valopt) {
WPI_ERROR(logger, "select() to " << server << " port " << port << " error " << valopt << " - " << SocketStrerror(valopt));
}
// connection established
else
result = 0;
} else
WPI_INFO(logger, "connect() to " << server << " port " << port << " timed out");
} else
WPI_ERROR(logger, "connect() to " << server << " port " << port << " error " << SocketErrno() << " - " << SocketStrerror());
}
// Return socket to blocking mode
#ifdef _WIN32
mode = 0;
ioctlsocket(sd, FIONBIO, &mode);
#else
arg = fcntl(sd, F_GETFL, nullptr);
arg &= (~O_NONBLOCK);
fcntl(sd, F_SETFL, arg);
#endif
// Create stream object if connected, close if not.
if (result == -1) {
#ifdef _WIN32
closesocket(sd);
#else
::close(sd);
#endif
return nullptr;
}
return std::unique_ptr<NetworkStream>(new TCPStream(sd, &address));
}

View File

@@ -0,0 +1,159 @@
/*
TCPStream.h
TCPStream class definition. TCPStream provides methods to trasnfer
data between peers over a TCP/IP connection.
------------------------------------------
Copyright © 2013 [Vic Hargrave - http://vichargrave.com]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#include "tcpsockets/TCPStream.h"
#ifdef _WIN32
#include <WinSock2.h>
#else
#include <arpa/inet.h>
#include <netinet/tcp.h>
#include <unistd.h>
#endif
using namespace wpi;
TCPStream::TCPStream(int sd, sockaddr_in* address) : m_sd(sd) {
char ip[50];
#ifdef _WIN32
unsigned long size = sizeof(ip) - 1;
WSAAddressToString((sockaddr*)address, sizeof sockaddr_in, nullptr, ip, &size);
#else
inet_ntop(PF_INET, (in_addr*)&(address->sin_addr.s_addr), ip,
sizeof(ip) - 1);
#endif
m_peerIP = ip;
m_peerPort = ntohs(address->sin_port);
}
TCPStream::~TCPStream() { close(); }
std::size_t TCPStream::send(const char* buffer, std::size_t len, Error* err) {
if (m_sd < 0) {
*err = kConnectionClosed;
return 0;
}
#ifdef _WIN32
WSABUF wsaBuf;
wsaBuf.buf = const_cast<char*>(buffer);
wsaBuf.len = (ULONG)len;
DWORD rv;
bool result = true;
while (WSASend(m_sd, &wsaBuf, 1, &rv, 0, nullptr, nullptr) == SOCKET_ERROR) {
if (WSAGetLastError() != WSAEWOULDBLOCK) {
result = false;
break;
}
Sleep(1);
}
if (!result) {
char Buffer[128];
#ifdef _MSC_VER
sprintf_s(Buffer, "Send() failed: WSA error=%d\n", WSAGetLastError());
#else
std::snprintf(Buffer, 128, "Send() failed: WSA error=%d\n", WSAGetLastError());
#endif
OutputDebugStringA(Buffer);
*err = kConnectionReset;
return 0;
}
#else
ssize_t rv = write(m_sd, buffer, len);
if (rv < 0) {
*err = kConnectionReset;
return 0;
}
#endif
return static_cast<std::size_t>(rv);
}
std::size_t TCPStream::receive(char* buffer, std::size_t len, Error* err,
int timeout) {
if (m_sd < 0) {
*err = kConnectionClosed;
return 0;
}
#ifdef _WIN32
int rv;
#else
ssize_t rv;
#endif
if (timeout <= 0) {
#ifdef _WIN32
rv = recv(m_sd, buffer, len, 0);
#else
rv = read(m_sd, buffer, len);
#endif
}
else if (WaitForReadEvent(timeout)) {
#ifdef _WIN32
rv = recv(m_sd, buffer, len, 0);
#else
rv = read(m_sd, buffer, len);
#endif
} else {
*err = kConnectionTimedOut;
return 0;
}
if (rv < 0) {
*err = kConnectionReset;
return 0;
}
return static_cast<std::size_t>(rv);
}
void TCPStream::close() {
if (m_sd >= 0) {
#ifdef _WIN32
::shutdown(m_sd, SD_BOTH);
closesocket(m_sd);
#else
::shutdown(m_sd, SHUT_RDWR);
::close(m_sd);
#endif
}
m_sd = -1;
}
llvm::StringRef TCPStream::getPeerIP() const { return m_peerIP; }
int TCPStream::getPeerPort() const { return m_peerPort; }
void TCPStream::setNoDelay() {
int optval = 1;
setsockopt(m_sd, IPPROTO_TCP, TCP_NODELAY, (char*)&optval, sizeof optval);
}
bool TCPStream::WaitForReadEvent(int timeout) {
fd_set sdset;
struct timeval tv;
tv.tv_sec = timeout;
tv.tv_usec = 0;
FD_ZERO(&sdset);
FD_SET(m_sd, &sdset);
if (select(m_sd + 1, &sdset, NULL, NULL, &tv) > 0) {
return true;
}
return false;
}