Add tcp server
This commit is contained in:
parent
bbf039a596
commit
5121c2a27b
@ -43,8 +43,8 @@ FetchContent_MakeAvailable(googletest)
|
|||||||
#
|
#
|
||||||
|
|
||||||
|
|
||||||
# add_executable(playground src/main.cpp src/tcp.cpp)
|
add_executable(playground src/main.cpp src/tcp.cpp)
|
||||||
# target_link_libraries(playground PRIVATE spdlog::spdlog)
|
target_link_libraries(playground PRIVATE spdlog::spdlog)
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
|
|||||||
@ -1,15 +1,48 @@
|
|||||||
import socket
|
import socket
|
||||||
|
import sys
|
||||||
|
import logging
|
||||||
|
from time import sleep
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
logging.basicConfig(format='%(asctime)s %(levelname)s %(message)s',
|
||||||
|
datefmt='%H:%M:%S',
|
||||||
|
level=logging.DEBUG,
|
||||||
|
stream=sys.stdout)
|
||||||
|
|
||||||
sock.connect(("localhost", 1234))
|
host = "localhost"
|
||||||
sock.sendall(b"asdf")
|
port = 65432
|
||||||
data = sock.recv(1024)
|
|
||||||
print(data.decode())
|
while True:
|
||||||
|
client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||||
|
client_socket.connect((host, port))
|
||||||
|
|
||||||
|
client_socket.send(b"""{
|
||||||
|
"jsonrpc": "2.0",
|
||||||
|
"id": "1",
|
||||||
|
"method": "SetLedPattern",
|
||||||
|
"params": {
|
||||||
|
"led": 19,
|
||||||
|
"pattern": 3.1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
""")
|
||||||
|
|
||||||
|
try:
|
||||||
|
res = client_socket.recv(1024)
|
||||||
|
if not res:
|
||||||
|
logging.info("Client disconnected")
|
||||||
|
break
|
||||||
|
|
||||||
|
logging.info(f"SetLedPattern response: {res.decode()}")
|
||||||
|
except socket.timeout:
|
||||||
|
logging.error("Connection timed out")
|
||||||
|
break
|
||||||
|
|
||||||
|
client_socket.close()
|
||||||
|
|
||||||
|
sleep(1)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
main()
|
main()
|
||||||
|
|
||||||
|
|||||||
172
src/main.cpp
172
src/main.cpp
@ -1,101 +1,103 @@
|
|||||||
#include "tcp.hpp"
|
#include "tcp.hpp"
|
||||||
|
#include <cstddef>
|
||||||
#include <spdlog/spdlog.h>
|
#include <spdlog/spdlog.h>
|
||||||
|
|
||||||
|
|
||||||
int main() {
|
int main() {
|
||||||
spdlog::set_level(spdlog::level::debug);
|
spdlog::set_level(spdlog::level::debug);
|
||||||
|
|
||||||
tcp::NonBlockingClient client;
|
tcp::NonBlockingServer server{{.port = 65432}};
|
||||||
|
if (auto res = server.start_listening(); !res) {
|
||||||
tcp::HostString host = {"192.168.0.12"};
|
spdlog::error("Failed to start server: {}", res.error());
|
||||||
uint16_t port = 123;
|
return 1;
|
||||||
|
|
||||||
spdlog::info("Connecting to {}:{}", host.data(), port);
|
|
||||||
|
|
||||||
if (!client.connect(host, port)) std::terminate();
|
|
||||||
|
|
||||||
tcp::ConnectionStatus status = tcp::ConnectionStatus::AttemptingConnection;
|
|
||||||
while (status == tcp::ConnectionStatus::AttemptingConnection) {
|
|
||||||
auto statusRes = client.get_last_connection_status();
|
|
||||||
if (!statusRes) std::terminate();
|
|
||||||
status = statusRes.value();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (status) {
|
spdlog::info("Server started, waiting for connections...");
|
||||||
case tcp::ConnectionStatus::Successful:
|
|
||||||
spdlog::info("Connected successfully");
|
|
||||||
break;
|
|
||||||
case tcp::ConnectionStatus::Refused:
|
|
||||||
spdlog::error("Connection refused");
|
|
||||||
std::terminate();
|
|
||||||
case tcp::ConnectionStatus::NoRouteToHost:
|
|
||||||
spdlog::error("No route to host");
|
|
||||||
std::terminate();
|
|
||||||
case tcp::ConnectionStatus::TimedOut:
|
|
||||||
spdlog::error("Connection timed out");
|
|
||||||
std::terminate();
|
|
||||||
case tcp::ConnectionStatus::HostUnreachable:
|
|
||||||
spdlog::error("Host unreachable");
|
|
||||||
std::terminate();
|
|
||||||
case tcp::ConnectionStatus::OtherError:
|
|
||||||
spdlog::error("Other error");
|
|
||||||
std::terminate();
|
|
||||||
case tcp::ConnectionStatus::AttemptingConnection:
|
|
||||||
spdlog::error("Attempting connection");
|
|
||||||
std::terminate();
|
|
||||||
}
|
|
||||||
|
|
||||||
char buffer[1024];
|
while (true) {
|
||||||
|
while (!server.next_client_available())
|
||||||
|
;
|
||||||
|
|
||||||
while (!client.data_available())
|
if (auto res = server.accept_next_client(); !res) {
|
||||||
;
|
spdlog::error("Failed to accept client: {}", res.error());
|
||||||
|
continue;
|
||||||
auto recvRes = client.recv(std::span<char>(buffer, sizeof(buffer)));
|
|
||||||
|
|
||||||
if (!recvRes) {
|
|
||||||
switch (recvRes.error()) {
|
|
||||||
case tcp::Error::FailedToCreateSocket:
|
|
||||||
spdlog::error("Failed to create socket");
|
|
||||||
case tcp::Error::FailedToStartConnecting:
|
|
||||||
spdlog::error("Failed to start connecting");
|
|
||||||
case tcp::Error::FailedToReadSocketError:
|
|
||||||
spdlog::error("Failed to read socket error");
|
|
||||||
case tcp::Error::FailedToResolveHost:
|
|
||||||
spdlog::error("Failed to resolve host");
|
|
||||||
case tcp::Error::OtherOperationInProgress:
|
|
||||||
spdlog::error("Other operation in progress");
|
|
||||||
case tcp::Error::SocketNotConnected:
|
|
||||||
spdlog::error("Socket not connected");
|
|
||||||
case tcp::Error::BufferFull:
|
|
||||||
spdlog::error("Buffer full");
|
|
||||||
case tcp::Error::NoDataAvailable:
|
|
||||||
spdlog::error("No data available");
|
|
||||||
case tcp::Error::SendFailed:
|
|
||||||
spdlog::error("Send failed");
|
|
||||||
case tcp::Error::RecvFailed:
|
|
||||||
spdlog::error("Recv failed");
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::terminate();
|
spdlog::info("Client connected");
|
||||||
}
|
|
||||||
|
|
||||||
spdlog::info("Received {} bytes", recvRes.value());
|
std::array<std::byte, 1024> buffer;
|
||||||
if (recvRes.value() > 0) {
|
|
||||||
|
while (!server.data_available())
|
||||||
|
;
|
||||||
|
|
||||||
|
auto recvRes = server.recv(std::span(buffer));
|
||||||
|
if (!recvRes) {
|
||||||
|
spdlog::error("Failed to receive data: {}", recvRes.error());
|
||||||
|
break;
|
||||||
|
}
|
||||||
spdlog::info("Received message: {}",
|
spdlog::info("Received message: {}",
|
||||||
std::string(buffer, recvRes.value()));
|
std::string(reinterpret_cast<const char*>(buffer.data()),
|
||||||
|
recvRes.value()));
|
||||||
|
|
||||||
|
const char* msg = "Hello, world!";
|
||||||
|
auto sendRes = server.send(std::as_bytes(std::span(msg, strlen(msg))));
|
||||||
|
|
||||||
|
if (!sendRes) {
|
||||||
|
spdlog::error("Failed to send message: {}", sendRes.error());
|
||||||
|
} else {
|
||||||
|
spdlog::info("Sent {} bytes", sendRes.value());
|
||||||
|
}
|
||||||
|
|
||||||
|
server.disconnect();
|
||||||
}
|
}
|
||||||
|
|
||||||
EAGAIN;
|
|
||||||
|
|
||||||
const char* msg = "Hello, world!";
|
|
||||||
auto sendRes = client.send(std::span<const char>(msg, strlen(msg)));
|
|
||||||
if (!sendRes) {
|
|
||||||
spdlog::error("Failed to send message");
|
|
||||||
std::terminate();
|
|
||||||
}
|
|
||||||
spdlog::info("Sent {} bytes", sendRes.value());
|
|
||||||
client.disconnect();
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// int main() {
|
||||||
|
// spdlog::set_level(spdlog::level::debug);
|
||||||
|
//
|
||||||
|
// tcp::NonBlockingClient client;
|
||||||
|
//
|
||||||
|
// tcp::HostString host = {"localhost"};
|
||||||
|
// uint16_t port = 65432;
|
||||||
|
//
|
||||||
|
// spdlog::info("Connecting to {}:{}", host.data(), port);
|
||||||
|
//
|
||||||
|
// if (!client.connect(host, port)) std::terminate();
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// int status = EINPROGRESS;
|
||||||
|
// while (status == EINPROGRESS) {
|
||||||
|
// auto statusRes = client.get_last_connection_status();
|
||||||
|
// if (!statusRes) std::terminate();
|
||||||
|
// status = statusRes.value();
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// char buffer[1024];
|
||||||
|
//
|
||||||
|
// while (!client.data_available())
|
||||||
|
// ;
|
||||||
|
//
|
||||||
|
// auto recvRes = client.recv(std::span<char>(buffer, sizeof(buffer)));
|
||||||
|
//
|
||||||
|
// if (!recvRes) {
|
||||||
|
// std::terminate();
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// spdlog::info("Received {} bytes", recvRes.value());
|
||||||
|
// if (recvRes.value() > 0) {
|
||||||
|
// spdlog::info("Received message: {}",
|
||||||
|
// std::string(buffer, recvRes.value()));
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// EINPROGRESS;
|
||||||
|
//
|
||||||
|
// const char* msg = "Hello, world!";
|
||||||
|
// auto sendRes = client.send(std::span<const char>(msg,
|
||||||
|
// strlen(msg))); if (!sendRes) {
|
||||||
|
// spdlog::error("Failed to send message");
|
||||||
|
// std::terminate();
|
||||||
|
// }
|
||||||
|
// spdlog::info("Sent {} bytes", sendRes.value());
|
||||||
|
// client.disconnect();
|
||||||
|
//
|
||||||
|
// return 0;
|
||||||
|
// }
|
||||||
|
|||||||
249
src/tcp.cpp
249
src/tcp.cpp
@ -18,8 +18,8 @@
|
|||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
|
|
||||||
std::expected<sockaddr_in, int> get_server_address(const char* host,
|
std::expected<sockaddr_in, int> resolve_remote_address(const char* host,
|
||||||
uint16_t port) {
|
uint16_t port) {
|
||||||
hostent* server = gethostbyname(host);
|
hostent* server = gethostbyname(host);
|
||||||
if (server == nullptr) {
|
if (server == nullptr) {
|
||||||
spdlog::error("tcp: get_host_by_name() failed with h_errno={}",
|
spdlog::error("tcp: get_host_by_name() failed with h_errno={}",
|
||||||
@ -36,6 +36,15 @@ std::expected<sockaddr_in, int> get_server_address(const char* host,
|
|||||||
return address;
|
return address;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline auto get_local_address(uint16_t port) -> sockaddr_in {
|
||||||
|
sockaddr_in address{};
|
||||||
|
address.sin_addr.s_addr = htonl(INADDR_ANY);
|
||||||
|
address.sin_family = AF_INET;
|
||||||
|
address.sin_port = htons(port);
|
||||||
|
return address;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
@ -43,11 +52,213 @@ std::expected<sockaddr_in, int> get_server_address(const char* host,
|
|||||||
namespace tcp {
|
namespace tcp {
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// Non blocking server
|
||||||
|
//
|
||||||
|
//
|
||||||
|
|
||||||
|
|
||||||
|
NonBlockingServer::NonBlockingServer(ServerSettings settings)
|
||||||
|
: m_settings{settings}, m_serverAddress(get_local_address(settings.port)) {
|
||||||
|
}
|
||||||
|
|
||||||
|
std::expected<void, int> NonBlockingServer::start_listening() {
|
||||||
|
/// Create socket
|
||||||
|
|
||||||
|
close_server_socket();
|
||||||
|
if (auto res = create_socket(); !res) {
|
||||||
|
return std::unexpected{res.error()};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Bin socket and start listening
|
||||||
|
|
||||||
|
if (::bind(m_serverSocket, (struct sockaddr*)&m_serverAddress,
|
||||||
|
sizeof(m_serverAddress)) != 0) {
|
||||||
|
spdlog::error("tcp::NonBlockingServer: bind() failed with errno={}",
|
||||||
|
errno);
|
||||||
|
close(m_serverSocket);
|
||||||
|
return std::unexpected{errno};
|
||||||
|
};
|
||||||
|
|
||||||
|
if (::listen(m_serverSocket, 1) != 0) {
|
||||||
|
spdlog::error("tcp::NonBlockingServer: listen() failed with errno={}",
|
||||||
|
errno);
|
||||||
|
close(m_serverSocket);
|
||||||
|
return std::unexpected{errno};
|
||||||
|
}
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
bool NonBlockingServer::next_client_available() {
|
||||||
|
int ret = poll(&m_serverPfdIn, 1, 0);
|
||||||
|
if (ret < 0) {
|
||||||
|
spdlog::error("tcp::NonBlockingServer: poll() failed with errno={}",
|
||||||
|
errno);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (m_serverPfdIn.revents & POLLIN);
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] std::expected<void, int> NonBlockingServer::accept_next_client() {
|
||||||
|
disconnect();
|
||||||
|
|
||||||
|
int result = ::accept(m_serverSocket, (struct sockaddr*)&m_clientAddress,
|
||||||
|
&m_clientAddrLen);
|
||||||
|
if (result < 0) return std::unexpected{errno};
|
||||||
|
|
||||||
|
m_clientSocket = result;
|
||||||
|
m_clientPfdIn.fd = m_clientSocket;
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
void NonBlockingServer::disconnect() {
|
||||||
|
close_client_socket();
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Do we need this?
|
||||||
|
// bool NonBlockingServer::socket_closed() const {
|
||||||
|
// }
|
||||||
|
|
||||||
|
[[nodiscard]] std::expected<std::array<char, 16>, int>
|
||||||
|
NonBlockingServer::get_client_ip_as_string() const {
|
||||||
|
std::array<char, 16> addrStr;
|
||||||
|
|
||||||
|
if (inet_ntop(AF_INET, &((struct sockaddr_in*)&m_clientAddress)->sin_addr,
|
||||||
|
addrStr.data(), addrStr.size()) == nullptr) {
|
||||||
|
spdlog::error(
|
||||||
|
"tcp::NonBlockingServer: inet_ntop() failed with errno={}", errno);
|
||||||
|
return std::unexpected{errno};
|
||||||
|
}
|
||||||
|
|
||||||
|
// if (inet_ntoa_r(((struct sockaddr_in*)&m_clientAddress)->sin_addr,
|
||||||
|
// addrStr.data(), addrStr.size()) == nullptr) {
|
||||||
|
// return std::unexpected{errno};
|
||||||
|
// }
|
||||||
|
|
||||||
|
return {addrStr};
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] std::expected<int, int>
|
||||||
|
NonBlockingServer::send(std::span<const std::byte> data) const {
|
||||||
|
const int bytesWritten =
|
||||||
|
::send(m_clientSocket, (const char*)data.data(), data.size(), 0);
|
||||||
|
|
||||||
|
if (bytesWritten <= 0) {
|
||||||
|
spdlog::error("tcp::NonBlockingServer: send() failed with errno={}",
|
||||||
|
errno);
|
||||||
|
return std::unexpected{errno};
|
||||||
|
}
|
||||||
|
|
||||||
|
return bytesWritten;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] std::expected<int, int>
|
||||||
|
NonBlockingServer::recv(std::span<std::byte> buffer) const {
|
||||||
|
const int bytesReceived =
|
||||||
|
::recv(m_clientSocket, (char*)buffer.data(), buffer.size(), 0);
|
||||||
|
|
||||||
|
if (bytesReceived < 0) {
|
||||||
|
spdlog::error("tcp::NonBlockingServer: recv() failed with errno={}",
|
||||||
|
errno);
|
||||||
|
return std::unexpected{errno};
|
||||||
|
}
|
||||||
|
return {bytesReceived};
|
||||||
|
}
|
||||||
|
|
||||||
|
bool NonBlockingServer::data_available() {
|
||||||
|
int ret = poll(&m_clientPfdIn, 1, 0);
|
||||||
|
if (ret < 0) {
|
||||||
|
spdlog::error("tcp::NonBlockingServer: poll() failed with errno={}",
|
||||||
|
errno);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (m_clientPfdIn.revents & POLLIN);
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] std::expected<void, int> NonBlockingServer::create_socket() {
|
||||||
|
m_serverSocket = socket(AF_INET, SOCK_STREAM, 0);
|
||||||
|
if (m_serverSocket < 1) {
|
||||||
|
spdlog::error("tcp::NonBlockingServer: socket() failed with errno={}",
|
||||||
|
errno);
|
||||||
|
return std::unexpected{errno};
|
||||||
|
}
|
||||||
|
|
||||||
|
m_serverPfdIn.fd = m_serverSocket;
|
||||||
|
|
||||||
|
auto currentFlags = fcntl(m_serverSocket, F_GETFL, 0);
|
||||||
|
if (currentFlags == -1) {
|
||||||
|
spdlog::error("tcp::NonBlockingServer: fcntl() failed with errno={}",
|
||||||
|
errno);
|
||||||
|
close_server_socket();
|
||||||
|
return std::unexpected{errno};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fcntl(m_serverSocket, F_SETFL, (currentFlags) | O_NONBLOCK) == -1) {
|
||||||
|
spdlog::error("tcp::NonBlockingServer: fcntl() failed with errno={}",
|
||||||
|
errno);
|
||||||
|
close_server_socket();
|
||||||
|
return std::unexpected{errno};
|
||||||
|
}
|
||||||
|
|
||||||
|
int flag = 1;
|
||||||
|
if (::setsockopt(m_serverSocket, SOL_SOCKET, SO_REUSEADDR, &flag,
|
||||||
|
sizeof(flag)) < 0) {
|
||||||
|
spdlog::error(
|
||||||
|
"tcp::NonBlockingServer: setsockopt() failed with errno={}", errno);
|
||||||
|
close_server_socket();
|
||||||
|
return std::unexpected{errno};
|
||||||
|
}
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
void NonBlockingServer::close_server_socket() {
|
||||||
|
if (shutdown(m_serverSocket, 0) < 0) {
|
||||||
|
spdlog::debug("tcp::NonBlockingServer: shutdown() failed with errno={}",
|
||||||
|
errno);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (close(m_serverSocket) < 0) {
|
||||||
|
spdlog::debug("tcp::NonBlockingServer: close() failed with errno={}",
|
||||||
|
errno);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_serverSocket = -1;
|
||||||
|
m_serverPfdIn.fd = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void NonBlockingServer::close_client_socket() {
|
||||||
|
if (shutdown(m_clientSocket, 0) < 0) {
|
||||||
|
spdlog::debug("tcp::NonBlockingServer: shutdown() failed with errno={}",
|
||||||
|
errno);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (close(m_clientSocket) < 0) {
|
||||||
|
spdlog::debug("tcp::NonBlockingServer: close() failed with errno={}",
|
||||||
|
errno);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_clientSocket = -1;
|
||||||
|
m_clientPfdIn.fd = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// Non blocking client
|
||||||
|
//
|
||||||
|
//
|
||||||
|
|
||||||
|
|
||||||
[[nodiscard]] std::expected<void, int>
|
[[nodiscard]] std::expected<void, int>
|
||||||
NonBlockingClient::connect(HostString host, uint16_t port) {
|
NonBlockingClient::connect(HostString host, uint16_t port) {
|
||||||
/// Resolve host
|
/// Resolve host
|
||||||
|
|
||||||
auto addrRes = get_server_address(host.data(), port);
|
auto addrRes = resolve_remote_address(host.data(), port);
|
||||||
if (!addrRes) return std::unexpected{addrRes.error()};
|
if (!addrRes) return std::unexpected{addrRes.error()};
|
||||||
sockaddr_in serverAddress = addrRes.value();
|
sockaddr_in serverAddress = addrRes.value();
|
||||||
|
|
||||||
@ -63,7 +274,7 @@ NonBlockingClient::connect(HostString host, uint16_t port) {
|
|||||||
int connectRes = ::connect(m_socket, (struct sockaddr*)&serverAddress,
|
int connectRes = ::connect(m_socket, (struct sockaddr*)&serverAddress,
|
||||||
sizeof(serverAddress));
|
sizeof(serverAddress));
|
||||||
|
|
||||||
if (connectRes < 0) {
|
if (connectRes < 0 && errno != EINPROGRESS) {
|
||||||
spdlog::error("tcp::NonBlockingClient: connect() failed with errno={}",
|
spdlog::error("tcp::NonBlockingClient: connect() failed with errno={}",
|
||||||
errno);
|
errno);
|
||||||
return std::unexpected{errno};
|
return std::unexpected{errno};
|
||||||
@ -74,7 +285,7 @@ NonBlockingClient::connect(HostString host, uint16_t port) {
|
|||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] std::expected<ConnectionStatus, int>
|
[[nodiscard]] std::expected<int, int>
|
||||||
NonBlockingClient::get_last_connection_status() {
|
NonBlockingClient::get_last_connection_status() {
|
||||||
if (!m_startedNewConAttemt) return m_lastKnownConStatus;
|
if (!m_startedNewConAttemt) return m_lastKnownConStatus;
|
||||||
|
|
||||||
@ -82,7 +293,7 @@ NonBlockingClient::get_last_connection_status() {
|
|||||||
|
|
||||||
auto pendingOpRes = new_error_codes_available();
|
auto pendingOpRes = new_error_codes_available();
|
||||||
if (!pendingOpRes) return std::unexpected{pendingOpRes.error()};
|
if (!pendingOpRes) return std::unexpected{pendingOpRes.error()};
|
||||||
if (pendingOpRes.value()) return ConnectionStatus::AttemptingConnection;
|
if (!pendingOpRes.value()) return EINPROGRESS;
|
||||||
|
|
||||||
m_startedNewConAttemt = false;
|
m_startedNewConAttemt = false;
|
||||||
|
|
||||||
@ -97,25 +308,9 @@ NonBlockingClient::get_last_connection_status() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (err == 0) {
|
if (err == 0) {
|
||||||
m_lastKnownConStatus = ConnectionStatus::Successful;
|
m_lastKnownConStatus = EISCONN;
|
||||||
} else {
|
} else {
|
||||||
switch (err) {
|
m_lastKnownConStatus = err;
|
||||||
case ECONNREFUSED:
|
|
||||||
m_lastKnownConStatus = ConnectionStatus::Refused;
|
|
||||||
break;
|
|
||||||
case ENETUNREACH:
|
|
||||||
m_lastKnownConStatus = ConnectionStatus::NoRouteToHost;
|
|
||||||
break;
|
|
||||||
case ETIMEDOUT:
|
|
||||||
m_lastKnownConStatus = ConnectionStatus::TimedOut;
|
|
||||||
break;
|
|
||||||
case EHOSTUNREACH:
|
|
||||||
m_lastKnownConStatus = ConnectionStatus::HostUnreachable;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
m_lastKnownConStatus = ConnectionStatus::OtherError;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return m_lastKnownConStatus;
|
return m_lastKnownConStatus;
|
||||||
@ -130,7 +325,7 @@ NonBlockingClient::send(std::span<const char> data) {
|
|||||||
// TODO: Do we need this?
|
// TODO: Do we need this?
|
||||||
auto conRes = get_last_connection_status();
|
auto conRes = get_last_connection_status();
|
||||||
if (!conRes) return std::unexpected{conRes.error()};
|
if (!conRes) return std::unexpected{conRes.error()};
|
||||||
if (conRes != ConnectionStatus::Successful) {
|
if (conRes != EISCONN) {
|
||||||
spdlog::error("tcp::NonBlockingClient: Socket not connected");
|
spdlog::error("tcp::NonBlockingClient: Socket not connected");
|
||||||
return std::unexpected{ENOTCONN};
|
return std::unexpected{ENOTCONN};
|
||||||
}
|
}
|
||||||
@ -150,7 +345,7 @@ NonBlockingClient::send(std::span<const char> data) {
|
|||||||
bool NonBlockingClient::data_available() {
|
bool NonBlockingClient::data_available() {
|
||||||
auto conRes = get_last_connection_status();
|
auto conRes = get_last_connection_status();
|
||||||
if (!conRes) return false;
|
if (!conRes) return false;
|
||||||
if (conRes != ConnectionStatus::Successful) return false;
|
if (conRes != EISCONN) return false;
|
||||||
|
|
||||||
int ret = poll(&m_pfdIn, 1, 0);
|
int ret = poll(&m_pfdIn, 1, 0);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
@ -230,7 +425,7 @@ NonBlockingClient::new_error_codes_available() {
|
|||||||
return std::unexpected{errno};
|
return std::unexpected{errno};
|
||||||
}
|
}
|
||||||
|
|
||||||
return (ret == 0);
|
return (ret != 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
78
src/tcp.hpp
78
src/tcp.hpp
@ -24,27 +24,65 @@
|
|||||||
namespace tcp {
|
namespace tcp {
|
||||||
|
|
||||||
|
|
||||||
enum class ConnectionStatus {
|
using HostString = std::array<char, 64>;
|
||||||
AttemptingConnection,
|
|
||||||
Successful,
|
//
|
||||||
Refused,
|
//
|
||||||
NoRouteToHost,
|
// Non blocking server
|
||||||
TimedOut,
|
//
|
||||||
HostUnreachable,
|
//
|
||||||
OtherError,
|
|
||||||
|
struct ServerSettings {
|
||||||
|
uint16_t port = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
class NonBlockingServer {
|
||||||
|
public:
|
||||||
|
NonBlockingServer(ServerSettings settings);
|
||||||
|
|
||||||
|
[[nodiscard]] std::expected<void, int> start_listening();
|
||||||
|
|
||||||
|
bool next_client_available();
|
||||||
|
[[nodiscard]] std::expected<void, int> accept_next_client();
|
||||||
|
void disconnect();
|
||||||
|
// bool socket_closed() const;
|
||||||
|
|
||||||
|
[[nodiscard]] std::expected<int, int>
|
||||||
|
send(std::span<const std::byte> data) const;
|
||||||
|
|
||||||
|
[[nodiscard]] std::expected<int, int>
|
||||||
|
recv(std::span<std::byte> buffer) const;
|
||||||
|
|
||||||
|
bool data_available();
|
||||||
|
|
||||||
|
[[nodiscard]] std::expected<std::array<char, 16>, int>
|
||||||
|
get_client_ip_as_string() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
ServerSettings m_settings;
|
||||||
|
const struct sockaddr_in m_serverAddress;
|
||||||
|
int m_serverSocket = -1;
|
||||||
|
|
||||||
|
pollfd m_serverPfdIn = {.fd = -1, .events = POLLIN};
|
||||||
|
pollfd m_clientPfdIn = {.fd = -1, .events = POLLIN};
|
||||||
|
|
||||||
|
int m_clientSocket = -1;
|
||||||
|
struct sockaddr m_clientAddress;
|
||||||
|
socklen_t m_clientAddrLen = sizeof(m_clientAddress);
|
||||||
|
|
||||||
|
|
||||||
|
[[nodiscard]] std::expected<void, int> create_socket();
|
||||||
|
void close_server_socket();
|
||||||
|
void close_client_socket();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
using HostString = std::array<char, 64>;
|
|
||||||
|
|
||||||
// struct ServerSettings {
|
|
||||||
// uint16_t port = 0;
|
|
||||||
// };
|
|
||||||
//
|
//
|
||||||
// class NonBlockingServer {
|
//
|
||||||
// public:
|
// Non blocking client
|
||||||
// NonBlockingServer(ServerSettings settings);
|
//
|
||||||
// };
|
//
|
||||||
|
|
||||||
|
|
||||||
class NonBlockingClient {
|
class NonBlockingClient {
|
||||||
public:
|
public:
|
||||||
@ -55,11 +93,12 @@ public:
|
|||||||
|
|
||||||
void disconnect();
|
void disconnect();
|
||||||
|
|
||||||
[[nodiscard]] std::expected<ConnectionStatus, int>
|
[[nodiscard]] std::expected<int, int>
|
||||||
get_last_connection_status();
|
get_last_connection_status();
|
||||||
|
|
||||||
bool data_available();
|
bool data_available();
|
||||||
|
|
||||||
|
// TODO: Make this std::byte
|
||||||
[[nodiscard]] std::expected<int, int> recv(std::span<char> buffer);
|
[[nodiscard]] std::expected<int, int> recv(std::span<char> buffer);
|
||||||
[[nodiscard]] std::expected<int, int> send(std::span<const char> data);
|
[[nodiscard]] std::expected<int, int> send(std::span<const char> data);
|
||||||
|
|
||||||
@ -69,7 +108,7 @@ private:
|
|||||||
pollfd m_pfdIn = {.fd = -1, .events = POLLIN};
|
pollfd m_pfdIn = {.fd = -1, .events = POLLIN};
|
||||||
|
|
||||||
bool m_startedNewConAttemt = false;
|
bool m_startedNewConAttemt = false;
|
||||||
ConnectionStatus m_lastKnownConStatus = ConnectionStatus::OtherError;
|
int m_lastKnownConStatus = 0;
|
||||||
|
|
||||||
void close_socket();
|
void close_socket();
|
||||||
|
|
||||||
@ -77,4 +116,5 @@ private:
|
|||||||
[[nodiscard]] std::expected<bool, int> new_error_codes_available();
|
[[nodiscard]] std::expected<bool, int> new_error_codes_available();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
} // namespace tcp
|
} // namespace tcp
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user