From a3dbe9a800a32e8e44415745c6ac3c0e030f8e48 Mon Sep 17 00:00:00 2001 From: Peter Johnson Date: Mon, 24 Aug 2015 21:21:13 -0700 Subject: [PATCH] Fix Windows client. Windows returns WSAEWOULDBLOCK on a connect() attempt on a nonblocking socket. Also wrap socket error handling so errors are correctly reported on Windows. Fixes #19. --- src/tcpsockets/SocketError.cpp | 27 ++++++++++++++++++++++++ src/tcpsockets/SocketError.h | 37 +++++++++++++++++++++++++++++++++ src/tcpsockets/TCPAcceptor.cpp | 11 ++++++---- src/tcpsockets/TCPConnector.cpp | 19 +++++++++++------ 4 files changed, 84 insertions(+), 10 deletions(-) create mode 100644 src/tcpsockets/SocketError.cpp create mode 100644 src/tcpsockets/SocketError.h diff --git a/src/tcpsockets/SocketError.cpp b/src/tcpsockets/SocketError.cpp new file mode 100644 index 0000000000..de4d43452d --- /dev/null +++ b/src/tcpsockets/SocketError.cpp @@ -0,0 +1,27 @@ +/*----------------------------------------------------------------------------*/ +/* 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 "SocketError.h" + +#include + +namespace tcpsockets { + +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 tcpsockets diff --git a/src/tcpsockets/SocketError.h b/src/tcpsockets/SocketError.h new file mode 100644 index 0000000000..267e8dae80 --- /dev/null +++ b/src/tcpsockets/SocketError.h @@ -0,0 +1,37 @@ +/*----------------------------------------------------------------------------*/ +/* 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. */ +/*----------------------------------------------------------------------------*/ + +#ifndef TCPSOCKETS_SOCKETERROR_H_ +#define TCPSOCKETS_SOCKETERROR_H_ + +#include + +#ifdef _WIN32 +#include +#else +#include +#endif + +namespace tcpsockets { + +static inline int SocketErrno() { +#ifdef _WIN32 + return WSAGetLastError(); +#else + return errno; +#endif +} + +std::string SocketStrerror(int code); + +static inline std::string SocketStrerror() { + return SocketStrerror(SocketErrno()); +} + +} // namespace tcpsockets + +#endif // TCPSOCKETS_SOCKETERROR_H_ diff --git a/src/tcpsockets/TCPAcceptor.cpp b/src/tcpsockets/TCPAcceptor.cpp index b61603bf6e..04a995dca4 100644 --- a/src/tcpsockets/TCPAcceptor.cpp +++ b/src/tcpsockets/TCPAcceptor.cpp @@ -36,6 +36,9 @@ #include "llvm/SmallString.h" #include "../Log.h" +#include "SocketError.h" + +using namespace tcpsockets; TCPAcceptor::TCPAcceptor(int port, const char* address) : m_lsd(0), @@ -75,7 +78,7 @@ int TCPAcceptor::start() { if (m_address.size() > 0) { #ifdef _WIN32 llvm::SmallString<128> addr_copy(m_address); - addr_copy.append('\0'); + addr_copy.push_back('\0'); int size = sizeof(address); WSAStringToAddress(addr_copy.data(), PF_INET, nullptr, (struct sockaddr*)&address, &size); #else @@ -91,13 +94,13 @@ int TCPAcceptor::start() { int result = bind(m_lsd, (struct sockaddr*)&address, sizeof(address)); if (result != 0) { - ERROR("bind() failed: " << strerror(errno)); + ERROR("bind() failed: " << SocketStrerror()); return result; } result = listen(m_lsd, 5); if (result != 0) { - ERROR("listen() failed: " << strerror(errno)); + ERROR("listen() failed: " << SocketStrerror()); return result; } m_listening = true; @@ -125,7 +128,7 @@ std::unique_ptr TCPAcceptor::accept() { std::memset(&address, 0, sizeof(address)); int sd = ::accept(m_lsd, (struct sockaddr*)&address, &len); if (sd < 0) { - if (!m_shutdown) ERROR("accept() failed: " << strerror(errno)); + if (!m_shutdown) ERROR("accept() failed: " << SocketStrerror()); return nullptr; } return std::unique_ptr(new TCPStream(sd, &address)); diff --git a/src/tcpsockets/TCPConnector.cpp b/src/tcpsockets/TCPConnector.cpp index 3d2f81affc..988edc407a 100644 --- a/src/tcpsockets/TCPConnector.cpp +++ b/src/tcpsockets/TCPConnector.cpp @@ -40,6 +40,9 @@ #include "llvm/SmallString.h" #include "../Log.h" +#include "SocketError.h" + +using namespace tcpsockets; static int ResolveHostName(const char* hostname, struct in_addr* addr) { struct addrinfo* res; @@ -73,7 +76,7 @@ std::unique_ptr TCPConnector::connect(const char* server, if (ResolveHostName(server, &(address.sin_addr)) != 0) { #ifdef _WIN32 llvm::SmallString<128> addr_copy(server); - addr_copy.append('\0'); + addr_copy.push_back('\0'); int size = sizeof(address); WSAStringToAddress(addr_copy.data(), PF_INET, nullptr, (struct sockaddr*)&address, &size); #else @@ -85,7 +88,7 @@ std::unique_ptr TCPConnector::connect(const char* server, if (timeout == 0) { int sd = socket(AF_INET, SOCK_STREAM, 0); if (::connect(sd, (struct sockaddr*)&address, sizeof(address)) != 0) { - DEBUG("connect() failed: " << strerror(errno)); + DEBUG("connect() failed: " << SocketStrerror()); return nullptr; } return std::unique_ptr(new TCPStream(sd, &address)); @@ -108,10 +111,14 @@ std::unique_ptr TCPConnector::connect(const char* server, #endif // Connect with time limit - std::string message; if ((result = ::connect(sd, (struct sockaddr*)&address, sizeof(address))) < 0) { - if (errno == EINPROGRESS) { + 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); @@ -120,7 +127,7 @@ std::unique_ptr TCPConnector::connect(const char* server, len = sizeof(int); getsockopt(sd, SOL_SOCKET, SO_ERROR, (char*)(&valopt), &len); if (valopt) { - DEBUG("connect() error " << valopt << " - " << strerror(valopt)); + DEBUG("select() error " << valopt << " - " << SocketStrerror(valopt)); } // connection established else @@ -128,7 +135,7 @@ std::unique_ptr TCPConnector::connect(const char* server, } else DEBUG("connect() timed out"); } else - DEBUG("connect() error " << errno << " - " << strerror(errno)); + DEBUG("connect() error " << SocketErrno() << " - " << SocketStrerror()); } // Return socket to blocking mode