diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 65e6d5b..abdca54 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -31,16 +31,16 @@ jobs: with: python-version: '3.x' - - name: Install dependencies (Ubuntu) - if: startsWith(matrix.config.os, 'ubuntu') - run: | - sudo apt-get update - sudo apt-get install -y libboost-all-dev - - - name: Install dependencies (macOS) - if: startsWith(matrix.config.os, 'macos') - run: | - brew install boost + # - name: Install dependencies (Ubuntu) + # if: startsWith(matrix.config.os, 'ubuntu') + # run: | + # sudo apt-get update + # sudo apt-get install -y libboost-all-dev + + # - name: Install dependencies (macOS) + # if: startsWith(matrix.config.os, 'macos') + # run: | + # brew install boost - name: Install dependencies (Windows) if: startsWith(matrix.config.os, 'windows') diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index fa58b5f..f7a01ea 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -29,21 +29,21 @@ jobs: with: python-version: '3.x' - - name: Install dependencies (Ubuntu) - if: startsWith(matrix.config.os, 'ubuntu') - run: | - sudo apt-get update - sudo apt-get install -y libboost-all-dev + # - name: Install dependencies (Ubuntu) + # if: startsWith(matrix.config.os, 'ubuntu') + # run: | + # sudo apt-get update + # sudo apt-get install -y libboost-all-dev - - name: Install dependencies (macOS) - if: startsWith(matrix.config.os, 'macos') - run: | - brew install boost + # - name: Install dependencies (macOS) + # if: startsWith(matrix.config.os, 'macos') + # run: | + # brew install boost - - name: Install dependencies (Windows) - if: startsWith(matrix.config.os, 'windows') - run: | - choco install boost-msvc-14.2 + # - name: Install dependencies (Windows) + # if: startsWith(matrix.config.os, 'windows') + # run: | + # choco install boost-msvc-14.2 - name: Set up CMake (All platforms) uses: lukka/get-cmake@latest diff --git a/CMakeLists.txt b/CMakeLists.txt index 4dd8fb4..1a8a735 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,9 +4,6 @@ project(SerialNetworkBridge LANGUAGES CXX) set(CMAKE_CXX_STANDARD 14) set(CMAKE_CXX_STANDARD_REQUIRED ON) -# Find Boost libraries -find_package(Boost 1.65 REQUIRED COMPONENTS system program_options log_setup log date_time) - include(FetchContent) FetchContent_Declare( googletest @@ -23,48 +20,22 @@ endif() include_directories(${CMAKE_SOURCE_DIR}/include) -add_library(logging src/logging.cpp) -target_include_directories(logging PUBLIC ${CMAKE_SOURCE_DIR}/include) -target_link_libraries(logging PUBLIC - Boost::log - Boost::log_setup - Boost::date_time +# Create core library without Logger.cpp, which is now in the logging library +add_library(core_lib + src/Logger.cpp + src/SerialServer.cpp + src/SerialClient.cpp + src/VirtualSerialPort.cpp + src/SerialPort.cpp + src/TCPServer.cpp ) - -add_library(core_lib src/logging.cpp src/SerialServer.cpp src/SerialClient.cpp src/VirtualSerialPort.cpp) target_include_directories(core_lib PUBLIC ${CMAKE_SOURCE_DIR}/include) -target_link_libraries(core_lib PRIVATE - Boost::system - Boost::program_options - Boost::log - Boost::log_setup - Boost::date_time - logging +target_link_libraries(core_lib PUBLIC pthread ) +# Create the executable add_executable(ser2net2ser src/main.cpp) target_link_libraries(ser2net2ser PRIVATE core_lib - logging - Boost::system - Boost::program_options - Boost::log - Boost::log_setup - Boost::date_time ) - -# # TODO: REENABLE TESTS -# enable_testing() -# add_executable(test_app tests/test_server.cpp) -# target_compile_definitions(test_app PRIVATE UNIT_TEST) -# target_link_libraries(test_app PRIVATE -# serial_server_lib # this includes server.cpp -# Boost::system -# GTest::gtest_main -# GTest::gtest -# GTest::gmock -# ) - -# include(GoogleTest) -# gtest_discover_tests(test_app EXTRA_ARGS --gtest_color=yes) diff --git a/Dockerfile b/Dockerfile index de100ca..f2946b6 100644 --- a/Dockerfile +++ b/Dockerfile @@ -6,11 +6,7 @@ WORKDIR /app # Install build dependencies RUN apt-get update && apt-get install -y \ g++ \ - cmake \ - libboost-system-dev \ - libboost-program-options-dev \ - libboost-log-dev \ - libboost-date-time-dev + cmake # Copy source code COPY . /app @@ -23,14 +19,6 @@ FROM ubuntu:20.04 WORKDIR /app -# Install runtime dependencies only -RUN apt-get update && apt-get install -y \ - libboost-system1.71.0 \ - libboost-program-options1.71.0 \ - libboost-log1.71.0 \ - libboost-date-time1.71.0 && \ - rm -rf /var/lib/apt/lists/* - # Copy binaries from the builder stage COPY --from=builder /app/ser2net2ser /usr/local/bin/ser2net2ser diff --git a/README.md b/README.md index a518df8..a1b5811 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,12 @@ # ser2net2ser -> WIP: server seems to work fine, client still has some issues +## Motivation + +I've been using `socat` + `ser2net` for sharing serial devices over network for some time, mainly in my other project -> [kubeserial]. I've run into few limitations so I created this project to recreate the behaviour of `socat` + `ser2net` and then solve connection issues I've been having. ## Requirements - Linux or macOS operating system -- Boost libraries (system, program_options, log_setup, log, date_time) - CMake for building the project - Docker (optional) for containerization @@ -66,3 +67,7 @@ Dockerfiles for both the server and client are included. Build and run the conta ```bash make build-images ``` + + + +[kubeserial]: https://github.com/janekbaraniewski/kubeserial "KubeSerial" diff --git a/include/ISerialPort.h b/include/ISerialPort.h index 1777a15..5c1534e 100644 --- a/include/ISerialPort.h +++ b/include/ISerialPort.h @@ -2,17 +2,15 @@ #define ISERIALPORT_H #include -#include +#include -using namespace boost::asio; using std::string; class ISerialPort { public: - virtual void open(const string& device, boost::asio::serial_port_base::baud_rate baudRate) = 0; - // virtual void set_option(const serial_port_base::baud_rate& option) = 0; - virtual void async_read_some(const boost::asio::mutable_buffer& buffer, std::function handler) = 0; - virtual void async_write(const boost::asio::const_buffer& buffer, std::function handler) = 0; + virtual void open(const string& device, unsigned int baudRate) = 0; + virtual ssize_t async_read_some(char* buffer, size_t size) = 0; + virtual ssize_t async_write(const char* buffer, size_t size) = 0; virtual ~ISerialPort() = default; }; diff --git a/include/Logger.h b/include/Logger.h new file mode 100644 index 0000000..a217781 --- /dev/null +++ b/include/Logger.h @@ -0,0 +1,37 @@ +#ifndef LOGGER_H +#define LOGGER_H + +#include +#include +#include + +class Logger { +public: + enum class Level { + Info, + Warning, + Error + }; + +private: + std::ostringstream stream; + Level logLevel; + static std::mutex mtx; + +public: + Logger(Level level = Level::Info); + ~Logger(); + + // Delete copy constructor and assignment operator + Logger(const Logger&) = delete; + Logger& operator=(const Logger&) = delete; + Logger& operator<<(std::ostream& (*pf)(std::ostream&)); + static std::string levelToString(Level level); + + template + Logger& operator<<(const T& msg); +}; + +#include "Logger.inl" // Include template implementation + +#endif // LOGGER_H diff --git a/include/Logger.inl b/include/Logger.inl new file mode 100644 index 0000000..bd55e53 --- /dev/null +++ b/include/Logger.inl @@ -0,0 +1,11 @@ +template +inline Logger& Logger::operator<<(const T& msg) { + stream << msg; + return *this; +} + +// Handle ostream manipulators like std::endl +inline Logger& Logger::operator<<(std::ostream& (*pf)(std::ostream&)) { + stream << pf; + return *this; +} diff --git a/include/RealSerialPort.h b/include/RealSerialPort.h deleted file mode 100644 index 38806fb..0000000 --- a/include/RealSerialPort.h +++ /dev/null @@ -1,55 +0,0 @@ -#ifndef REALSERIALPORT_H -#define REALSERIALPORT_H - -#include "ISerialPort.h" -#include -#include - -using boost::asio::serial_port_base; - -class RealSerialPort : public ISerialPort { -public: - explicit RealSerialPort(boost::asio::io_service& io) : port(io) {} - - void open(const std::string& device, serial_port_base::baud_rate baudRate) override { - BOOST_LOG_TRIVIAL(info) << "Setting up connection to serial port " << device; - try { - port.open(device); - setSerialOptions(baudRate); - } catch (const boost::system::system_error& e) { - BOOST_LOG_TRIVIAL(error) << "Failed to open serial port: " << e.what(); - } - } - - void setSerialOptions(serial_port_base::baud_rate baudRate) { - BOOST_LOG_TRIVIAL(info) << "Setting connection options."; - port.set_option(baudRate); - port.set_option(serial_port_base::character_size(8)); - port.set_option(serial_port_base::parity(serial_port_base::parity::none)); - port.set_option(serial_port_base::stop_bits(serial_port_base::stop_bits::one)); - port.set_option(serial_port_base::flow_control(serial_port_base::flow_control::none)); - } - - void async_read_some(const boost::asio::mutable_buffer& buffer, std::function handler) override { - // BOOST_LOG_TRIVIAL(info) << "Read some."; - if (!port.is_open()) { - BOOST_LOG_TRIVIAL(error) << "Attempt to read from a closed serial port."; - return; - } - port.async_read_some(buffer, handler); - } - - void async_write(const boost::asio::const_buffer& buffer, std::function handler) override { - BOOST_LOG_TRIVIAL(info) << "write some some."; - if (!port.is_open()) { - BOOST_LOG_TRIVIAL(error) << "Attempt to write to a closed serial port."; - return; - } - boost::asio::async_write(port, buffer, handler); - } - -private: - boost::asio::serial_port port; -}; - -#endif // REALSERIALPORT_H diff --git a/include/SerialClient.h b/include/SerialClient.h index f8e5933..2f6cb4c 100644 --- a/include/SerialClient.h +++ b/include/SerialClient.h @@ -5,8 +5,6 @@ #include "VirtualSerialPort.h" #include "SocketClient.h" -using namespace boost::asio; -using ip::tcp; using std::string; class SerialClient { @@ -15,7 +13,7 @@ class SerialClient { VirtualSerialPort vsp_; public: - SerialClient(boost::asio::io_service& io_service, const std::string& server_ip, unsigned short server_port, const std::string& vsp_name); + SerialClient(const std::string& server_ip, unsigned short server_port, const std::string& vsp_name); SocketClient socketClient_; void run(); }; diff --git a/include/SerialPort.h b/include/SerialPort.h new file mode 100644 index 0000000..e7fd541 --- /dev/null +++ b/include/SerialPort.h @@ -0,0 +1,33 @@ +#ifndef SERIALPORT_H +#define SERIALPORT_H + +#include +#include +#include +#include +#include + +#include "Logger.h" + +class SerialPort { +private: + int serial_fd; + std::thread read_thread; + std::atomic keep_reading; + std::queue read_buffer; + std::mutex mtx; + std::condition_variable cv; + + void readLoop(); + void configurePort(int baud_rate); + +public: + SerialPort(const std::string& device, int baud_rate); + ~SerialPort(); + void startReading(); + void stopReading(); + void writeData(const std::string& data); + std::string readData(); +}; + +#endif // SERIALPORT_H diff --git a/include/SerialServer.h b/include/SerialServer.h index e640f49..4827c38 100644 --- a/include/SerialServer.h +++ b/include/SerialServer.h @@ -1,27 +1,25 @@ #ifndef SERIALSERVER_H #define SERIALSERVER_H -#include "RealSerialPort.h" -#include +#include +#include +#include +#include #include +#include "SerialPort.h" +#include "TCPServer.h" + class SerialServer { public: - SerialServer(boost::asio::io_service& io_service, const std::string& device, unsigned int baud_rate); + SerialServer(const std::string& device, unsigned int baudRate, unsigned int port); + ~SerialServer(); void run(); private: - void start_accept(); - void handle_session(); - void async_read_socket(); - void async_read_serial(); - - boost::asio::io_service& io_service_; - boost::asio::ip::tcp::acceptor acceptor_; - boost::asio::ip::tcp::socket socket_; - RealSerialPort serial_port_; - std::array buffer_; + SerialPort serial_port_; + TcpServer server_; }; #endif // SERIALSERVER_H diff --git a/include/SocketClient.h b/include/SocketClient.h index 5c2422d..15eec3a 100644 --- a/include/SocketClient.h +++ b/include/SocketClient.h @@ -21,25 +21,27 @@ class SocketClient { } bool connectToServer(const char* server_ip, int server_port) { + Logger(Logger::Level::Info) << "Start connecting to socet server:" << server_ip << ":" << server_port; sock_fd = socket(AF_INET, SOCK_STREAM, 0); if (sock_fd == -1) { - std::cerr << "Error creating socket: " << strerror(errno) << std::endl; + Logger(Logger::Level::Error) << "Error creating socket: " << strerror(errno) << std::endl; return false; } server_addr.sin_family = AF_INET; server_addr.sin_port = htons(server_port); if (inet_pton(AF_INET, server_ip, &server_addr.sin_addr) <= 0) { - std::cerr << "Invalid address/ Address not supported" << std::endl; + Logger(Logger::Level::Error) << "Invalid address/ Address not supported" << std::endl; return false; } - if (::connect(sock_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) { - std::cerr << "Connection Failed: " << strerror(errno) << std::endl; + if (connect(sock_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) { + Logger(Logger::Level::Error) << "Connection Failed: " << strerror(errno) << std::endl; + exit(1); return false; } - std::cout << "Connected to server at " << server_ip << ":" << server_port << std::endl; + Logger(Logger::Level::Info) << "Connected to server at " << server_ip << ":" << server_port << std::endl; return true; } diff --git a/include/TCPServer.h b/include/TCPServer.h new file mode 100644 index 0000000..8495a99 --- /dev/null +++ b/include/TCPServer.h @@ -0,0 +1,29 @@ +#ifndef TCPSERVER_H +#define TCPSERVER_H + +#include +#include +#include +#include +#include "SerialPort.h" +#include "Logger.h" + +class TcpServer { +public: + TcpServer(int port, SerialPort& serial); + ~TcpServer(); + + void run(); + void stop(); + +private: + void handleClient(int client_socket); + + int server_fd_; + int port_; + bool is_running_; + struct sockaddr_in address_; + SerialPort& serial_; +}; + +#endif // TCPSERVER_H diff --git a/include/VirtualSerialPort.h b/include/VirtualSerialPort.h index a094e8d..86b5ac2 100644 --- a/include/VirtualSerialPort.h +++ b/include/VirtualSerialPort.h @@ -2,23 +2,22 @@ #define VIRTUALSERIALPORT_H #include "common.hpp" +#include #include +#include class VirtualSerialPort { public: - boost::asio::posix::stream_descriptor master_fd_; + int master_fd_raw_; - VirtualSerialPort(boost::asio::io_context& io_context, const std::string& device); + VirtualSerialPort(const std::string& device); ~VirtualSerialPort(); ssize_t async_read(char* buffer, unsigned int length); ssize_t async_write(const char* buffer, unsigned int length); - void close(); private: - boost::asio::posix::stream_descriptor slave_fd_; - // boost::array buffer_; - // std::array read_buffer_; + int slave_fd_raw_; std::string device_name_; std::mutex mutex_; diff --git a/include/common.hpp b/include/common.hpp index ca65d32..1f98fc3 100644 --- a/include/common.hpp +++ b/include/common.hpp @@ -8,10 +8,6 @@ #include #include -#include -#include -#include - -#include "logging.h" +#include "Logger.h" #endif // COMMON_HPP diff --git a/include/logging.h b/include/logging.h deleted file mode 100644 index 59e8f88..0000000 --- a/include/logging.h +++ /dev/null @@ -1,23 +0,0 @@ -#ifndef LOGGING_H -#define LOGGING_H - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace logging = boost::log; -namespace src = boost::log::sources; -namespace expr = boost::log::expressions; -namespace keywords = boost::log::keywords; - -extern src::severity_logger lg; - -void init_logging(); - -#endif // LOGGING_H diff --git a/src/Logger.cpp b/src/Logger.cpp new file mode 100644 index 0000000..ef9166d --- /dev/null +++ b/src/Logger.cpp @@ -0,0 +1,27 @@ +#include "Logger.h" +#include +#include +#include + +std::mutex Logger::mtx; + +Logger::Logger(Level level) : logLevel(level) {} + +Logger::~Logger() { + mtx.lock(); + auto now = std::chrono::system_clock::now(); + auto now_c = std::chrono::system_clock::to_time_t(now); + std::tm now_tm = *std::localtime(&now_c); + + std::cout << std::put_time(&now_tm, "%Y-%m-%d %H:%M:%S") << " " << levelToString(logLevel) << " " << stream.str() << std::endl; + mtx.unlock(); +} + +std::string Logger::levelToString(Level level) { + switch (level) { + case Level::Info: return "[info]"; + case Level::Warning: return "[warning]"; + case Level::Error: return "[error]"; + } + return "[info]"; +} diff --git a/src/SerialClient.cpp b/src/SerialClient.cpp index 93d0c74..73f1f8f 100644 --- a/src/SerialClient.cpp +++ b/src/SerialClient.cpp @@ -1,48 +1,45 @@ #include "SerialClient.h" -using namespace boost::asio; -using namespace boost::program_options; -using ip::tcp; using std::string; using std::cout; using std::cerr; using std::endl; -SerialClient::SerialClient(boost::asio::io_service& io_service, const std::string& server_ip, unsigned short server_port, const std::string& vsp_name) - : vsp_(io_service, vsp_name) { - std::cout << "Initializing client..."; - std::cout << "Connecting to server at " << server_ip << ":" << server_port; +SerialClient::SerialClient(const std::string& server_ip, unsigned short server_port, const std::string& vsp_name) + : vsp_(vsp_name) { + Logger(Logger::Level::Info) << "Initializing client..."; + Logger(Logger::Level::Info) << "Connecting to server at " << server_ip << ":" << server_port; socketClient_.connectToServer(server_ip.c_str(), server_port); - std::cout << "Connected to server."; - std::cout << "Opening virtual serial port: " << vsp_name; + Logger(Logger::Level::Info) << "Connected to server."; + Logger(Logger::Level::Info) << "Opening virtual serial port: " << vsp_name; } void SerialClient::run() { fd_set read_fds; char buffer[256]; - int max_fd = std::max(vsp_.master_fd_.native_handle(), socketClient_.sock_fd) + 1; + int max_fd = std::max(vsp_.master_fd_raw_, socketClient_.sock_fd) + 1; while (true) { FD_ZERO(&read_fds); - FD_SET(vsp_.master_fd_.native_handle(), &read_fds); + FD_SET(vsp_.master_fd_raw_, &read_fds); FD_SET(socketClient_.sock_fd, &read_fds); if (select(max_fd, &read_fds, NULL, NULL, NULL) < 0 && errno != EINTR) { - std::cerr << "Select error: " << strerror(errno) << std::endl; + Logger(Logger::Level::Error) << "Select error: " << strerror(errno) << std::endl; break; } - if (FD_ISSET(vsp_.master_fd_.native_handle(), &read_fds)) { + if (FD_ISSET(vsp_.master_fd_raw_, &read_fds)) { ssize_t bytes_read = vsp_.async_read(buffer, sizeof(buffer) - 1); if (bytes_read > 0) { buffer[bytes_read] = '\0'; - std::cout << "From PTY: " << buffer; + Logger(Logger::Level::Info) << "From PTY: " << buffer; socketClient_.sendToServer(buffer, bytes_read); } else if (bytes_read == 0) { - std::cout << "PTY closed." << std::endl; + Logger(Logger::Level::Info) << "PTY closed." << std::endl; break; } else { - std::cerr << "Error reading from PTY: " << strerror(errno) << std::endl; + Logger(Logger::Level::Error) << "Error reading from PTY: " << strerror(errno) << std::endl; } } @@ -50,13 +47,13 @@ void SerialClient::run() { ssize_t bytes_read = socketClient_.receiveFromServer(buffer, sizeof(buffer) - 1); if (bytes_read > 0) { buffer[bytes_read] = '\0'; - std::cout << "From Server: " << buffer; + Logger(Logger::Level::Info) << "From Server: " << buffer; vsp_.async_write(buffer, bytes_read); } else if (bytes_read == 0) { - std::cout << "Server closed connection." << std::endl; + Logger(Logger::Level::Info) << "Server closed connection." << std::endl; break; } else { - std::cerr << "Error reading from socket: " << strerror(errno) << std::endl; + Logger(Logger::Level::Error) << "Error reading from socket: " << strerror(errno) << std::endl; } } } diff --git a/src/SerialPort.cpp b/src/SerialPort.cpp new file mode 100644 index 0000000..b52f4e4 --- /dev/null +++ b/src/SerialPort.cpp @@ -0,0 +1,97 @@ +#include "SerialPort.h" +#include +#include +#include +#include +#include + +SerialPort::SerialPort(const std::string& device, int baud_rate) { + Logger(Logger::Level::Info) << "SerialPort init start - device " << device << " - baudRate - " << baud_rate << std::endl; + serial_fd = open(device.c_str(), O_RDWR | O_NOCTTY); + if (serial_fd < 0) { + Logger(Logger::Level::Error) << "Error opening serial port: " << strerror(errno) << std::endl; + throw std::runtime_error("Error opening serial port"); + } + + configurePort(baud_rate); + Logger(Logger::Level::Info) << "SerialPort init finish" << std::endl; +} + +SerialPort::~SerialPort() { + close(serial_fd); +} + + +void SerialPort::configurePort(int baud_rate) { + struct termios tty; + memset(&tty, 0, sizeof tty); + if (tcgetattr(serial_fd, &tty) != 0) { + Logger(Logger::Level::Error) << "tcgetattr failed: " << strerror(errno) << std::endl; + throw std::runtime_error("tcgetattr failed"); + } + + cfsetospeed(&tty, B115200); + cfsetispeed(&tty, B115200); + + tty.c_cflag |= (CLOCAL | CREAD); // Ignore modem controls, enable reading + tty.c_cflag &= ~CSIZE; // Clear the mask + tty.c_cflag |= CS8; // 8 data bits + tty.c_cflag &= ~PARENB; // No parity bit + tty.c_cflag &= ~CSTOPB; // 1 stop bit + tty.c_cflag &= ~CRTSCTS; // No hardware flow control + + tty.c_iflag &= ~(IXON | IXOFF | IXANY); // Turn off software flow control + tty.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); // Disable canonical mode, echo, and signal chars + tty.c_oflag &= ~OPOST; // Prevent special interpretation of output bytes (raw output) + + // Set read conditions: minimal character and timing + tty.c_cc[VMIN] = 0; + tty.c_cc[VTIME] = 5; // 0.5 seconds read timeout + + if (tcsetattr(serial_fd, TCSANOW, &tty) != 0) { + Logger(Logger::Level::Error) << "tcsetattr failed: " << strerror(errno) << std::endl; + throw std::runtime_error("tcsetattr failed"); + } +} + +void SerialPort::writeData(const std::string& data) { + Logger(Logger::Level::Info) << "SerialPort write data - " << data; + write(serial_fd, data.c_str(), data.size()); +} + + +void SerialPort::readLoop() { + Logger(Logger::Level::Info) << "SerialPort start reading loop"; + while (keep_reading) { + char buf[1024]; + int n = read(serial_fd, buf, sizeof(buf) - 1); + if (n > 0) { + buf[n] = '\0'; // Ensure null-termination + Logger(Logger::Level::Info) << "SerialPort read - " << buf; + std::lock_guard lock(mtx); + read_buffer.push(std::string(buf)); + cv.notify_one(); + } + } +} + +void SerialPort::startReading() { + keep_reading = true; + read_thread = std::thread(&SerialPort::readLoop, this); +} + +void SerialPort::stopReading() { + keep_reading = false; + if (read_thread.joinable()) + read_thread.join(); +} + +std::string SerialPort::readData() { + Logger(Logger::Level::Info) << "SerialPort read data"; + std::unique_lock lock(mtx); + cv.wait(lock, [this]{ return !read_buffer.empty(); }); + std::string data = read_buffer.front(); + read_buffer.pop(); + Logger(Logger::Level::Info) << "SerialPort read data - got data - " << data; + return data; +} diff --git a/src/SerialServer.cpp b/src/SerialServer.cpp index 952b46a..cee3644 100644 --- a/src/SerialServer.cpp +++ b/src/SerialServer.cpp @@ -1,72 +1,22 @@ -#include "SerialServer.h" -#include - -SerialServer::SerialServer(boost::asio::io_service& io_service, const std::string& device, unsigned int baud_rate) - : io_service_(io_service), - acceptor_(io_service, boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), 3333)), - socket_(io_service), - serial_port_(io_service) { - serial_port_.open(device, boost::asio::serial_port_base::baud_rate(baud_rate)); - } - -void SerialServer::run() { - BOOST_LOG_TRIVIAL(info) << "SerialServer::run"; - start_accept(); - BOOST_LOG_TRIVIAL(info) << "ioservice::run"; - io_service_.run(); -} - -void SerialServer::start_accept() { - acceptor_.async_accept(socket_, [this](boost::system::error_code ec) { - if (!ec) { - handle_session(); - } - start_accept(); - }); -} - -void SerialServer::handle_session() { - BOOST_LOG_TRIVIAL(info) << "SerialServer::handle_session"; +#include +#include +#include +#include - async_read_socket(); - async_read_serial(); -} +#include "SerialServer.h" -void SerialServer::async_read_socket() { - BOOST_LOG_TRIVIAL(info) << "SerialServer::async_read_socket"; +SerialServer::SerialServer(const std::string& device, unsigned int baud_rate, unsigned int port) + : serial_port_(device, baud_rate), server_(port, serial_port_) {} - socket_.async_read_some(boost::asio::buffer(buffer_), [this](boost::system::error_code ec, std::size_t length) { - if (!ec) { - serial_port_.async_write(boost::asio::buffer(buffer_, length), [this](boost::system::error_code ec, std::size_t) { - if (!ec) { - async_read_socket(); - } else { - BOOST_LOG_TRIVIAL(error) << "Error writing to client: " << ec.message(); - } - }); - } else { - BOOST_LOG_TRIVIAL(error) << "Read error on socket: " << ec.message(); - socket_.close(); - } - }); +SerialServer::~SerialServer() { + server_.stop(); } -void SerialServer::async_read_serial() { - // WIP: this works fine - // BOOST_LOG_TRIVIAL(info) << "SerialServer::async_read_serial"; - - serial_port_.async_read_some(boost::asio::buffer(buffer_), [this](boost::system::error_code ec, std::size_t length) { - if (!ec) { - boost::asio::async_write(socket_, boost::asio::buffer(buffer_, length), [this](boost::system::error_code ec, std::size_t) { - if (!ec) { - async_read_serial(); - } else { - BOOST_LOG_TRIVIAL(error) << "Error sending to socket: " << ec.message(); - } - }); - } else { - BOOST_LOG_TRIVIAL(error) << "Read error on serial port: " << ec.message(); - socket_.close(); - } - }); +void SerialServer::run() { + Logger(Logger::Level::Info) << "SerialServer::run"; + try { + server_.run(); + } catch (const std::exception& e) { + std::cerr << "Error: " << e.what() << std::endl; + } } diff --git a/src/TCPServer.cpp b/src/TCPServer.cpp new file mode 100644 index 0000000..7d0ac5e --- /dev/null +++ b/src/TCPServer.cpp @@ -0,0 +1,86 @@ +#include "TCPServer.h" +#include +#include +#include + +TcpServer::TcpServer(int port, SerialPort& serial) : port_(port), serial_(serial), is_running_(false) { + Logger(Logger::Level::Info) << "TCPServer init start"; + if ((server_fd_ = socket(AF_INET, SOCK_STREAM, 0)) == 0) { + throw std::runtime_error("Socket creation failed"); + } + + int opt = 1; + if (setsockopt(server_fd_, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt))) { + throw std::runtime_error("Setsockopt failed"); + } + + address_.sin_family = AF_INET; + address_.sin_addr.s_addr = INADDR_ANY; + address_.sin_port = htons(port); + + if (bind(server_fd_, (struct sockaddr*)&address_, sizeof(address_)) < 0) { + throw std::runtime_error("Bind failed"); + } + + if (listen(server_fd_, 3) < 0) { + throw std::runtime_error("Listen failed"); + } + Logger(Logger::Level::Info) << "TCPServer init finish"; +} + +TcpServer::~TcpServer() { + if (is_running_) { + stop(); + } +} + +void TcpServer::run() { + is_running_ = true; + Logger(Logger::Level::Info) << "TCPServer start listening"; + while (is_running_) { + struct sockaddr_in clientAddr; + socklen_t clientLen = sizeof(clientAddr); + int clientSocket = accept(server_fd_, (struct sockaddr *)&clientAddr, &clientLen); + if (clientSocket < 0) { + continue; + } + + std::thread clientThread(&TcpServer::handleClient, this, clientSocket); + clientThread.detach(); // Detach the thread to handle multiple clients + } +} + +void TcpServer::stop() { + close(server_fd_); + is_running_ = false; +} + +void TcpServer::handleClient(int client_socket) { + char buffer[1024]; + Logger(Logger::Level::Info) << "HandleClient start - read thread from serial"; + serial_.startReading(); + std::thread readThread([&]() { + while (true) { + std::string response = serial_.readData(); // This will block until data is available + if (!response.empty()) { + Logger(Logger::Level::Info) << "HandleClient start - write to client " << response.c_str(); + write(client_socket, response.c_str(), response.size()); + } + } + }); + + Logger(Logger::Level::Info) << "HandleClient start - read data from client"; + while (true) { + memset(buffer, 0, sizeof(buffer)); + ssize_t bytesReceived = read(client_socket, buffer, sizeof(buffer)); + if (bytesReceived <= 0) { + break; + } + Logger(Logger::Level::Info) << "HandleClient start - write to serial " << buffer; + serial_.writeData(std::string(buffer, bytesReceived)); + } + + serial_.stopReading(); + readThread.join(); + close(client_socket); +} diff --git a/src/VirtualSerialPort.cpp b/src/VirtualSerialPort.cpp index 01d3cd5..2410a8c 100644 --- a/src/VirtualSerialPort.cpp +++ b/src/VirtualSerialPort.cpp @@ -1,58 +1,53 @@ #include "VirtualSerialPort.h" -VirtualSerialPort::VirtualSerialPort(boost::asio::io_context& io_context, const std::string& device) - : master_fd_(io_context), slave_fd_(io_context), device_name_("/dev/" + device) { +VirtualSerialPort::VirtualSerialPort(const std::string& device) + : device_name_("/dev/" + device) { std::lock_guard lock(mutex_); - int master_fd, slave_fd; char* slave_name; - master_fd = posix_openpt(O_RDWR | O_NOCTTY); - if (master_fd == -1) { - BOOST_LOG_TRIVIAL(error) << "Failed to open PTY master: " << strerror(errno); + master_fd_raw_ = posix_openpt(O_RDWR | O_NOCTTY); + if (master_fd_raw_ == -1) { + Logger(Logger::Level::Error) << "Failed to open PTY master: " << strerror(errno); throw std::runtime_error("Failed to open PTY master"); } - BOOST_LOG_TRIVIAL(info) << "PTY master opened successfully"; + Logger(Logger::Level::Info) << "PTY master opened successfully"; - if (grantpt(master_fd) == -1 || unlockpt(master_fd) == -1 || (slave_name = ptsname(master_fd)) == nullptr) { - BOOST_LOG_TRIVIAL(error) << "Failed to grant or unlock PTY: " << strerror(errno); + if (grantpt(master_fd_raw_) == -1 || unlockpt(master_fd_raw_) == -1 || (slave_name = ptsname(master_fd_raw_)) == nullptr) { + Logger(Logger::Level::Error) << "Failed to grant or unlock PTY: " << strerror(errno); throw std::runtime_error("Failed to grant or unlock PTY"); } - BOOST_LOG_TRIVIAL(info) << "PTY grant and unlock successful"; - BOOST_LOG_TRIVIAL(info) << "Slave PTY name: " << slave_name << std::endl; + Logger(Logger::Level::Info) << "PTY grant and unlock successful"; + Logger(Logger::Level::Info) << "Slave PTY name: " << slave_name << std::endl; // Attempt to create a symbolic link from slave_name to "/dev/ttyUSB0" if (symlink(slave_name, device_name_.c_str()) == -1) { - BOOST_LOG_TRIVIAL(error) << "Failed to create symlink for PTY slave: " << strerror(errno); + Logger(Logger::Level::Error) << "Failed to create symlink for PTY slave: " << strerror(errno); throw std::runtime_error("Failed to create symlink for PTY slave"); } - BOOST_LOG_TRIVIAL(info) << "Symlink for PTY slave created successfully"; + Logger(Logger::Level::Info) << "Symlink for PTY slave created successfully"; // Open the slave pseudoterminal - slave_fd = open(slave_name, O_RDWR); - if (slave_fd == -1) { - BOOST_LOG_TRIVIAL(error) << "Failed to create symlink for PTY slave: " << strerror(errno); + slave_fd_raw_ = open(slave_name, O_RDWR); + if (slave_fd_raw_ == -1) { + Logger(Logger::Level::Error) << "Failed to create symlink for PTY slave: " << strerror(errno); throw std::runtime_error("Failed to open the slave pseudoterminal"); } - - chmod(device_name_.c_str(), 0660); struct group* tty_grp = getgrnam("tty"); if (tty_grp && chown(device_name_.c_str(), -1, tty_grp->gr_gid) == -1) { - BOOST_LOG_TRIVIAL(error) << "Failed to change group of device: " << strerror(errno); + Logger(Logger::Level::Error) << "Failed to change group of device: " << strerror(errno); throw std::runtime_error("Failed to change group of device"); } - BOOST_LOG_TRIVIAL(info) << "Group changed successfully for the device"; + Logger(Logger::Level::Info) << "Group changed successfully for the device"; - master_fd_.assign(master_fd); - slave_fd_.assign(slave_fd); - setup_pty(master_fd); - setup_pty(slave_fd); + setup_pty(master_fd_raw_); + setup_pty(slave_fd_raw_); } void VirtualSerialPort::setup_pty(int fd) { struct termios tty; if (tcgetattr(fd, &tty) != 0) { - BOOST_LOG_TRIVIAL(error) << "Error from tcgetattr: " << strerror(errno); + Logger(Logger::Level::Error) << "Error from tcgetattr: " << strerror(errno); return; } @@ -70,31 +65,27 @@ void VirtualSerialPort::setup_pty(int fd) { tty.c_oflag &= ~OPOST; if (tcsetattr(fd, TCSANOW, &tty) != 0) { - BOOST_LOG_TRIVIAL(error) << "Error from tcsetattr: " << strerror(errno); + Logger(Logger::Level::Error) << "Error from tcsetattr: " << strerror(errno); } else { - BOOST_LOG_TRIVIAL(info) << "PTY attributes set successfully"; + Logger(Logger::Level::Info) << "PTY attributes set successfully"; } } -void VirtualSerialPort::close() { - master_fd_.close(); - slave_fd_.close(); +VirtualSerialPort::~VirtualSerialPort() { + close(master_fd_raw_); + close(slave_fd_raw_); unlink(device_name_.c_str()); -} -VirtualSerialPort::~VirtualSerialPort() { - close(); } ssize_t VirtualSerialPort::async_read(char* buffer, unsigned int length) { - BOOST_LOG_TRIVIAL(info) << "VSP::async_read"; - ssize_t bytes_read = read(master_fd_.native_handle(), buffer, length); - BOOST_LOG_TRIVIAL(info) << "READ FROM SERIAL!!!! -> "; - return bytes_read; + // Logger(Logger::Level::Info) << "VSP::async_read"; + return read(master_fd_raw_, buffer, length); } ssize_t VirtualSerialPort::async_write(const char* buffer, unsigned int length) { - return write(master_fd_.native_handle(), buffer, length); + // Logger(Logger::Level::Info) << "VSP::async_write"; + return write(master_fd_raw_, buffer, length); } diff --git a/src/alternative_server_main.cpp b/src/alternative_server_main.cpp new file mode 100644 index 0000000..09f3666 --- /dev/null +++ b/src/alternative_server_main.cpp @@ -0,0 +1,67 @@ +#include +#include +#include +#include +#include +#include +#include + +void handleClient(int clientSocket) { + char buffer[1024]; + while (true) { + memset(buffer, 0, sizeof(buffer)); + ssize_t bytesReceived = read(clientSocket, buffer, sizeof(buffer)); + if (bytesReceived <= 0) { + std::cerr << "Read error or connection closed by client" << std::endl; + break; + } + + std::cout << "Received: " << buffer << std::endl; + write(clientSocket, buffer, bytesReceived); + } + + close(clientSocket); +} + +int main() { + const int port = 3333; + int serverFd = socket(AF_INET, SOCK_STREAM, 0); + if (serverFd < 0) { + std::cerr << "Cannot open socket" << std::endl; + return 1; + } + + struct sockaddr_in serverAddr; + memset(&serverAddr, 0, sizeof(serverAddr)); + serverAddr.sin_family = AF_INET; + serverAddr.sin_addr.s_addr = INADDR_ANY; // Listen on all network interfaces + serverAddr.sin_port = htons(port); + + if (bind(serverFd, (struct sockaddr *) &serverAddr, sizeof(serverAddr)) < 0) { + std::cerr << "Cannot bind to port " << port << std::endl; + return 1; + } + + if (listen(serverFd, 10) < 0) { // Listen for up to 10 connections + std::cerr << "Listen failed" << std::endl; + return 1; + } + + std::cout << "Server is listening on port " << port << std::endl; + + while (true) { + struct sockaddr_in clientAddr; + socklen_t clientLen = sizeof(clientAddr); + int clientSocket = accept(serverFd, (struct sockaddr *) &clientAddr, &clientLen); + if (clientSocket < 0) { + std::cerr << "Cannot accept connection" << std::endl; + continue; + } + + std::thread clientThread(handleClient, clientSocket); + clientThread.detach(); // Detach the thread to handle multiple clients + } + + close(serverFd); + return 0; +} diff --git a/src/another-client.cpp b/src/another-client.cpp deleted file mode 100644 index ded4a54..0000000 --- a/src/another-client.cpp +++ /dev/null @@ -1,129 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include - -int main() { - int master_fd, slave_fd; - int sock_fd; - struct sockaddr_in server_addr; - char *slave_name; - char server_ip[] = "192.168.1.29"; // Example IP address - int server_port = 3333; // Example port - - // Create a pseudoterminal - master_fd = posix_openpt(O_RDWR | O_NOCTTY); - if (master_fd == -1) { - std::cerr << "Error opening PTY: " << strerror(errno) << std::endl; - return 1; - } - - if (grantpt(master_fd) == -1 || unlockpt(master_fd) == -1 || (slave_name = ptsname(master_fd)) == nullptr) { - std::cerr << "Failed to initialize PTY: " << strerror(errno) << std::endl; - close(master_fd); - return 1; - } - - std::cout << "Slave PTY name: " << slave_name << std::endl; - - // Attempt to create a symbolic link from slave_name to "/dev/ttyUSB0" - if (symlink(slave_name, "/dev/ttyUSB0") == -1) { - std::cerr << "Failed to create symlink for PTY slave: " << strerror(errno) << std::endl; - close(master_fd); - return 1; - } - std::cout << "Symlink for PTY slave created successfully" << std::endl; - - // Open the slave pseudoterminal - slave_fd = open(slave_name, O_RDWR); - if (slave_fd == -1) { - std::cerr << "Error opening slave PTY: " << strerror(errno) << std::endl; - close(master_fd); - return 1; - } - - // Set up the socket - sock_fd = socket(AF_INET, SOCK_STREAM, 0); - if (sock_fd == -1) { - std::cerr << "Error creating socket: " << strerror(errno) << std::endl; - close(slave_fd); - close(master_fd); - return 1; - } - - memset(&server_addr, 0, sizeof(server_addr)); - server_addr.sin_family = AF_INET; - server_addr.sin_port = htons(server_port); - if (inet_pton(AF_INET, server_ip, &server_addr.sin_addr) <= 0) { - std::cerr << "Invalid address/ Address not supported" << std::endl; - close(sock_fd); - close(slave_fd); - close(master_fd); - return 1; - } - - if (connect(sock_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) { - std::cerr << "Connection Failed: " << strerror(errno) << std::endl; - close(sock_fd); - close(slave_fd); - close(master_fd); - return 1; - } - - std::cout << "Connected to server at " << server_ip << ":" << server_port << std::endl; - - // Communication loop - fd_set read_fds; - char buffer[256]; - int max_fd = std::max(master_fd, sock_fd) + 1; - while (true) { - FD_ZERO(&read_fds); - FD_SET(master_fd, &read_fds); - FD_SET(sock_fd, &read_fds); - - int activity = select(max_fd, &read_fds, NULL, NULL, NULL); - - if ((activity < 0) && (errno != EINTR)) { - std::cerr << "Select error: " << strerror(errno) << std::endl; - break; - } - - if (FD_ISSET(master_fd, &read_fds)) { - ssize_t bytes_read = read(master_fd, buffer, sizeof(buffer) - 1); - if (bytes_read > 0) { - buffer[bytes_read] = '\0'; - std::cout << "From PTY: " << buffer; - send(sock_fd, buffer, bytes_read, 0); // Send to network - } else if (bytes_read == 0) { - std::cout << "PTY closed." << std::endl; - break; - } else { - std::cerr << "Error reading from PTY: " << strerror(errno) << std::endl; - } - } - - if (FD_ISSET(sock_fd, &read_fds)) { - ssize_t bytes_read = recv(sock_fd, buffer, sizeof(buffer) - 1, 0); - if (bytes_read > 0) { - buffer[bytes_read] = '\0'; - std::cout << "From Server: " << buffer; - write(master_fd, buffer, bytes_read); // Send back to PTY - } else if (bytes_read == 0) { - std::cout << "Server closed connection." << std::endl; - break; - } else { - std::cerr << "Error reading from socket: " << strerror(errno) << std::endl; - } - } - } - - close(sock_fd); - close(slave_fd); - close(master_fd); - return 0; -} \ No newline at end of file diff --git a/src/logging.cpp b/src/logging.cpp deleted file mode 100644 index bffd618..0000000 --- a/src/logging.cpp +++ /dev/null @@ -1,34 +0,0 @@ -#include "logging.h" - -// Define the global logger -src::severity_logger lg; - -void init_logging() { - logging::add_common_attributes(); - - logging::add_console_log( - std::cout, - keywords::format = ( - expr::stream - << expr::format_date_time("TimeStamp", "%Y-%m-%d %H:%M:%S") - << " [" << logging::trivial::severity << "] " - << expr::smessage - ) - ); - - logging::add_file_log( - keywords::file_name = "serial_application_%N.log", - keywords::rotation_size = 10 * 1024 * 1024, // Rotate files every 10 MiB - keywords::time_based_rotation = logging::sinks::file::rotation_at_time_point(0, 0, 0), - keywords::format = ( - expr::stream - << expr::format_date_time("TimeStamp", "%Y-%m-%d %H:%M:%S") - << " [" << logging::trivial::severity << "] " - << expr::smessage - ) - ); - - logging::core::get()->set_filter( - logging::trivial::severity >= logging::trivial::info - ); -} diff --git a/src/main.cpp b/src/main.cpp index 89732e2..299a9f9 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,76 +1,79 @@ #include -#include -#include -#include "logging.h" +#include +#include +#include "Logger.h" #include "SerialClient.h" #include "SerialServer.h" -#include "RealSerialPort.h" -using namespace boost::asio; -using namespace boost::program_options; using std::string; using std::cout; using std::cerr; using std::endl; -void setup_and_run_server(io_service& io, const variables_map& vm) { - unsigned int baud_rate = vm["baud"].as(); // Get baud rate as unsigned int - SerialServer server(io, vm["device"].as(), baud_rate); +void setup_and_run_server(const string& device, unsigned int baud_rate, unsigned int port) { + SerialServer server(device, baud_rate, port); server.run(); } -void setup_and_run_client(io_service& io, const variables_map& vm) { - SerialClient client(io, vm["server"].as(), vm["port"].as(), vm["vsp"].as()); +void setup_and_run_client(const string& server_ip, unsigned short port, const string& vsp) { + SerialClient client(server_ip, port, vsp); client.run(); } int main(int argc, char* argv[]) { - init_logging(); - if (argc < 2) { cerr << "Usage: ser2net2ser [options]\n"; return 1; } string command = argv[1]; - io_service io; try { if (command == "serve") { - options_description serve_desc("Server options"); - serve_desc.add_options() - ("help,h", "produce help message") - ("device,d", value()->default_value("/dev/ttyUSB0"), "Device name") - ("baud,b", value()->default_value(9600), "Baud rate") - ("port,p", value()->default_value(12345), "Port number"); - variables_map vm; - store(parse_command_line(argc, argv, serve_desc), vm); + string device = "/dev/ttyUSB0"; + unsigned int baud = 9600; + unsigned int port = 12345; - if (vm.count("help")) { - cout << serve_desc << endl; - return 0; + for (int i = 2; i < argc; i += 2) { + string arg = argv[i]; + if (arg == "--device" || arg == "-d") { + if (i + 1 < argc) device = argv[i + 1]; + } else if (arg == "--baud" || arg == "-b") { + if (i + 1 < argc) baud = std::stoi(argv[i + 1]); + } else if (arg == "--port" || arg == "-p") { + if (i + 1 < argc) port = std::stoi(argv[i + 1]); + } else if (arg == "--help" || arg == "-h") { + cout << "Usage: ser2net2ser serve [--device ] [--baud ] [--port ]\n"; + return 0; + } } - notify(vm); - setup_and_run_server(io, vm); + setup_and_run_server(device, baud, port); } else if (command == "connect") { - options_description connect_desc("Client options"); - connect_desc.add_options() - ("help,h", "produce help message") - ("server,s", value()->default_value("127.0.0.1"), "Server IP address") - ("port,p", value()->default_value(12345), "Server port") - ("vsp,v", value()->required(), "Virtual serial port name"); - variables_map vm; - store(parse_command_line(argc, argv, connect_desc), vm); + string server_ip = "127.0.0.1"; + unsigned short port = 12345; + string vsp; - if (vm.count("help")) { - cout << connect_desc << endl; - return 0; + for (int i = 2; i < argc; i += 2) { + string arg = argv[i]; + if (arg == "--server" || arg == "-s") { + if (i + 1 < argc) server_ip = argv[i + 1]; + } else if (arg == "--port" || arg == "-p") { + if (i + 1 < argc) port = static_cast(std::stoi(argv[i + 1])); + } else if (arg == "--vsp" || arg == "-v") { + if (i + 1 < argc) vsp = argv[i + 1]; + } else if (arg == "--help" || arg == "-h") { + cout << "Usage: ser2net2ser connect [--server ] [--port ] --vsp \n"; + return 0; + } } + if (vsp.empty()) { + cerr << "Virtual serial port name must be specified with --vsp.\n"; + return 1; + } - notify(vm); - setup_and_run_client(io, vm); + setup_and_run_client(server_ip, port, vsp); } else { throw std::invalid_argument("Invalid command provided. Use 'serve' or 'connect'."); } diff --git a/src/separate_main.cpp b/src/separate_main.cpp deleted file mode 100644 index ab28c60..0000000 --- a/src/separate_main.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include -#include "VirtualSerialPort.h" -#include - -int main() { - try { - boost::asio::io_context io_context; - std::string device = "ttyUSB9"; // Adjust the device name as needed - - // Instantiate your VirtualSerialPort - VirtualSerialPort vsp(io_context, device); - - // Prepare a message to send - std::string test_message = "Hello, Virtual Serial Port!"; - boost::asio::const_buffer write_buffer = boost::asio::buffer(test_message); - - // Function to handle write completion - auto write_handler = [](const boost::system::error_code& ec, std::size_t bytes_transferred) { - std::cout << "Write completed. Bytes transferred: " << bytes_transferred << std::endl; - if (ec) { - std::cerr << "Error on write: " << ec.message() << std::endl; - } - }; - - // Write to the virtual serial port - vsp.async_write(write_buffer, write_handler); - - // Prepare a buffer for reading - std::array read_buffer; - boost::asio::mutable_buffer mutable_buffer = boost::asio::buffer(read_buffer); - - // Function to handle read completion - auto read_handler = [&read_buffer](const boost::system::error_code& ec, std::size_t bytes_transferred) { - std::cout << "Read completed. Bytes transferred: " << bytes_transferred << std::endl; - if (!ec) { - std::cout << "Received: " << std::string(read_buffer.begin(), read_buffer.begin() + bytes_transferred) << std::endl; - } else { - std::cerr << "Error on read: " << ec.message() << std::endl; - } - }; - - // Read from the virtual serial port - vsp.async_read(mutable_buffer, read_handler); - - // Run the io_context to perform asynchronous operations - io_context.run(); - - } catch (const std::exception& e) { - std::cerr << "Exception: " << e.what() << std::endl; - return 1; - } - - return 0; -} diff --git a/src/yet-another-client.cpp b/src/yet-another-client.cpp deleted file mode 100644 index 114da9b..0000000 --- a/src/yet-another-client.cpp +++ /dev/null @@ -1,155 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include - -class PTYSimulator { -public: - int master_fd, slave_fd; - char* slave_name; - - PTYSimulator() : master_fd(-1), slave_fd(-1), slave_name(nullptr) {} - - ~PTYSimulator() { - close(slave_fd); - close(master_fd); - } - - bool setup() { - master_fd = posix_openpt(O_RDWR | O_NOCTTY); - if (master_fd == -1 || grantpt(master_fd) == -1 || unlockpt(master_fd) == -1 || (slave_name = ptsname(master_fd)) == nullptr) { - std::cerr << "Failed to initialize PTY: " << strerror(errno) << std::endl; - return false; - } - - std::cout << "Slave PTY name: " << slave_name << std::endl; - - // Attempt to create a symbolic link from slave_name to "/dev/ttyUSB0" - if (symlink(slave_name, "/dev/ttyUSB0") == -1) { - std::cerr << "Failed to create symlink for PTY slave: " << strerror(errno) << std::endl; - return false; - } - - std::cout << "Symlink for PTY slave created successfully" << std::endl; - - slave_fd = open(slave_name, O_RDWR); - if (slave_fd == -1) { - std::cerr << "Error opening slave PTY: " << strerror(errno) << std::endl; - return false; - } - - return true; - } - - ssize_t readFromPTY(char* buffer, size_t bufferSize) { - return read(master_fd, buffer, bufferSize); - } - - void writeToPTY(const char* buffer, size_t bufferSize) { - write(master_fd, buffer, bufferSize); - } -}; - -class SocketClient { - struct sockaddr_in server_addr; - -public: - int sock_fd; - SocketClient() : sock_fd(-1) { - memset(&server_addr, 0, sizeof(server_addr)); - } - - ~SocketClient() { - close(sock_fd); - } - - bool connectToServer(const char* server_ip, int server_port) { - sock_fd = socket(AF_INET, SOCK_STREAM, 0); - if (sock_fd == -1) { - std::cerr << "Error creating socket: " << strerror(errno) << std::endl; - return false; - } - - server_addr.sin_family = AF_INET; - server_addr.sin_port = htons(server_port); - if (inet_pton(AF_INET, server_ip, &server_addr.sin_addr) <= 0) { - std::cerr << "Invalid address/ Address not supported" << std::endl; - return false; - } - - if (::connect(sock_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) { - std::cerr << "Connection Failed: " << strerror(errno) << std::endl; - return false; - } - - std::cout << "Connected to server at " << server_ip << ":" << server_port << std::endl; - return true; - } - - ssize_t sendToServer(const char* buffer, size_t bufferSize) { - return send(sock_fd, buffer, bufferSize, 0); - } - - ssize_t receiveFromServer(char* buffer, size_t bufferSize) { - return recv(sock_fd, buffer, bufferSize, 0); - } -}; - -int main() { - PTYSimulator pty; - SocketClient socketClient; - - if (!pty.setup() || !socketClient.connectToServer("192.168.1.29", 3333)) { - return 1; - } - - fd_set read_fds; - char buffer[256]; - int max_fd = std::max(pty.master_fd, socketClient.sock_fd) + 1; - - while (true) { - FD_ZERO(&read_fds); - FD_SET(pty.master_fd, &read_fds); - FD_SET(socketClient.sock_fd, &read_fds); - - if (select(max_fd, &read_fds, NULL, NULL, NULL) < 0 && errno != EINTR) { - std::cerr << "Select error: " << strerror(errno) << std::endl; - break; - } - - if (FD_ISSET(pty.master_fd, &read_fds)) { - ssize_t bytes_read = pty.readFromPTY(buffer, sizeof(buffer) - 1); - if (bytes_read > 0) { - buffer[bytes_read] = '\0'; - std::cout << "From PTY: " << buffer; - socketClient.sendToServer(buffer, bytes_read); - } else if (bytes_read == 0) { - std::cout << "PTY closed." << std::endl; - break; - } else { - std::cerr << "Error reading from PTY: " << strerror(errno) << std::endl; - } - } - - if (FD_ISSET(socketClient.sock_fd, &read_fds)) { - ssize_t bytes_read = socketClient.receiveFromServer(buffer, sizeof(buffer) - 1); - if (bytes_read > 0) { - buffer[bytes_read] = '\0'; - std::cout << "From Server: " << buffer; - pty.writeToPTY(buffer, bytes_read); - } else if (bytes_read == 0) { - std::cout << "Server closed connection." << std::endl; - break; - } else { - std::cerr << "Error reading from socket: " << strerror(errno) << std::endl; - } - } - } - - return 0; -}