#include "../Common.h" #include "../Lockable.h" #include "TCPListener.h" #include "IListenerInterface.h" #include "TCPClient.h" #include "NetworkManager.h" #include "NetworkMessage.h" #include "PacketReader.h" #include "logging/Logger.h" #include #include #include #include #include #include namespace Feather::Network { class TCPSocket { public: TCPSocket() { m_socket = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP); // Can't create IPv6 socket? Create an IPv4 one. if (!(m_ipv6 = IsValid())) m_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); } ~TCPSocket() { Close(); } bool IsIPV6() const { return m_ipv6; } bool MarkReusable() { return evutil_make_listen_socket_reuseable(m_socket) == 0; } bool MarkDualBind() { constexpr uint32_t zero = 0; return setsockopt(m_socket, IPPROTO_IPV6, IPV6_V6ONLY, reinterpret_cast(&zero), sizeof(zero)) == 0; } bool Bind(uint16_t port) { sockaddr_in6 name_ipv6 = {}; name_ipv6.sin6_family = AF_INET6; name_ipv6.sin6_port = ntohs(port); sockaddr_in name_ipv4 = {}; name_ipv4.sin_family = AF_INET; name_ipv4.sin_port = ntohs(port); return m_ipv6 ? BindGeneric(name_ipv6) : BindGeneric(name_ipv4); } bool MarkNonBlocking() { return evutil_make_socket_nonblocking(m_socket) == 0; } bool Close() { if (!IsValid()) return true; return evutil_closesocket(m_socket) == 0; } template evconnlistener* Listen(evconnlistener_cb callback, T* self) { if (listen(m_socket, SOMAXCONN) != 0) return nullptr; return evconnlistener_new( NetworkManager::Instance().GetEventBase(), callback, reinterpret_cast(self), LEV_OPT_CLOSE_ON_FREE | LEV_OPT_REUSEABLE, 0, m_socket); } bool IsValid() const { return m_socket != EVUTIL_INVALID_SOCKET; } private: template bool BindGeneric(const T& name) { return bind(m_socket, reinterpret_cast(&name), sizeof(name)) == 0; } SocketHandle m_socket; bool m_ipv6; }; TCPClient::TCPClient(TCPListener* parent, SocketHandle socket) : m_parent(parent) , m_bufferEvent(bufferevent_socket_new(NetworkManager::Instance().GetEventBase(), socket, BEV_OPT_CLOSE_ON_FREE | BEV_OPT_THREADSAFE | BEV_OPT_DEFER_CALLBACKS | BEV_OPT_UNLOCK_CALLBACKS)) { printf("Created TCPListenerClient\n"); bufferevent_setcb(m_bufferEvent, [](bufferevent* be, void* self) { static_cast(self)->ReadCallback(); }, [](bufferevent* be, void* self) { static_cast(self)->WriteCallback(); }, [](bufferevent* be, int16_t event, void* self) { static_cast(self)->EventCallback(event); }, this); bufferevent_enable(m_bufferEvent, EV_READ | EV_WRITE); } TCPClient::~TCPClient() { bufferevent_free(m_bufferEvent); } void TCPClient::ReadCallback() { evbuffer* buffer = bufferevent_get_input(m_bufferEvent); const size_t size = evbuffer_get_length(buffer); auto [data, lock] = m_data.borrow(); const size_t offset = data.size(); data.resize(offset + size); if (evbuffer_remove(buffer, &data[offset], size) != size) { printf("fuck"); return; } } void TCPClient::WriteCallback() { } void TCPClient::EventCallback(int16_t event) { if (event & BEV_EVENT_ERROR) { const char* errorString = evutil_socket_error_to_string(EVUTIL_SOCKET_ERROR()); Log_Error("TCPClient: %s", errorString); } if (event & BEV_EVENT_EOF) m_parent->OnClientDisconnected(this); } void TCPClient::Write(const uint8_t* data, size_t size) { if (bufferevent_write(m_bufferEvent, data, size) != 0) printf("Fuck!"); } LockableVector& TCPClient::GetData() { return m_data; } TCPListener::TCPListener(uint16_t port, IListenerInterface* callbacks) : m_callbacks(callbacks) { // Setup the listen socket. auto socket = std::make_unique(); if (!socket->IsValid()) return; if (!socket->MarkReusable()) return; if (socket->IsIPV6() && !socket->MarkDualBind()) return; if (!socket->Bind(port)) return; if (!socket->MarkNonBlocking()) return; auto ListenerCallback = [](evconnlistener* evListener, SocketHandle socket, sockaddr* addr, int len, void* self) { TCPListener* listener = static_cast(self); listener->OnClientConnect(std::make_unique(listener, socket)); }; if (!socket->Listen(ListenerCallback, this)) return; m_socket = std::move(socket); } TCPListener::~TCPListener() { } void TCPListener::OnClientConnect(TCPClientHandle&& client) { m_callbacks->OnClientConnect(std::move(client)); } void TCPListener::OnClientDisconnected(const TCPClient* client) { m_callbacks->OnClientDisconnect(client); } }