diff --git a/projects/.vscode/settings.json b/projects/.vscode/settings.json index 33c2443..7b1a728 100644 --- a/projects/.vscode/settings.json +++ b/projects/.vscode/settings.json @@ -4,6 +4,7 @@ "editor.formatOnSave": true, "cmake.installPrefix": "${workspaceFolder}", "cmake.environment": { - "PATH": "${env:PATH};C:/mingw64/fltk-1.3.8-cmake/bin" + "PATH": "${env:PATH};C:/mingw64/fltk-1.3.8-cmake/bin", + "BOOST_ROOT": "C:/mingw64/boost-1.83.0" } } \ No newline at end of file diff --git a/projects/CMakeLists.txt b/projects/CMakeLists.txt index 9dc5c6c..9078839 100644 --- a/projects/CMakeLists.txt +++ b/projects/CMakeLists.txt @@ -23,3 +23,5 @@ add_subdirectory(ch13/fibonacci_spiral) add_subdirectory(ch13/hexagon_tile) add_subdirectory(ch14/logic_shapes) + +add_subdirectory(messenger) \ No newline at end of file diff --git a/projects/messenger/CMakeLists.txt b/projects/messenger/CMakeLists.txt new file mode 100644 index 0000000..5eb450c --- /dev/null +++ b/projects/messenger/CMakeLists.txt @@ -0,0 +1,25 @@ +cmake_minimum_required(VERSION 3.20) + +set(TARGET "messenger") + +set(SOURCES + main.cpp +) + +project(${TARGET} CXX) + +find_package( + Boost 1.83.0 EXACT REQUIRED + COMPONENTS headers +) + +include_directories(SYSTEM ${Boost_INCLUDE_DIRS}) + +add_executable(${TARGET} ${SOURCES}) + +target_link_libraries(${TARGET} ${Boost_LIBRARIES}) +if(CMAKE_SYSTEM_NAME STREQUAL "Windows") + target_link_libraries(${TARGET} ws2_32) +endif() + +install(TARGETS ${TARGET}) diff --git a/projects/messenger/ReadMe.md b/projects/messenger/ReadMe.md new file mode 100644 index 0000000..abed7d0 --- /dev/null +++ b/projects/messenger/ReadMe.md @@ -0,0 +1,29 @@ +## Notes for Windows user + +### Download Boost libraries + +- use [direct link](https://boostorg.jfrog.io/artifactory/main/release/1.83.0/source/boost_1_83_0.7z) +- or get latest version from the official [web-site](https://www.boost.org) + + +### Unpack and install + +`Boost.Asio` is a header-only library. So, just unpack archive and install headers without building anything else. +```sh +$ cd /path/to/boost_1_83_0 +$ ./bootstrap.sh +$ ./b2 --prefix=/absolute/path/to/boost-1.83.0 --with-headers variant=release -j4 install +``` + + +### Configure VS Code + +Add to your workspace `settings.json` a hint where `CMake` should find Boost libraries. +```json +{ + "cmake.environment": { + ... + "BOOST_ROOT": "/absolute/path/to/boost-1.83.0" + } +} +``` diff --git a/projects/messenger/main.cpp b/projects/messenger/main.cpp new file mode 100644 index 0000000..3de1639 --- /dev/null +++ b/projects/messenger/main.cpp @@ -0,0 +1,114 @@ +// Simple TCP server-client console messenger + +#include +#include +#include +#include +#include + +#include + +using boost::asio::ip::tcp; + +using namespace std::chrono_literals; + +void run_as_server (unsigned port) +{ + boost::asio::io_context context; + tcp::acceptor acceptor(context, tcp::endpoint(tcp::v4(), port)); + + tcp::endpoint endpoint = acceptor.local_endpoint(); + std::cout << "Server listening on " << endpoint.address().to_string() + << ":" << endpoint.port() << std::endl; + + while (acceptor.is_open() && std::cin) + { + tcp::iostream stream; + boost::system::error_code ec; + acceptor.accept(*stream.rdbuf(), ec); + if (ec) + { + std::cerr << ec << std::endl; + std::this_thread::sleep_for(500ms); + continue; + } + auto ip = stream.rdbuf()->remote_endpoint().address().to_string(); + std::cout << "Has connection from " << ip << "\n" + << "*** Chat starts ***" << std::endl; + + while (stream && std::cin) + { + // receive message from client + std::string msg; + if (!std::getline(stream, msg)) + break; + + std::cout << "client: " << msg << std::endl; + + // send responce to the client + std::cout << ">> " << std::flush; + if (!std::getline(std::cin, msg)) + break; + + stream << msg << std::endl; + } + std::cout << "*** Chat ends ***\n" + << "Waiting for a new connection..." << std::endl; + } + std::cout << "Bye, bye!" << std::endl; +} + +void run_as_client (const std::string& server, unsigned port) +{ + tcp::iostream stream; + stream.connect(server, std::to_string(port)); + + if (!stream) + { + std::cerr << "Failed to connect to the server" << std::endl; + return; + } + std::cout << "*** Has connection. Chat starts ***" << std::endl; + + while (stream && std::cin) + { + // send a message to the server + std::cout << ">> " << std::flush; + std::string msg; + if (!std::getline(std::cin, msg)) + break; + + stream << msg << std::endl; + + // receive the server's response + if (!std::getline(stream, msg)) + break; + + std::cout << "server: " << msg << std::endl; + } + std::cout << "*** Chat ends ***\n" + << "Bye, bye!" << std::endl; +} + +int main (int argc, char* argv[]) +try +{ + if (argc < 2 || 3 < argc) + { + std::cerr << "Usage: " << argv[0] + << " {\"server\"|\"localhost\"|ip} [port]" << std::endl; + return 2; + } + std::string host{argv[1]}; + int port = (argc == 3) ? std::stoi(argv[2]) : 443; + + if (host == "server") // keyword to start as server + run_as_server(port); + else + run_as_client(host, port); +} +catch (std::exception& e) +{ + std::cerr << e.what() << std::endl; + return 1; +}