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.
This commit is contained in:
Peter Johnson
2015-08-24 21:21:13 -07:00
parent 822dc45834
commit a3dbe9a800
4 changed files with 84 additions and 10 deletions

View File

@@ -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 <windows.h>
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

View File

@@ -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 <string>
#ifdef _WIN32
#include <WinSock2.h>
#else
#include <errno.h>
#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_

View File

@@ -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<NetworkStream> 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<NetworkStream>(new TCPStream(sd, &address));

View File

@@ -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<NetworkStream> 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<NetworkStream> 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<NetworkStream>(new TCPStream(sd, &address));
@@ -108,10 +111,14 @@ std::unique_ptr<NetworkStream> 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<NetworkStream> 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<NetworkStream> 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