|
|
@@ -60,58 +60,68 @@ namespace crown
|
|
|
|
|
|
struct ConnectResult
|
|
|
{
|
|
|
- enum { NO_ERROR, REFUSED, TIMEOUT, UNKNOWN } error;
|
|
|
+ enum { NO_ERROR, BAD_SOCKET, REFUSED, TIMEOUT, UNKNOWN } error;
|
|
|
};
|
|
|
|
|
|
struct ReadResult
|
|
|
{
|
|
|
- enum { NO_ERROR, REMOTE_CLOSED, TIMEOUT, UNKNOWN } error;
|
|
|
+ enum { NO_ERROR, BAD_SOCKET, REMOTE_CLOSED, TIMEOUT, UNKNOWN } error;
|
|
|
size_t bytes_read;
|
|
|
};
|
|
|
|
|
|
struct WriteResult
|
|
|
{
|
|
|
- enum { NO_ERROR, REMOTE_CLOSED, TIMEOUT, UNKNOWN } error;
|
|
|
+ enum { NO_ERROR, BAD_SOCKET, REMOTE_CLOSED, TIMEOUT, UNKNOWN } error;
|
|
|
size_t bytes_wrote;
|
|
|
};
|
|
|
|
|
|
struct AcceptResult
|
|
|
{
|
|
|
- enum { NO_ERROR, NO_CONNECTION, UNKNOWN } error;
|
|
|
+ enum { NO_ERROR, BAD_SOCKET, NO_CONNECTION, UNKNOWN } error;
|
|
|
};
|
|
|
|
|
|
struct TCPSocket
|
|
|
{
|
|
|
//-----------------------------------------------------------------------------
|
|
|
TCPSocket()
|
|
|
+#if CROWN_PLATFORM_POSIX
|
|
|
: m_socket(0)
|
|
|
+#elif CROWN_PLATFORM_WINDOWS
|
|
|
+ : m_socket(INVALID_SOCKET)
|
|
|
+#endif
|
|
|
{
|
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
- TCPSocket(int socket)
|
|
|
- : m_socket(socket)
|
|
|
+ void open()
|
|
|
{
|
|
|
+#if CROWN_PLATFORM_POSIX
|
|
|
+ m_socket = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
|
|
|
+ CE_ASSERT(m_socket >= 0, "socket: errno = %d", errno);
|
|
|
+#elif CROWN_PLATFORM_WINDOWS
|
|
|
+ m_socket = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
|
|
|
+ CE_ASSERT(m_socket >= 0, "socket: WSAGetLastError = %d", WSAGetLastError());
|
|
|
+#endif
|
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
- ConnectResult connect(const NetAddress& destination, uint16_t port)
|
|
|
+ ConnectResult connect(const NetAddress& ip, uint16_t port)
|
|
|
{
|
|
|
close();
|
|
|
- m_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
|
|
|
- CE_ASSERT(m_socket >= 0, "socket: errno = %d", errno);
|
|
|
+ open();
|
|
|
+
|
|
|
+ ConnectResult cr;
|
|
|
+ cr.error = ConnectResult::NO_ERROR;
|
|
|
|
|
|
+#if CROWN_PLATFORM_POSIX
|
|
|
sockaddr_in addr_in;
|
|
|
addr_in.sin_family = AF_INET;
|
|
|
- addr_in.sin_addr.s_addr = htonl(destination.address());
|
|
|
- addr_in.sin_port = htons(port);
|
|
|
+ addr_in.sin_addr.s_addr = htonl(ip.address());
|
|
|
+ addr_in.sin_port = ::htons(port);
|
|
|
|
|
|
- int res = ::connect(m_socket, (const sockaddr*)&addr_in, sizeof(sockaddr_in));
|
|
|
+ int err = ::connect(m_socket, (const sockaddr*)&addr_in, sizeof(sockaddr_in));
|
|
|
|
|
|
- ConnectResult cr;
|
|
|
- cr.error = ConnectResult::NO_ERROR;
|
|
|
-
|
|
|
- if (res == 0)
|
|
|
+ if (err == 0)
|
|
|
return cr;
|
|
|
|
|
|
if (errno == ECONNREFUSED)
|
|
|
@@ -122,143 +132,159 @@ struct TCPSocket
|
|
|
cr.error = ConnectResult::UNKNOWN;
|
|
|
|
|
|
return cr;
|
|
|
+#elif CROWN_PLATFORM_WINDOWS
|
|
|
+ sockaddr_in addr_in;
|
|
|
+ addr_in.sin_family = AF_INET;
|
|
|
+ addr_in.sin_addr.s_addr = ::htonl(ip.address());
|
|
|
+ addr_in.sin_port = ::htons(port);
|
|
|
+
|
|
|
+ int err = ::connect(m_socket, (const sockaddr*)&addr_in, sizeof(sockaddr_in));
|
|
|
+
|
|
|
+ if (err == 0)
|
|
|
+ return cr;
|
|
|
+
|
|
|
+ int wsaerr = WSAGetLastError();
|
|
|
+ if (wsaerr == WSAECONNREFUSED)
|
|
|
+ cr.error = ConnectResult::REFUSED;
|
|
|
+ else if (wsaerr == WSAETIMEDOUT)
|
|
|
+ cr.error = ConnectResult::TIMEOUT;
|
|
|
+ else
|
|
|
+ cr.error = ConnectResult::UNKNOWN;
|
|
|
+
|
|
|
+ return cr;
|
|
|
+#endif
|
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
bool bind(uint16_t port)
|
|
|
{
|
|
|
close();
|
|
|
- m_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
|
|
|
- CE_ASSERT(m_socket >= 0, "socket: errno = %d", errno);
|
|
|
-
|
|
|
+ open();
|
|
|
+ set_resuse_address(true);
|
|
|
+#if CROWN_PLATFORM_POSIX
|
|
|
sockaddr_in address;
|
|
|
address.sin_family = AF_INET;
|
|
|
address.sin_addr.s_addr = htonl(INADDR_ANY);
|
|
|
address.sin_port = htons(port);
|
|
|
|
|
|
- set_resuse_address(true);
|
|
|
- int bind_ret = ::bind(m_socket, (const sockaddr*) &address, sizeof(sockaddr_in));
|
|
|
- CE_ASSERT(bind_ret != -1, "bind: errno = %d", errno);
|
|
|
- CE_UNUSED(bind_ret);
|
|
|
+ int err = ::bind(m_socket, (const sockaddr*) &address, sizeof(sockaddr_in));
|
|
|
+ CE_ASSERT(err == 0, "bind: errno = %d", errno);
|
|
|
+ CE_UNUSED(err);
|
|
|
+ return true;
|
|
|
+#elif CROWN_PLATFORM_WINDOWS
|
|
|
+ sockaddr_in address;
|
|
|
+ address.sin_family = AF_INET;
|
|
|
+ address.sin_addr.s_addr = htonl(INADDR_ANY);
|
|
|
+ address.sin_port = htons(port);
|
|
|
|
|
|
+ int err = ::bind(m_socket, (const sockaddr*) &address, sizeof(sockaddr_in));
|
|
|
+ CE_ASSERT(err == 0, "bind: WSAGetLastError = %d", WSAGetLastError());
|
|
|
+ CE_UNUSED(err);
|
|
|
return true;
|
|
|
+#endif
|
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
void listen(uint32_t max)
|
|
|
{
|
|
|
- int listen_ret = ::listen(m_socket, max);
|
|
|
- CE_ASSERT(listen_ret != -1, "listen: errno = %d", errno);
|
|
|
- CE_UNUSED(listen_ret);
|
|
|
+#if CROWN_PLATFORM_POSIX
|
|
|
+ int err = ::listen(m_socket, max);
|
|
|
+ CE_ASSERT(err == 0, "listen: errno = %d", errno);
|
|
|
+ CE_UNUSED(err);
|
|
|
+#elif CROWN_PLATFORM_WINDOWS
|
|
|
+ int err = ::listen(m_socket, max);
|
|
|
+ CE_ASSERT(err == 0, "listen: WSAGetLastError = %d", WSAGetLastError());
|
|
|
+ CE_UNUSED(err);
|
|
|
+#endif
|
|
|
}
|
|
|
|
|
|
- //-----------------------------------------------------------------------------
|
|
|
- AcceptResult accept_nonblock(TCPSocket& c)
|
|
|
+ AcceptResult accept_internal(TCPSocket& c)
|
|
|
{
|
|
|
- set_blocking(false);
|
|
|
+ AcceptResult ar;
|
|
|
+ ar.error = AcceptResult::NO_ERROR;
|
|
|
|
|
|
- sockaddr_in client;
|
|
|
- size_t client_size = sizeof(client);
|
|
|
- int sock = ::accept(m_socket, (sockaddr*) &client, (socklen_t*) &client_size);
|
|
|
+#if CROWN_PLATFORM_POSIX
|
|
|
+ int err = ::accept(m_socket, NULL, NULL);
|
|
|
+
|
|
|
+ if (err >= 0)
|
|
|
+ c.m_socket = err;
|
|
|
+ else if (err == -1 && errno == EBADF)
|
|
|
+ ar.error = AcceptResult::BAD_SOCKET;
|
|
|
+ else if (err == -1 && (errno == EAGAIN || errno == EWOULDBLOCK))
|
|
|
+ ar.error = AcceptResult::NO_CONNECTION;
|
|
|
+ else
|
|
|
+ ar.error = AcceptResult::UNKNOWN;
|
|
|
|
|
|
- AcceptResult result;
|
|
|
- if (sock == -1 && (errno == EAGAIN || errno == EWOULDBLOCK))
|
|
|
- {
|
|
|
- result.error = AcceptResult::NO_CONNECTION;
|
|
|
- }
|
|
|
- else if (sock == -1)
|
|
|
+ return ar;
|
|
|
+#elif CROWN_PLATFORM_WINDOWS
|
|
|
+ int err = ::accept(m_socket, NULL, NULL);
|
|
|
+
|
|
|
+ if (err != INVALID_SOCKET)
|
|
|
{
|
|
|
- result.error = AcceptResult::UNKNOWN;
|
|
|
+ c.m_socket = err;
|
|
|
+ return ar;
|
|
|
}
|
|
|
+
|
|
|
+ int wsaerr = WSAGetLastError();
|
|
|
+ if (wsaerr == WSAEWOULDBLOCK))
|
|
|
+ ar.error = AcceptResult::NO_CONNECTION;
|
|
|
else
|
|
|
- {
|
|
|
- result.error = AcceptResult::NO_ERROR;
|
|
|
- c.m_socket = sock;
|
|
|
- }
|
|
|
+ ar.error = AcceptResult::UNKNOWN;
|
|
|
|
|
|
- return result;
|
|
|
+ return ar;
|
|
|
+#endif
|
|
|
+ }
|
|
|
+
|
|
|
+ //-----------------------------------------------------------------------------
|
|
|
+ AcceptResult accept_nonblock(TCPSocket& c)
|
|
|
+ {
|
|
|
+ set_blocking(false);
|
|
|
+ return accept_internal(c);
|
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
AcceptResult accept(TCPSocket& c)
|
|
|
{
|
|
|
set_blocking(true);
|
|
|
-
|
|
|
- sockaddr_in client;
|
|
|
- size_t client_size = sizeof(client);
|
|
|
-
|
|
|
- int sock = ::accept(m_socket, (sockaddr*) &client, (socklen_t*) &client_size);
|
|
|
-
|
|
|
- AcceptResult result;
|
|
|
- if (sock == -1)
|
|
|
- {
|
|
|
- result.error = AcceptResult::UNKNOWN;
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- result.error = AcceptResult::NO_ERROR;
|
|
|
- c.m_socket = sock;
|
|
|
- }
|
|
|
-
|
|
|
- return result;
|
|
|
+ return accept_internal(c);
|
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
void close()
|
|
|
{
|
|
|
+#if CROWN_PLATFORM_POSIX
|
|
|
if (m_socket != 0)
|
|
|
{
|
|
|
::close(m_socket);
|
|
|
m_socket = 0;
|
|
|
}
|
|
|
- }
|
|
|
-
|
|
|
- //-----------------------------------------------------------------------------
|
|
|
- ReadResult read_nonblock(void* data, size_t size)
|
|
|
- {
|
|
|
- set_blocking(false);
|
|
|
- ssize_t read_bytes = ::read(m_socket, (char*) data, size);
|
|
|
-
|
|
|
- ReadResult rr;
|
|
|
- if (read_bytes == -1 && (errno == EAGAIN || errno == EWOULDBLOCK))
|
|
|
- {
|
|
|
- rr.error = ReadResult::NO_ERROR;
|
|
|
- rr.bytes_read = 0;
|
|
|
- }
|
|
|
- else if (read_bytes == -1 && errno == ETIMEDOUT)
|
|
|
- {
|
|
|
- rr.error = ReadResult::TIMEOUT;
|
|
|
- }
|
|
|
- else if (read_bytes == 0)
|
|
|
- {
|
|
|
- rr.error = ReadResult::REMOTE_CLOSED;
|
|
|
- }
|
|
|
- else
|
|
|
+#elif CROWN_PLATFORM_WINDOWS
|
|
|
+ if (m_socket != INVALID_SOCKET)
|
|
|
{
|
|
|
- rr.error = ReadResult::NO_ERROR;
|
|
|
- rr.bytes_read = read_bytes;
|
|
|
+ ::closesocket(m_socket);
|
|
|
+ m_socket = INVALID_SOCKET;
|
|
|
}
|
|
|
-
|
|
|
- return rr;
|
|
|
+#endif
|
|
|
}
|
|
|
|
|
|
- //-----------------------------------------------------------------------------
|
|
|
- ReadResult read(void* data, size_t size)
|
|
|
+ ReadResult read_internal(void* data, size_t size)
|
|
|
{
|
|
|
- set_blocking(true);
|
|
|
+ ReadResult rr;
|
|
|
+ rr.error = ReadResult::NO_ERROR;
|
|
|
+ rr.bytes_read = 0;
|
|
|
|
|
|
- // Ensure all data is read
|
|
|
+#if CROWN_PLATFORM_POSIX
|
|
|
char* buf = (char*) data;
|
|
|
size_t to_read = size;
|
|
|
- ReadResult rr;
|
|
|
- rr.bytes_read = 0;
|
|
|
- rr.error = ReadResult::NO_ERROR;
|
|
|
|
|
|
while (to_read > 0)
|
|
|
{
|
|
|
- ssize_t read_bytes = ::read(m_socket, buf, to_read);
|
|
|
+ ssize_t read_bytes = ::recv(m_socket, buf, to_read, 0);
|
|
|
|
|
|
- if (read_bytes == -1 && (errno == EAGAIN || errno == EWOULDBLOCK)) continue;
|
|
|
+ if (read_bytes == -1 && (errno == EAGAIN || errno == EWOULDBLOCK))
|
|
|
+ {
|
|
|
+ return rr;
|
|
|
+ }
|
|
|
else if (read_bytes == -1 && errno == ETIMEDOUT)
|
|
|
{
|
|
|
rr.error = ReadResult::TIMEOUT;
|
|
|
@@ -275,74 +301,81 @@ struct TCPSocket
|
|
|
rr.bytes_read += read_bytes;
|
|
|
}
|
|
|
|
|
|
- rr.error = ReadResult::NO_ERROR;
|
|
|
return rr;
|
|
|
+#elif CROWN_PLATFORM_WINDOWS
|
|
|
+ char* buf = (char*) data;
|
|
|
+ size_t to_read = size;
|
|
|
+
|
|
|
+ while (to_read > 0)
|
|
|
+ {
|
|
|
+ int read_bytes = ::recv(m_socket, buf, to_read, 0);
|
|
|
+
|
|
|
+ if (read_bytes == SOCKET_ERROR && WSAGetLastError() == WSAEWOULDBLOCK)
|
|
|
+ return rr;
|
|
|
+ else if (read_bytes == SOCKET_ERROR && WSAGetLastError() == WSAETIMEDOUT)
|
|
|
+ {
|
|
|
+ rr.error = ReadResult::TIMEOUT;
|
|
|
+ return rr;
|
|
|
+ }
|
|
|
+ else if (read_bytes == 0)
|
|
|
+ {
|
|
|
+ rr.error = ReadResult::REMOTE_CLOSED;
|
|
|
+ return rr;
|
|
|
+ }
|
|
|
+
|
|
|
+ buf += read_bytes;
|
|
|
+ to_read -= read_bytes;
|
|
|
+ result.bytes_read += read_bytes;
|
|
|
+ }
|
|
|
+
|
|
|
+ return rr;
|
|
|
+#endif
|
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
- WriteResult write_nonblock(const void* data, size_t size)
|
|
|
+ ReadResult read_nonblock(void* data, size_t size)
|
|
|
{
|
|
|
set_blocking(false);
|
|
|
- ssize_t bytes_wrote = ::send(m_socket, data, size, 0);
|
|
|
-
|
|
|
- WriteResult wr;
|
|
|
- if (bytes_wrote == -1 && (errno == EAGAIN || errno == EWOULDBLOCK))
|
|
|
- {
|
|
|
- wr.error = WriteResult::NO_ERROR;
|
|
|
- wr.bytes_wrote = 0;
|
|
|
- }
|
|
|
- else if (bytes_wrote == -1 && errno == ETIMEDOUT)
|
|
|
- {
|
|
|
- wr.error = WriteResult::TIMEOUT;
|
|
|
- }
|
|
|
- else if (bytes_wrote == 0)
|
|
|
- {
|
|
|
- wr.error = WriteResult::REMOTE_CLOSED;
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- wr.error = WriteResult::UNKNOWN;
|
|
|
- }
|
|
|
-
|
|
|
- return wr;
|
|
|
+ return read_internal(data, size);
|
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
- WriteResult write(const void* data, size_t size)
|
|
|
+ ReadResult read(void* data, size_t size)
|
|
|
{
|
|
|
set_blocking(true);
|
|
|
+ return read_internal(data, size);
|
|
|
+ }
|
|
|
|
|
|
- const char* buf = (const char*) data;
|
|
|
- size_t to_send = size;
|
|
|
+ WriteResult write_internal(const void* data, size_t size)
|
|
|
+ {
|
|
|
WriteResult wr;
|
|
|
- wr.bytes_wrote = 0;
|
|
|
wr.error = WriteResult::NO_ERROR;
|
|
|
+ wr.bytes_wrote = 0;
|
|
|
+
|
|
|
+#if CROWN_PLATFORM_POSIX
|
|
|
+ const char* buf = (const char*) data;
|
|
|
+ size_t to_send = size;
|
|
|
|
|
|
- // Ensure all data is sent
|
|
|
while (to_send > 0)
|
|
|
{
|
|
|
ssize_t bytes_wrote = ::send(m_socket, (const char*) buf, to_send, 0);
|
|
|
|
|
|
- // Check for errors
|
|
|
- if (bytes_wrote == -1)
|
|
|
+ if (bytes_wrote == -1 && (errno == EAGAIN || errno == EWOULDBLOCK))
|
|
|
+ return wr;
|
|
|
+ else if (bytes_wrote == -1 && errno == ETIMEDOUT)
|
|
|
+ {
|
|
|
+ wr.error = WriteResult::TIMEOUT;
|
|
|
+ return wr;
|
|
|
+ }
|
|
|
+ else if (bytes_wrote == 0)
|
|
|
+ {
|
|
|
+ wr.error = WriteResult::REMOTE_CLOSED;
|
|
|
+ return wr;
|
|
|
+ }
|
|
|
+ else
|
|
|
{
|
|
|
- switch (errno)
|
|
|
- {
|
|
|
- case EAGAIN:
|
|
|
- {
|
|
|
- continue;
|
|
|
- }
|
|
|
- case ETIMEDOUT:
|
|
|
- {
|
|
|
- wr.error = WriteResult::TIMEOUT;
|
|
|
- return wr;
|
|
|
- }
|
|
|
- default:
|
|
|
- {
|
|
|
- wr.error = WriteResult::UNKNOWN;
|
|
|
- return wr;
|
|
|
- }
|
|
|
- }
|
|
|
+ wr.error = WriteResult::UNKNOWN;
|
|
|
+ return wr;
|
|
|
}
|
|
|
|
|
|
buf += bytes_wrote;
|
|
|
@@ -350,385 +383,116 @@ struct TCPSocket
|
|
|
wr.bytes_wrote += bytes_wrote;
|
|
|
}
|
|
|
|
|
|
- wr.error = WriteResult::NO_ERROR;
|
|
|
return wr;
|
|
|
- }
|
|
|
-
|
|
|
- //-----------------------------------------------------------------------------
|
|
|
- void set_blocking(bool blocking)
|
|
|
- {
|
|
|
- int flags = fcntl(m_socket, F_GETFL, 0);
|
|
|
- fcntl(m_socket, F_SETFL, blocking ? (flags & ~O_NONBLOCK) : O_NONBLOCK);
|
|
|
- }
|
|
|
-
|
|
|
- //-----------------------------------------------------------------------------
|
|
|
- void set_resuse_address(bool reuse)
|
|
|
- {
|
|
|
- int optval = (int) reuse;
|
|
|
- setsockopt(m_socket, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval));
|
|
|
- }
|
|
|
-
|
|
|
- //-----------------------------------------------------------------------------
|
|
|
- void set_timeout(uint32_t seconds)
|
|
|
- {
|
|
|
- struct timeval timeout;
|
|
|
- timeout.tv_sec = seconds;
|
|
|
- timeout.tv_usec = 0;
|
|
|
- int res;
|
|
|
- res = setsockopt (m_socket, SOL_SOCKET, SO_RCVTIMEO, (char*) &timeout, sizeof(timeout));
|
|
|
- CE_ASSERT(res == 0, "setsockopt: errno: %d", errno);
|
|
|
- res = setsockopt (m_socket, SOL_SOCKET, SO_SNDTIMEO, (char*) &timeout, sizeof(timeout));
|
|
|
- CE_ASSERT(res == 0, "setsockopt: errno: %d", errno);
|
|
|
- }
|
|
|
-
|
|
|
-private:
|
|
|
-
|
|
|
- int m_socket;
|
|
|
-};
|
|
|
-
|
|
|
-
|
|
|
-/*
|
|
|
-class TCPSocket
|
|
|
-{
|
|
|
-public:
|
|
|
- //-----------------------------------------------------------------------------
|
|
|
- TCPSocket()
|
|
|
- : m_socket(0)
|
|
|
- {
|
|
|
- }
|
|
|
-
|
|
|
- //-----------------------------------------------------------------------------
|
|
|
- TCPSocket(int socket)
|
|
|
- : m_socket(socket)
|
|
|
- {
|
|
|
- }
|
|
|
-
|
|
|
- //-----------------------------------------------------------------------------
|
|
|
- bool connect(const NetAddress& destination, uint16_t port)
|
|
|
- {
|
|
|
- m_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
|
|
|
-
|
|
|
- CE_ASSERT(m_socket > 0, "Failed to create socket");
|
|
|
-
|
|
|
- sockaddr_in address;
|
|
|
- address.sin_family = AF_INET;
|
|
|
- address.sin_addr.s_addr = ::htonl(destination.address());
|
|
|
- address.sin_port = ::htons(port);
|
|
|
-
|
|
|
- if (::connect(m_socket, (const sockaddr*)&address, sizeof(sockaddr_in)) < 0)
|
|
|
- {
|
|
|
- os::printf("Failed to connect socket\n");
|
|
|
- close();
|
|
|
- return false;
|
|
|
- }
|
|
|
-
|
|
|
- return true;
|
|
|
- }
|
|
|
-
|
|
|
- //-----------------------------------------------------------------------------
|
|
|
- void close()
|
|
|
- {
|
|
|
- if (m_socket != 0)
|
|
|
- {
|
|
|
- ::closesocket(m_socket);
|
|
|
- m_socket = 0;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- //-----------------------------------------------------------------------------
|
|
|
- ReadResult read_nonblock(void* data, size_t size)
|
|
|
- {
|
|
|
- set_blocking(false);
|
|
|
- int read_bytes = ::recv(m_socket, (char*) data, size, 0);
|
|
|
-
|
|
|
- ReadResult result;
|
|
|
- if (read_bytes == SOCKET_ERROR && (errno == WSAEWOULDBLOCK))
|
|
|
- {
|
|
|
- result.error = ReadResult::NO_ERROR;
|
|
|
- result.bytes_read = 0;
|
|
|
- }
|
|
|
- else if (read_bytes == SOCKET_ERROR && errno == WSAETIMEDOUT)
|
|
|
- {
|
|
|
- result.error = ReadResult::TIMEOUT;
|
|
|
- }
|
|
|
- else if (read_bytes == SOCKET_ERROR)
|
|
|
- {
|
|
|
- result.error = ReadResult::UNKNOWN;
|
|
|
- }
|
|
|
- else if (read_bytes == 0)
|
|
|
- {
|
|
|
- result.error = ReadResult::REMOTE_CLOSED;
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- result.error = ReadResult::NO_ERROR;
|
|
|
- result.bytes_read = read_bytes;
|
|
|
- }
|
|
|
-
|
|
|
- return result;
|
|
|
- }
|
|
|
-
|
|
|
- //-----------------------------------------------------------------------------
|
|
|
- ReadResult read(void* data, size_t size)
|
|
|
- {
|
|
|
- set_blocking(true);
|
|
|
-
|
|
|
- // Ensure all data is read
|
|
|
- char* buf = (char*) data;
|
|
|
- size_t to_read = size;
|
|
|
- ReadResult result;
|
|
|
- result.bytes_read = 0;
|
|
|
- result.error = ReadResult::NO_ERROR;
|
|
|
+#elif CROWN_PLATFORM_WINDOWS
|
|
|
+ const char* buf = (const char*) data;
|
|
|
+ size_t to_send = size;
|
|
|
|
|
|
- while (to_read > 0)
|
|
|
+ while (to_send > 0)
|
|
|
{
|
|
|
- int read_bytes = recv(m_socket, buf, to_read, 0);
|
|
|
+ ssize_t bytes_wrote = ::send(m_socket, (const char*) buf, to_send, 0);
|
|
|
|
|
|
- if (read_bytes == SOCKET_ERROR && (errno == WSAEWOULDBLOCK)) continue;
|
|
|
- else if (read_bytes == SOCKET_ERROR && errno == WSAETIMEDOUT)
|
|
|
+ if (bytes_wrote == SOCKET_ERROR && WSAGetLastError() == WSAEWOULDBLOCK)
|
|
|
{
|
|
|
- result.error = ReadResult::TIMEOUT;
|
|
|
- return result;
|
|
|
+ return wr;
|
|
|
}
|
|
|
- else if (read_bytes == 0)
|
|
|
+ else if (bytes_wrote == SOCKET_ERROR && WSAGetLastError() == WSAETIMEDOUT)
|
|
|
{
|
|
|
- result.error = ReadResult::REMOTE_CLOSED;
|
|
|
- return result;
|
|
|
+ wr.error = WriteResult::TIMEOUT;
|
|
|
+ return wr;
|
|
|
+ }
|
|
|
+ else if (bytes_wrote == 0)
|
|
|
+ {
|
|
|
+ wr.error = WriteResult::REMOTE_CLOSED;
|
|
|
+ return wr;
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ wr.error = WriteResult::UNKNOWN;
|
|
|
+ return wr;
|
|
|
}
|
|
|
|
|
|
- buf += read_bytes;
|
|
|
- to_read -= read_bytes;
|
|
|
- result.bytes_read += read_bytes;
|
|
|
+ buf += bytes_wrote;
|
|
|
+ to_send -= bytes_wrote;
|
|
|
+ wr.bytes_wrote += bytes_wrote;
|
|
|
}
|
|
|
|
|
|
- result.error = ReadResult::NO_ERROR;
|
|
|
- return result;
|
|
|
+ return wr;
|
|
|
+#endif
|
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
WriteResult write_nonblock(const void* data, size_t size)
|
|
|
{
|
|
|
set_blocking(false);
|
|
|
- int bytes_wrote = ::send(m_socket, (char*)data, size, 0);
|
|
|
-
|
|
|
- WriteResult result;
|
|
|
- if (bytes_wrote == SOCKET_ERROR && (errno == WSAEWOULDBLOCK))
|
|
|
- {
|
|
|
- result.error = WriteResult::NO_ERROR;
|
|
|
- result.bytes_wrote = 0;
|
|
|
- }
|
|
|
- else if (bytes_wrote == SOCKET_ERROR && errno == WSAETIMEDOUT)
|
|
|
- {
|
|
|
- result.error = WriteResult::TIMEOUT;
|
|
|
- }
|
|
|
- else if (bytes_wrote == 0)
|
|
|
- {
|
|
|
- result.error = WriteResult::REMOTE_CLOSED;
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- result.error = WriteResult::UNKNOWN;
|
|
|
- }
|
|
|
-
|
|
|
- return result;
|
|
|
+ return write_internal(data, size);
|
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
WriteResult write(const void* data, size_t size)
|
|
|
{
|
|
|
set_blocking(true);
|
|
|
-
|
|
|
- const char* buf = (const char*) data;
|
|
|
- size_t to_send = size;
|
|
|
- WriteResult result;
|
|
|
- result.bytes_wrote = 0;
|
|
|
- result.error = WriteResult::NO_ERROR;
|
|
|
-
|
|
|
- // Ensure all data is sent
|
|
|
- while (to_send > 0)
|
|
|
- {
|
|
|
- int bytes_wrote = ::send(m_socket, (const char*) buf, to_send, 0);
|
|
|
-
|
|
|
- // Check for errors
|
|
|
- if (bytes_wrote == SOCKET_ERROR)
|
|
|
- {
|
|
|
- switch (errno)
|
|
|
- {
|
|
|
- case WSAEWOULDBLOCK:
|
|
|
- {
|
|
|
- continue;
|
|
|
- }
|
|
|
- case WSAETIMEDOUT:
|
|
|
- {
|
|
|
- result.error = WriteResult::TIMEOUT;
|
|
|
- return result;
|
|
|
- }
|
|
|
- default:
|
|
|
- {
|
|
|
- result.error = WriteResult::UNKNOWN;
|
|
|
- return result;
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- buf += bytes_wrote;
|
|
|
- to_send -= bytes_wrote;
|
|
|
- result.bytes_wrote += bytes_wrote;
|
|
|
- }
|
|
|
-
|
|
|
- result.error = WriteResult::NO_ERROR;
|
|
|
- return result;
|
|
|
+ return write_internal(data, size);
|
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
void set_blocking(bool blocking)
|
|
|
{
|
|
|
- //Warning! Blocking sockets under windows seem to have some drawback, see http://www.sockets.com/winsock.htm#IoctlSocket
|
|
|
+#if CROWN_PLATFORM_POSIX
|
|
|
+ int flags = fcntl(m_socket, F_GETFL, 0);
|
|
|
+ fcntl(m_socket, F_SETFL, blocking ? (flags & ~O_NONBLOCK) : O_NONBLOCK);
|
|
|
+#elif CROWN_PLATFORM_WINDOWS
|
|
|
+ //Warning! http://www.sockets.com/winsock.htm#IoctlSocket
|
|
|
u_long non_blocking = blocking ? 0 : 1;
|
|
|
ioctlsocket(m_socket, FIONBIO, &non_blocking);
|
|
|
+#endif
|
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
void set_resuse_address(bool reuse)
|
|
|
{
|
|
|
+#if CROWN_PLATFORM_POSIX
|
|
|
int optval = (int) reuse;
|
|
|
- setsockopt(m_socket, SOL_SOCKET, SO_REUSEADDR, (char*)&optval, sizeof(optval));
|
|
|
+ int err = setsockopt(m_socket, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval));
|
|
|
+ CE_ASSERT(err == 0, "setsockopt: errno = %d", errno);
|
|
|
+#elif CROWN_PLATFORM_WINDOWS
|
|
|
+ int optval = (int) reuse;
|
|
|
+ int err = setsockopt(m_socket, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval));
|
|
|
+ CE_ASSERT(err == 0, "setsockopt: WSAGetLastError = %d", WSAGetLastError());
|
|
|
+#endif
|
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
void set_timeout(uint32_t seconds)
|
|
|
{
|
|
|
+#if CROWN_PLATFORM_POSIX
|
|
|
struct timeval timeout;
|
|
|
timeout.tv_sec = seconds;
|
|
|
timeout.tv_usec = 0;
|
|
|
- int res;
|
|
|
- res = setsockopt (m_socket, SOL_SOCKET, SO_RCVTIMEO, (char*) &timeout, sizeof(timeout));
|
|
|
- CE_ASSERT(res == 0, "Failed to set timeout on socket: errno: %d", errno);
|
|
|
- res = setsockopt (m_socket, SOL_SOCKET, SO_SNDTIMEO, (char*) &timeout, sizeof(timeout));
|
|
|
- CE_ASSERT(res == 0, "Failed to set timeout on socket: errno: %d", errno);
|
|
|
- }
|
|
|
-
|
|
|
-public:
|
|
|
-
|
|
|
- SOCKET m_socket;
|
|
|
-};
|
|
|
-
|
|
|
-class TCPServer
|
|
|
-{
|
|
|
-public:
|
|
|
-
|
|
|
- //-----------------------------------------------------------------------------
|
|
|
- bool open(uint16_t port)
|
|
|
- {
|
|
|
- m_server.m_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
|
|
|
- CE_ASSERT(m_server.m_socket > 0, "Failed to create socket");
|
|
|
-
|
|
|
- m_server.set_resuse_address(true);
|
|
|
-
|
|
|
- // Bind socket
|
|
|
- sockaddr_in address;
|
|
|
- address.sin_family = AF_INET;
|
|
|
- address.sin_addr.s_addr = htonl(INADDR_ANY);
|
|
|
- address.sin_port = htons(port);
|
|
|
-
|
|
|
- int bind_ret = bind(m_server.m_socket, (const sockaddr*) &address, sizeof(sockaddr_in));
|
|
|
- CE_ASSERT(bind_ret != -1, "Failed to bind socket: errno: %d", errno);
|
|
|
-
|
|
|
- return true;
|
|
|
- }
|
|
|
-
|
|
|
- //-----------------------------------------------------------------------------
|
|
|
- void close()
|
|
|
- {
|
|
|
- m_server.close();
|
|
|
- }
|
|
|
-
|
|
|
- //-----------------------------------------------------------------------------
|
|
|
- void listen(uint32_t max)
|
|
|
- {
|
|
|
- int listen_ret = ::listen(m_server.m_socket, max);
|
|
|
- CE_ASSERT(listen_ret != -1, "Failed to listen on socket: errno: %d", errno);
|
|
|
- }
|
|
|
-
|
|
|
- //-----------------------------------------------------------------------------
|
|
|
- AcceptResult accept_nonblock(TCPSocket& c)
|
|
|
- {
|
|
|
- m_server.set_blocking(false);
|
|
|
-
|
|
|
- sockaddr_in client;
|
|
|
- size_t client_size = sizeof(client);
|
|
|
- int sock = ::accept(m_server.m_socket, (sockaddr*) &client, (int*) &client_size);
|
|
|
-
|
|
|
- AcceptResult result;
|
|
|
- if (sock == SOCKET_ERROR && (errno == WSAEWOULDBLOCK))
|
|
|
- {
|
|
|
- result.error = AcceptResult::NO_CONNECTION;
|
|
|
- }
|
|
|
- else if (sock == SOCKET_ERROR)
|
|
|
- {
|
|
|
- result.error = AcceptResult::UNKNOWN;
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- result.error = AcceptResult::NO_ERROR;
|
|
|
- c.m_socket = sock;
|
|
|
- }
|
|
|
-
|
|
|
- return result;
|
|
|
- }
|
|
|
-
|
|
|
- //-----------------------------------------------------------------------------
|
|
|
- AcceptResult accept(TCPSocket& c)
|
|
|
- {
|
|
|
- m_server.set_blocking(true);
|
|
|
-
|
|
|
- sockaddr_in client;
|
|
|
- size_t client_size = sizeof(client);
|
|
|
-
|
|
|
- int sock = ::accept(m_server.m_socket, (sockaddr*) &client, (int*) &client_size);
|
|
|
-
|
|
|
- AcceptResult result;
|
|
|
- if (sock == SOCKET_ERROR)
|
|
|
- {
|
|
|
- result.error = AcceptResult::UNKNOWN;
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- result.error = AcceptResult::NO_ERROR;
|
|
|
- c.m_socket = sock;
|
|
|
- }
|
|
|
-
|
|
|
- return result;
|
|
|
- }
|
|
|
-
|
|
|
- //-----------------------------------------------------------------------------
|
|
|
- ReadResult read_nonblock(void* data, size_t size)
|
|
|
- {
|
|
|
- return m_server.read_nonblock(data, size);
|
|
|
- }
|
|
|
-
|
|
|
- //-----------------------------------------------------------------------------
|
|
|
- ReadResult read(void* data, size_t size)
|
|
|
- {
|
|
|
- return m_server.read(data, size);
|
|
|
- }
|
|
|
-
|
|
|
- //-----------------------------------------------------------------------------
|
|
|
- WriteResult write_nonblock(void* data, size_t size)
|
|
|
- {
|
|
|
- return m_server.write_nonblock(data, size);
|
|
|
- }
|
|
|
-
|
|
|
- //-----------------------------------------------------------------------------
|
|
|
- WriteResult write(const void* data, size_t size)
|
|
|
- {
|
|
|
- return m_server.write(data, size);
|
|
|
+ int err;
|
|
|
+ err = setsockopt (m_socket, SOL_SOCKET, SO_RCVTIMEO, (char*) &timeout, sizeof(timeout));
|
|
|
+ CE_ASSERT(err == 0, "setsockopt: errno: %d", errno);
|
|
|
+ err = setsockopt (m_socket, SOL_SOCKET, SO_SNDTIMEO, (char*) &timeout, sizeof(timeout));
|
|
|
+ CE_ASSERT(err == 0, "setsockopt: errno: %d", errno);
|
|
|
+#elif CROWN_PLATFORM_WINDOWS
|
|
|
+ struct timeval timeout;
|
|
|
+ timeout.tv_sec = seconds;
|
|
|
+ timeout.tv_usec = 0;
|
|
|
+ int err;
|
|
|
+ err = setsockopt (m_socket, SOL_SOCKET, SO_RCVTIMEO, (char*) &timeout, sizeof(timeout));
|
|
|
+ CE_ASSERT(err == 0, "setsockopt: WSAGetLastError: %d", WSAGetLastError());
|
|
|
+ err = setsockopt (m_socket, SOL_SOCKET, SO_SNDTIMEO, (char*) &timeout, sizeof(timeout));
|
|
|
+ CE_ASSERT(err == 0, "setsockopt: WSAGetLastError: %d", WSAGetLastError());
|
|
|
+#endif
|
|
|
}
|
|
|
|
|
|
private:
|
|
|
|
|
|
- TCPSocket m_server;
|
|
|
+#if CROWN_PLATFORM_POSIX
|
|
|
+ int m_socket;
|
|
|
+#elif CROWN_PLATFORM_WINDOWS
|
|
|
+ SOCKET m_socket;
|
|
|
+#endif
|
|
|
};
|
|
|
-*/
|
|
|
|
|
|
} // namespace crown
|