|
|
@@ -26,98 +26,214 @@ OTHER DEALINGS IN THE SOFTWARE.
|
|
|
|
|
|
#pragma once
|
|
|
|
|
|
+#include <sys/socket.h>
|
|
|
+#include <sys/types.h>
|
|
|
+#include <netinet/in.h>
|
|
|
+#include <fcntl.h>
|
|
|
+#include <unistd.h>
|
|
|
+#include <errno.h>
|
|
|
#include "Types.h"
|
|
|
#include "NetAddress.h"
|
|
|
+#include "Assert.h"
|
|
|
+#include "OS.h"
|
|
|
|
|
|
namespace crown
|
|
|
{
|
|
|
|
|
|
-class TCPClient
|
|
|
+struct ReadResult
|
|
|
{
|
|
|
-public:
|
|
|
-
|
|
|
- TCPClient();
|
|
|
- TCPClient(const TCPClient& c);
|
|
|
- ~TCPClient();
|
|
|
-
|
|
|
- bool connect(const os::NetAddress& destination);
|
|
|
- void close();
|
|
|
- size_t read(void* data, size_t size);
|
|
|
- size_t write(const void* data, size_t size);
|
|
|
-
|
|
|
-private:
|
|
|
+ enum { NO_ERROR, UNKNOWN, REMOTE_CLOSED } error;
|
|
|
+ size_t received_bytes;
|
|
|
+};
|
|
|
|
|
|
- TCPClient(int socket);
|
|
|
+struct WriteResult
|
|
|
+{
|
|
|
+ enum { NO_ERROR, UNKNOWN, REMOTE_CLOSED } error;
|
|
|
+ size_t sent_bytes;
|
|
|
+};
|
|
|
|
|
|
-private:
|
|
|
+class TCPSocket
|
|
|
+{
|
|
|
+public:
|
|
|
|
|
|
- int m_socket;
|
|
|
+ //-----------------------------------------------------------------------------
|
|
|
+ TCPSocket()
|
|
|
+ : m_socket(0)
|
|
|
+ {
|
|
|
+ }
|
|
|
+
|
|
|
+ //-----------------------------------------------------------------------------
|
|
|
+ TCPSocket(int socket)
|
|
|
+ : m_socket(socket)
|
|
|
+ {
|
|
|
+ }
|
|
|
+
|
|
|
+ //-----------------------------------------------------------------------------
|
|
|
+ bool open(const NetAddress& destination, uint16_t port)
|
|
|
+ {
|
|
|
+ int sd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
|
|
|
+
|
|
|
+ if (sd <= 0)
|
|
|
+ {
|
|
|
+ os::printf("Failed to open socket\n");
|
|
|
+ m_socket = 0;
|
|
|
+
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ m_socket = sd;
|
|
|
+
|
|
|
+ sockaddr_in address;
|
|
|
+ address.sin_family = AF_INET;
|
|
|
+ address.sin_addr.s_addr = htonl(destination.address());
|
|
|
+ address.sin_port = htons(port);
|
|
|
+
|
|
|
+ if (::connect(sd, (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)
|
|
|
+ {
|
|
|
+ ::close(m_socket);
|
|
|
+ m_socket = 0;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ //-----------------------------------------------------------------------------
|
|
|
+ ReadResult read(void* data, size_t size)
|
|
|
+ {
|
|
|
+ CE_ASSERT_NOT_NULL(data);
|
|
|
+
|
|
|
+ ssize_t received_bytes = ::read(m_socket, (char*) data, size);
|
|
|
+
|
|
|
+ ReadResult result;
|
|
|
+
|
|
|
+ if (received_bytes == -1 && errno == EAGAIN)
|
|
|
+ {
|
|
|
+ result.error = ReadResult::NO_ERROR;
|
|
|
+ result.received_bytes = 0;
|
|
|
+ }
|
|
|
+ else if (received_bytes == 0)
|
|
|
+ {
|
|
|
+ result.error = ReadResult::REMOTE_CLOSED;
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ result.error = ReadResult::NO_ERROR;
|
|
|
+ result.received_bytes = received_bytes;
|
|
|
+ }
|
|
|
+
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+ //-----------------------------------------------------------------------------
|
|
|
+ WriteResult write(const void* data, size_t size)
|
|
|
+ {
|
|
|
+ CE_ASSERT_NOT_NULL(data);
|
|
|
+
|
|
|
+ ssize_t sent_bytes = ::send(m_socket, (const char*) data, size, 0);
|
|
|
+
|
|
|
+ WriteResult result;
|
|
|
+
|
|
|
+ if (sent_bytes == -1)
|
|
|
+ {
|
|
|
+ result.error = WriteResult::UNKNOWN;
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ result.error = WriteResult::NO_ERROR;
|
|
|
+ result.sent_bytes = sent_bytes;
|
|
|
+ }
|
|
|
+
|
|
|
+ return result;
|
|
|
+ }
|
|
|
|
|
|
-private:
|
|
|
+public:
|
|
|
|
|
|
- friend class TCPListener;
|
|
|
+ int m_socket;
|
|
|
};
|
|
|
|
|
|
class TCPListener
|
|
|
{
|
|
|
public:
|
|
|
|
|
|
- TCPListener(uint16_t port);
|
|
|
- ~TCPListener();
|
|
|
+ //-----------------------------------------------------------------------------
|
|
|
+ bool open(uint16_t port)
|
|
|
+ {
|
|
|
+ int& sock_id = m_listener.m_socket;
|
|
|
|
|
|
- bool listen(TCPClient& c);
|
|
|
- void close();
|
|
|
- size_t read(void* data, size_t size);
|
|
|
- size_t write(const void* data, size_t size);
|
|
|
+ sock_id = socket(AF_INET, SOCK_STREAM, 0);
|
|
|
+ CE_ASSERT(sock_id != -1, "Failed to open socket: errno: %d", errno);
|
|
|
|
|
|
-private:
|
|
|
+ fcntl(sock_id, F_SETFL, O_NONBLOCK);
|
|
|
|
|
|
- int m_socket;
|
|
|
-};
|
|
|
+ // Bind socket
|
|
|
+ sockaddr_in address;
|
|
|
+ address.sin_family = AF_INET;
|
|
|
+ address.sin_addr.s_addr = htonl(INADDR_ANY);
|
|
|
+ address.sin_port = htons(port);
|
|
|
|
|
|
-namespace os
|
|
|
-{
|
|
|
+ int bind_ret = bind(sock_id, (const sockaddr*) &address, sizeof(sockaddr_in));
|
|
|
+ CE_ASSERT(bind_ret != -1, "Failed to bind socket: errno: %d", errno);
|
|
|
|
|
|
-/// OS level TCP socket.
|
|
|
-class TCPSocket
|
|
|
-{
|
|
|
-public:
|
|
|
+ int listen_ret = ::listen(sock_id, 5);
|
|
|
+ CE_ASSERT(listen_ret != -1, "Failed to listen on socket: errno: %d", errno);
|
|
|
+
|
|
|
+ return true;
|
|
|
+ }
|
|
|
|
|
|
- TCPSocket();
|
|
|
- ~TCPSocket();
|
|
|
+ //-----------------------------------------------------------------------------
|
|
|
+ void close()
|
|
|
+ {
|
|
|
+ m_listener.close();
|
|
|
+ }
|
|
|
|
|
|
- // Open connection (server side)
|
|
|
- bool open(uint16_t port);
|
|
|
+ //-----------------------------------------------------------------------------
|
|
|
+ bool listen(TCPSocket& c)
|
|
|
+ {
|
|
|
+ int& sock_id = m_listener.m_socket;
|
|
|
|
|
|
- // Connect (client side)
|
|
|
- bool connect(const NetAddress& destination);
|
|
|
+ sockaddr_in client;
|
|
|
+ size_t client_length = sizeof(client);
|
|
|
|
|
|
- // Close connection
|
|
|
- void close();
|
|
|
+ int asd = accept(sock_id, (sockaddr*)&client, (socklen_t*)&client_length);
|
|
|
|
|
|
- // Send data through socket
|
|
|
- bool send(const void* data, size_t size);
|
|
|
+ if (asd == -1 && errno == EWOULDBLOCK)
|
|
|
+ {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
|
|
|
- // Receive data through socket
|
|
|
- size_t receive(void* data, size_t size);
|
|
|
+ fcntl(asd, F_SETFL, O_NONBLOCK);
|
|
|
+ c.m_socket = asd;
|
|
|
|
|
|
- // Is connection open?
|
|
|
- bool is_open();
|
|
|
+ return true;
|
|
|
+ }
|
|
|
|
|
|
- // Getter method for socket descriptor
|
|
|
- int socket_id();
|
|
|
+ //-----------------------------------------------------------------------------
|
|
|
+ ReadResult read(void* data, size_t size)
|
|
|
+ {
|
|
|
+ return m_listener.read(data, size);
|
|
|
+ }
|
|
|
|
|
|
- // Getter method for active socket descriptor
|
|
|
- int active_socket_id();
|
|
|
+ //-----------------------------------------------------------------------------
|
|
|
+ WriteResult write(const void* data, size_t size)
|
|
|
+ {
|
|
|
+ return m_listener.write(data, size);
|
|
|
+ }
|
|
|
|
|
|
private:
|
|
|
-
|
|
|
- // Generated by ::socket
|
|
|
- int m_socket;
|
|
|
|
|
|
- // Generated by ::accept
|
|
|
- int m_active_socket;
|
|
|
+ TCPSocket m_listener;
|
|
|
};
|
|
|
|
|
|
-} // namespace os
|
|
|
} // namespace crown
|