From a1192a14c6a52e8ab77e1c84e38b65aa5e250bec Mon Sep 17 00:00:00 2001 From: crupest Date: Thu, 27 May 2021 15:13:46 +0800 Subject: import(life): Add computer network experiment. --- works/life/computer-network-experiment/server.cpp | 132 ++++++++++++++++++++++ 1 file changed, 132 insertions(+) create mode 100644 works/life/computer-network-experiment/server.cpp (limited to 'works/life/computer-network-experiment/server.cpp') diff --git a/works/life/computer-network-experiment/server.cpp b/works/life/computer-network-experiment/server.cpp new file mode 100644 index 0000000..1b196a9 --- /dev/null +++ b/works/life/computer-network-experiment/server.cpp @@ -0,0 +1,132 @@ +/** Created by crupest. + * This is the server program. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#pragma comment(lib, "Ws2_32.lib") + +const auto bind_address = "127.0.0.1"; // control bind address +const u_short port = 1234; // control bind port + +// As far as I know, cout is not thread safe. So we need a lock. But this might +// not be the best solution. We can use a queue instead to avoid block. +std::mutex cout_mutex; + +[[noreturn]] void +PrintErrorMessageAndExit(std::wstring_view message, + std::optional error_code = std::nullopt) { + std::wcerr << message << L'\n'; + + if (error_code) { + std::cerr << L"Error code is " << std::hex << *error_code << L'\n'; + wchar_t buffer[500]; + if (!FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_ARGUMENT_ARRAY | + FORMAT_MESSAGE_IGNORE_INSERTS, + nullptr, *error_code, 0, buffer, 500, nullptr)) { + std::wcerr << L"Failed to format error message.\n"; + } else { + std::wcerr << buffer << L'\n'; + } + } + + WSACleanup(); + + std::exit(1); +} + +void ResponseThreadProc(int socket, sockaddr_in address) { + auto address_string = inet_ntoa(address.sin_addr); + { + std::lock_guard guard(cout_mutex); + std::cout << "Connected to " << address_string << "!\n"; + } + + const std::string_view buffer = "Love you!!! By crupest!"; + + const int total_byte_count = buffer.size(); + int byte_count_sent = 0; + int retry_count = 0; + + while (true) { + // Now we have sent all data. + if (byte_count_sent == total_byte_count) + break; + + auto byte_actually_sent = send(socket, buffer.data() + byte_count_sent, + buffer.size() - byte_count_sent, 0); + + // send failed + if (byte_actually_sent == SOCKET_ERROR) { + std::lock_guard guard(cout_mutex); + std::cerr << "Failed to send!\n"; + closesocket(socket); + break; + } + + byte_count_sent += byte_actually_sent; + } + + { + std::lock_guard guard(cout_mutex); + std::cout << "Succeeded to send message to " << address_string << "!\n"; + } +} + +int main() { + WSADATA wsa_data; + + if (WSAStartup(MAKEWORD(2, 2), &wsa_data)) { // initialize wsa + PrintErrorMessageAndExit(L"Failed to initialize wsa.", WSAGetLastError()); + } + + int server_socket; + + if ((server_socket = socket(AF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) { + PrintErrorMessageAndExit(L"Failed to create socket.", WSAGetLastError()); + } + + sockaddr_in server_address; + + server_address.sin_family = AF_INET; + server_address.sin_port = htons(port); + server_address.sin_addr.s_addr = inet_addr(bind_address); + memset(&(server_address.sin_zero), 0, sizeof(server_address.sin_zero)); + + if (bind(server_socket, reinterpret_cast(&server_address), + sizeof(sockaddr_in)) == SOCKET_ERROR) { + PrintErrorMessageAndExit(L"Failed to bind.", WSAGetLastError()); + } + + if (listen(server_socket, SOMAXCONN) == SOCKET_ERROR) { + PrintErrorMessageAndExit(L"Failed to listen.", WSAGetLastError()); + } + + while (true) { + sockaddr_in client_address; + int client_socket; + int sin_size = sizeof(sockaddr_in); + client_socket = + accept(server_socket, reinterpret_cast(&client_address), + &sin_size); + + if (client_socket == INVALID_SOCKET) { + PrintErrorMessageAndExit(L"Failed to accecpt", WSAGetLastError()); + } + + std::thread response_thread(ResponseThreadProc, client_socket, + client_address); + response_thread.detach(); + } +} -- cgit v1.2.3 From baada7b0b03a0fd3bab6ccf996515c7dd7bd71af Mon Sep 17 00:00:00 2001 From: crupest Date: Fri, 28 May 2021 13:58:07 +0800 Subject: import(life): computer-network-experiment: Fix server not close socket. --- works/life/computer-network-experiment/server.cpp | 2 ++ 1 file changed, 2 insertions(+) (limited to 'works/life/computer-network-experiment/server.cpp') diff --git a/works/life/computer-network-experiment/server.cpp b/works/life/computer-network-experiment/server.cpp index 1b196a9..15470f4 100644 --- a/works/life/computer-network-experiment/server.cpp +++ b/works/life/computer-network-experiment/server.cpp @@ -82,6 +82,8 @@ void ResponseThreadProc(int socket, sockaddr_in address) { std::lock_guard guard(cout_mutex); std::cout << "Succeeded to send message to " << address_string << "!\n"; } + + closesocket(socket); } int main() { -- cgit v1.2.3 From 6d8fecb163a9c813a1b533970997353d33b6bf5e Mon Sep 17 00:00:00 2001 From: crupest Date: Sun, 6 Jun 2021 18:37:08 +0800 Subject: import(life): ... --- works/life/computer-network-experiment/Base.hpp | 59 ++++ .../computer-network-experiment/CMakeLists.txt | 7 +- .../life/computer-network-experiment/PreConfig.hpp | 6 + .../computer-network-experiment/StringUtil.cpp | 325 +++++++++++++++++++++ .../computer-network-experiment/StringUtil.hpp | 157 ++++++++++ works/life/computer-network-experiment/server.cpp | 77 ++++- 6 files changed, 615 insertions(+), 16 deletions(-) create mode 100644 works/life/computer-network-experiment/Base.hpp create mode 100644 works/life/computer-network-experiment/PreConfig.hpp create mode 100644 works/life/computer-network-experiment/StringUtil.cpp create mode 100644 works/life/computer-network-experiment/StringUtil.hpp (limited to 'works/life/computer-network-experiment/server.cpp') diff --git a/works/life/computer-network-experiment/Base.hpp b/works/life/computer-network-experiment/Base.hpp new file mode 100644 index 0000000..b1ad55c --- /dev/null +++ b/works/life/computer-network-experiment/Base.hpp @@ -0,0 +1,59 @@ +#pragma once +#include "PreConfig.hpp" + +#include +#include +#include + +#define CRU_UNUSED(entity) static_cast(entity); + +#define CRU__CONCAT(a, b) a##b +#define CRU_MAKE_UNICODE_LITERAL(str) CRU__CONCAT(u, #str) + +#define CRU_DEFAULT_COPY(classname) \ + classname(const classname&) = default; \ + classname& operator=(const classname&) = default; + +#define CRU_DEFAULT_MOVE(classname) \ + classname(classname&&) = default; \ + classname& operator=(classname&&) = default; + +#define CRU_DELETE_COPY(classname) \ + classname(const classname&) = delete; \ + classname& operator=(const classname&) = delete; + +#define CRU_DELETE_MOVE(classname) \ + classname(classname&&) = delete; \ + classname& operator=(classname&&) = delete; + +namespace cru { +class Object { + public: + Object() = default; + CRU_DEFAULT_COPY(Object) + CRU_DEFAULT_MOVE(Object) + virtual ~Object() = default; +}; + +struct Interface { + Interface() = default; + CRU_DELETE_COPY(Interface) + CRU_DELETE_MOVE(Interface) + virtual ~Interface() = default; +}; + +[[noreturn]] inline void UnreachableCode() { std::terminate(); } + +using Index = gsl::index; + +// https://www.boost.org/doc/libs/1_54_0/doc/html/hash/reference.html#boost.hash_combine +template +inline void hash_combine(std::size_t& s, const T& v) { + std::hash h; + s ^= h(v) + 0x9e3779b9 + (s << 6) + (s >> 2); +} + +#define CRU_DEFINE_CLASS_LOG_TAG(tag) \ + private: \ + constexpr static std::u16string_view log_tag = tag; +} // namespace cru diff --git a/works/life/computer-network-experiment/CMakeLists.txt b/works/life/computer-network-experiment/CMakeLists.txt index 41fec71..923de1f 100644 --- a/works/life/computer-network-experiment/CMakeLists.txt +++ b/works/life/computer-network-experiment/CMakeLists.txt @@ -7,8 +7,13 @@ project(network-experiment) set(CMAKE_CXX_STANDARD 17) +find_package(fmt CONFIG REQUIRED) +find_package(Microsoft.GSL CONFIG REQUIRED) +add_library(base STATIC StringUtil.cpp) +target_link_libraries(base PUBLIC Microsoft.GSL::GSL fmt::fmt) + add_executable(client client.cpp) add_executable(server server.cpp) find_package(folly CONFIG REQUIRED) -target_link_libraries(server PRIVATE Folly::folly) +target_link_libraries(server PRIVATE base Folly::folly) diff --git a/works/life/computer-network-experiment/PreConfig.hpp b/works/life/computer-network-experiment/PreConfig.hpp new file mode 100644 index 0000000..d78292c --- /dev/null +++ b/works/life/computer-network-experiment/PreConfig.hpp @@ -0,0 +1,6 @@ +#pragma once + +#ifdef _MSC_VER +// disable the unnecessary warning about multi-inheritance +#pragma warning(disable : 4250) +#endif diff --git a/works/life/computer-network-experiment/StringUtil.cpp b/works/life/computer-network-experiment/StringUtil.cpp new file mode 100644 index 0000000..1224bdc --- /dev/null +++ b/works/life/computer-network-experiment/StringUtil.cpp @@ -0,0 +1,325 @@ +#include "StringUtil.hpp" +#include "Base.hpp" +#include + +namespace cru { +namespace { +template +inline std::enable_if_t, ReturnType> ExtractBits( + UInt n) { + return static_cast(n & ((1u << number_of_bit) - 1)); +} +} // namespace + +CodePoint Utf8NextCodePoint(std::string_view str, Index current, + Index* next_position) { + CodePoint result; + + if (current >= static_cast(str.length())) { + result = k_invalid_code_point; + } else { + const auto cu0 = static_cast(str[current++]); + + auto read_next_folowing_code = [&str, ¤t]() -> CodePoint { + if (current == static_cast(str.length())) + throw TextEncodeException( + "Unexpected end when read continuing byte of multi-byte code " + "point."); + + const auto u = static_cast(str[current]); + if (!(u & (1u << 7)) || (u & (1u << 6))) { + throw TextEncodeException( + "Unexpected bad-format (not 0b10xxxxxx) continuing byte of " + "multi-byte code point."); + } + + return ExtractBits(str[current++]); + }; + + if ((1u << 7) & cu0) { + if ((1u << 6) & cu0) { // 2~4-length code point + if ((1u << 5) & cu0) { // 3~4-length code point + if ((1u << 4) & cu0) { // 4-length code point + if (cu0 & (1u << 3)) { + throw TextEncodeException( + "Unexpected bad-format begin byte (not 0b11110xxx) of 4-byte" + "code point."); + } + + const CodePoint s0 = ExtractBits(cu0) + << (6 * 3); + const CodePoint s1 = read_next_folowing_code() << (6 * 2); + const CodePoint s2 = read_next_folowing_code() << 6; + const CodePoint s3 = read_next_folowing_code(); + result = s0 + s1 + s2 + s3; + } else { // 3-length code point + const CodePoint s0 = ExtractBits(cu0) + << (6 * 2); + const CodePoint s1 = read_next_folowing_code() << 6; + const CodePoint s2 = read_next_folowing_code(); + result = s0 + s1 + s2; + } + } else { // 2-length code point + const CodePoint s0 = ExtractBits(cu0) + << 6; + const CodePoint s1 = read_next_folowing_code(); + result = s0 + s1; + } + } else { + throw TextEncodeException( + "Unexpected bad-format (0b10xxxxxx) begin byte of a code point."); + } + } else { + result = static_cast(cu0); + } + } + + if (next_position != nullptr) *next_position = current; + return result; +} + +CodePoint Utf16NextCodePoint(std::u16string_view str, Index current, + Index* next_position) { + CodePoint result; + + if (current >= static_cast(str.length())) { + result = k_invalid_code_point; + } else { + const auto cu0 = str[current++]; + + if (!IsUtf16SurrogatePairCodeUnit(cu0)) { // 1-length code point + result = static_cast(cu0); + } else if (IsUtf16SurrogatePairLeading(cu0)) { // 2-length code point + if (current >= static_cast(str.length())) { + throw TextEncodeException( + "Unexpected end when reading second code unit of surrogate pair."); + } + const auto cu1 = str[current++]; + + if (!IsUtf16SurrogatePairTrailing(cu1)) { + throw TextEncodeException( + "Unexpected bad-range second code unit of surrogate pair."); + } + + const auto s0 = ExtractBits(cu0) << 10; + const auto s1 = ExtractBits(cu1); + + result = s0 + s1 + 0x10000; + + } else { + throw TextEncodeException( + "Unexpected bad-range first code unit of surrogate pair."); + } + } + + if (next_position != nullptr) *next_position = current; + return result; +} + +CodePoint Utf16PreviousCodePoint(std::u16string_view str, Index current, + Index* previous_position) { + CodePoint result; + if (current <= 0) { + result = k_invalid_code_point; + } else { + const auto cu0 = str[--current]; + + if (!IsUtf16SurrogatePairCodeUnit(cu0)) { // 1-length code point + result = static_cast(cu0); + } else if (IsUtf16SurrogatePairTrailing(cu0)) { // 2-length code point + if (current <= 0) { + throw TextEncodeException( + "Unexpected end when reading first code unit of surrogate pair."); + } + const auto cu1 = str[--current]; + + if (!IsUtf16SurrogatePairLeading(cu1)) { + throw TextEncodeException( + "Unexpected bad-range first code unit of surrogate pair."); + } + + const auto s0 = ExtractBits(cu1) << 10; + const auto s1 = ExtractBits(cu0); + + result = s0 + s1 + 0x10000; + + } else { + throw TextEncodeException( + "Unexpected bad-range second code unit of surrogate pair."); + } + } + + if (previous_position != nullptr) *previous_position = current; + return result; +} + +void Utf8EncodeCodePointAppend(CodePoint code_point, std::string& str) { + auto write_continue_byte = [&str](std::uint8_t byte6) { + str.push_back((1u << 7) + (((1u << 6) - 1) & byte6)); + }; + + if (code_point >= 0 && code_point <= 0x007F) { + str.push_back(static_cast(code_point)); + } else if (code_point >= 0x0080 && code_point <= 0x07FF) { + std::uint32_t unsigned_code_point = code_point; + str.push_back(static_cast(ExtractBits( + (unsigned_code_point >> 6)) + + 0b11000000)); + write_continue_byte( + ExtractBits(unsigned_code_point)); + } else if (code_point >= 0x0800 && code_point <= 0xFFFF) { + std::uint32_t unsigned_code_point = code_point; + str.push_back(static_cast(ExtractBits( + (unsigned_code_point >> (6 * 2))) + + 0b11100000)); + write_continue_byte( + ExtractBits(unsigned_code_point >> 6)); + write_continue_byte( + ExtractBits(unsigned_code_point)); + } else if (code_point >= 0x10000 && code_point <= 0x10FFFF) { + std::uint32_t unsigned_code_point = code_point; + str.push_back(static_cast(ExtractBits( + (unsigned_code_point >> (6 * 3))) + + 0b11110000)); + write_continue_byte(ExtractBits( + unsigned_code_point >> (6 * 2))); + write_continue_byte( + ExtractBits(unsigned_code_point >> 6)); + write_continue_byte( + ExtractBits(unsigned_code_point)); + } else { + throw TextEncodeException("Code point out of range."); + } +} + +void Utf16EncodeCodePointAppend(CodePoint code_point, std::u16string& str) { + if ((code_point >= 0 && code_point <= 0xD7FF) || + (code_point >= 0xE000 && code_point <= 0xFFFF)) { + str.push_back(static_cast(code_point)); + } else if (code_point >= 0x10000 && code_point <= 0x10FFFF) { + std::uint32_t u = code_point - 0x10000; + str.push_back(static_cast( + ExtractBits(u >> 10) + 0xD800u)); + str.push_back(static_cast( + ExtractBits(u) + 0xDC00u)); + } else { + throw TextEncodeException("Code point out of range."); + } +} + +std::string ToUtf8(std::u16string_view s) { + std::string result; + for (CodePoint cp : Utf16CodePointIterator{s}) { + Utf8EncodeCodePointAppend(cp, result); + } + return result; +} + +std::u16string ToUtf16(std::string_view s) { + std::u16string result; + for (CodePoint cp : Utf8CodePointIterator{s}) { + Utf16EncodeCodePointAppend(cp, result); + } + return result; +} + +#ifdef WIN32 +std::wstring ToUtf16WString(std::string_view s) { + std::u16string result; + for (CodePoint cp : Utf8CodePointIterator{s}) { + Utf16EncodeCodePointAppend(cp, result); + } + + std::wstring r(result.cbegin(), result.cend()); + return r; +} +#endif + +bool Utf16IsValidInsertPosition(std::u16string_view s, gsl::index position) { + if (position < 0) return false; + if (position > static_cast(s.size())) return false; + if (position == 0) return true; + if (position == static_cast(s.size())) return true; + return !IsUtf16SurrogatePairTrailing(s[position]); +} + +gsl::index Utf16BackwardUntil(std::u16string_view str, gsl::index position, + const std::function& predicate) { + if (position <= 0) return position; + while (true) { + gsl::index p = position; + auto c = Utf16PreviousCodePoint(str, p, &position); + if (predicate(c)) return p; + if (c == k_invalid_code_point) return p; + } + UnreachableCode(); +} + +gsl::index Utf16ForwardUntil(std::u16string_view str, gsl::index position, + const std::function& predicate) { + if (position >= static_cast(str.size())) return position; + while (true) { + gsl::index p = position; + auto c = Utf16NextCodePoint(str, p, &position); + if (predicate(c)) return p; + if (c == k_invalid_code_point) return p; + } + UnreachableCode(); +} + +inline bool IsSpace(CodePoint c) { return c == 0x20 || c == 0xA; } + +gsl::index Utf16PreviousWord(std::u16string_view str, gsl::index position, + bool* is_space) { + if (position <= 0) return position; + auto c = Utf16PreviousCodePoint(str, position, nullptr); + if (IsSpace(c)) { // TODO: Currently only test against 0x20(space). + if (is_space) *is_space = true; + return Utf16BackwardUntil(str, position, + [](CodePoint c) { return !IsSpace(c); }); + } else { + if (is_space) *is_space = false; + return Utf16BackwardUntil(str, position, IsSpace); + } +} + +gsl::index Utf16NextWord(std::u16string_view str, gsl::index position, + bool* is_space) { + if (position >= static_cast(str.size())) return position; + auto c = Utf16NextCodePoint(str, position, nullptr); + if (IsSpace(c)) { // TODO: Currently only test against 0x20(space). + if (is_space) *is_space = true; + return Utf16ForwardUntil(str, position, + [](CodePoint c) { return !IsSpace(c); }); + } else { + if (is_space) *is_space = false; + return Utf16ForwardUntil(str, position, IsSpace); + } +} + +char16_t ToLower(char16_t c) { + if (c >= u'A' && c <= u'Z') { + return c - u'A' + u'a'; + } + return c; +} + +char16_t ToUpper(char16_t c) { + if (c >= u'a' && c <= u'z') { + return c - u'a' + u'A'; + } + return c; +} + +std::u16string ToLower(std::u16string_view s) { + std::u16string result; + for (auto c : s) result.push_back(ToLower(c)); + return result; +} + +std::u16string ToUpper(std::u16string_view s) { + std::u16string result; + for (auto c : s) result.push_back(ToUpper(c)); + return result; +} +} // namespace cru diff --git a/works/life/computer-network-experiment/StringUtil.hpp b/works/life/computer-network-experiment/StringUtil.hpp new file mode 100644 index 0000000..1a9634a --- /dev/null +++ b/works/life/computer-network-experiment/StringUtil.hpp @@ -0,0 +1,157 @@ +#pragma once +#include "Base.hpp" + +#include +#include +#include + +namespace cru { +using CodePoint = std::int32_t; +constexpr CodePoint k_invalid_code_point = -1; + +class TextEncodeException : public std::runtime_error { + public: + using runtime_error::runtime_error; +}; + +inline bool IsUtf16SurrogatePairCodeUnit(char16_t c) { + return c >= 0xD800 && c <= 0xDFFF; +} + +inline bool IsUtf16SurrogatePairLeading(char16_t c) { + return c >= 0xD800 && c <= 0xDBFF; +} + +inline bool IsUtf16SurrogatePairTrailing(char16_t c) { + return c >= 0xDC00 && c <= 0xDFFF; +} + +CodePoint Utf8NextCodePoint(std::string_view str, Index current, + Index* next_position); + +CodePoint Utf16NextCodePoint(std::u16string_view str, Index current, + Index* next_position); +CodePoint Utf16PreviousCodePoint(std::u16string_view str, Index current, + Index* previous_position); + +template +using NextCodePointFunctionType = CodePoint (*)(StringType, Index, Index*); + +template NextCodePointFunction> +class CodePointIterator { + public: + using difference_type = Index; + using value_type = CodePoint; + using pointer = void; + using reference = value_type; + using iterator_category = std::forward_iterator_tag; + + public: + struct past_end_tag_t {}; + + explicit CodePointIterator(StringType string) + : string_(std::move(string)), position_(0) {} + explicit CodePointIterator(StringType string, past_end_tag_t) + : string_(std::move(string)), position_(string_.size()) {} + + CRU_DEFAULT_COPY(CodePointIterator) + CRU_DEFAULT_MOVE(CodePointIterator) + + ~CodePointIterator() = default; + + public: + StringType GetString() const { return string_; } + Index GetPosition() const { return position_; } + + bool IsPastEnd() const { + return position_ == static_cast(string_.size()); + } + + public: + CodePointIterator begin() const { return *this; } + CodePointIterator end() const { + return CodePointIterator{string_, past_end_tag_t{}}; + } + + public: + bool operator==(const CodePointIterator& other) const { + // You should compare iterator that iterate on the same string. + Expects(this->string_.data() == other.string_.data() && + this->string_.size() == other.string_.size()); + return this->position_ == other.position_; + } + bool operator!=(const CodePointIterator& other) const { + return !this->operator==(other); + } + + CodePointIterator& operator++() { + Expects(!IsPastEnd()); + Forward(); + return *this; + } + + CodePointIterator operator++(int) { + Expects(!IsPastEnd()); + CodePointIterator old = *this; + Forward(); + return old; + } + + CodePoint operator*() const { + return NextCodePointFunction(string_, position_, &next_position_cache_); + } + + private: + void Forward() { + if (next_position_cache_ > position_) { + position_ = next_position_cache_; + } else { + NextCodePointFunction(string_, position_, &position_); + } + } + + private: + StringType string_; + Index position_; + mutable Index next_position_cache_; +}; + +using Utf8CodePointIterator = + CodePointIterator; + +using Utf16CodePointIterator = + CodePointIterator; + +void Utf8EncodeCodePointAppend(CodePoint code_point, std::string& str); +void Utf16EncodeCodePointAppend(CodePoint code_point, std::u16string& str); + +std::string ToUtf8(std::u16string_view s); +std::u16string ToUtf16(std::string_view s); + +#ifdef WIN32 +std::wstring ToUtf16WString(std::string_view s); +#endif + +// If given s is not a valid utf16 string, return value is UD. +bool Utf16IsValidInsertPosition(std::u16string_view s, gsl::index position); + +// Return position after the character making predicate returns true or 0 if no +// character doing so. +gsl::index Utf16BackwardUntil(std::u16string_view str, gsl::index position, + const std::function& predicate); +// Return position before the character making predicate returns true or +// str.size() if no character doing so. +gsl::index Utf16ForwardUntil(std::u16string_view str, gsl::index position, + const std::function& predicate); + +gsl::index Utf16PreviousWord(std::u16string_view str, gsl::index position, + bool* is_space = nullptr); +gsl::index Utf16NextWord(std::u16string_view str, gsl::index position, + bool* is_space = nullptr); + +char16_t ToLower(char16_t c); +char16_t ToUpper(char16_t c); +std::u16string ToLower(std::u16string_view s); +std::u16string ToUpper(std::u16string_view s); +} // namespace cru diff --git a/works/life/computer-network-experiment/server.cpp b/works/life/computer-network-experiment/server.cpp index 15470f4..de5bcd3 100644 --- a/works/life/computer-network-experiment/server.cpp +++ b/works/life/computer-network-experiment/server.cpp @@ -3,30 +3,83 @@ */ #include -#include #include -#include #include #include #include #include +#include +#include +#include + #include #include +#include "StringUtil.hpp" +#include "fmt/core.h" + #pragma comment(lib, "Ws2_32.lib") const auto bind_address = "127.0.0.1"; // control bind address const u_short port = 1234; // control bind port -// As far as I know, cout is not thread safe. So we need a lock. But this might -// not be the best solution. We can use a queue instead to avoid block. -std::mutex cout_mutex; +enum class OutputType { Normal, Error }; + +struct Output { + Output() = default; + Output(std::wstring message, OutputType type = OutputType::Normal) + : message(std::move(message)), type(type) {} + + CRU_DEFAULT_COPY(Output) + CRU_DEFAULT_MOVE(Output) + ~Output() = default; + + std::wstring message; + OutputType type; +}; + +folly::MPMCQueue output_queue; + +void SendOutput(std::wstring output) { + output_queue.blockingWrite(std::move(output)); +} + +void SendOutput(Output output) { + output_queue.blockingWrite(std::move(output)); +} + +template +void SendOutput(std::wstring_view format, Args &&...args) { + output_queue.blockingWrite(fmt::format(format, std::forward(args)...)); +} + +template +void SendOutput(OutputType type, std::wstring_view format, Args &&...args) { + output_queue.blockingWrite( + {fmt::format(format, std::forward(args)...), type}); +} + +void OutputThread() { + while (true) { + Output output; + output_queue.blockingRead(output); + switch (output.type) { + case OutputType::Error: + std::wcerr << output.message; + break; + default: + std::wcout << output.message; + break; + } + } +} [[noreturn]] void PrintErrorMessageAndExit(std::wstring_view message, std::optional error_code = std::nullopt) { - std::wcerr << message << L'\n'; + + SendOutput(L"{}\n", message); if (error_code) { std::cerr << L"Error code is " << std::hex << *error_code << L'\n'; @@ -48,10 +101,7 @@ PrintErrorMessageAndExit(std::wstring_view message, void ResponseThreadProc(int socket, sockaddr_in address) { auto address_string = inet_ntoa(address.sin_addr); - { - std::lock_guard guard(cout_mutex); - std::cout << "Connected to " << address_string << "!\n"; - } + SendOutput(L"Connected to {}!\n", cru::ToUtf16WString(address_string)); const std::string_view buffer = "Love you!!! By crupest!"; @@ -69,7 +119,6 @@ void ResponseThreadProc(int socket, sockaddr_in address) { // send failed if (byte_actually_sent == SOCKET_ERROR) { - std::lock_guard guard(cout_mutex); std::cerr << "Failed to send!\n"; closesocket(socket); break; @@ -78,10 +127,8 @@ void ResponseThreadProc(int socket, sockaddr_in address) { byte_count_sent += byte_actually_sent; } - { - std::lock_guard guard(cout_mutex); - std::cout << "Succeeded to send message to " << address_string << "!\n"; - } + SendOutput(L"Succeeded to send message to {} !\n", + cru::ToUtf16WString(address_string)); closesocket(socket); } -- cgit v1.2.3 From ff92a987ad05e40a1315306b31bbc4a219d2ee1d Mon Sep 17 00:00:00 2001 From: crupest Date: Sun, 6 Jun 2021 21:33:52 +0800 Subject: import(life): ... --- .../computer-network-experiment/CMakeLists.txt | 2 +- works/life/computer-network-experiment/Output.cpp | 18 ++++++ works/life/computer-network-experiment/Output.h | 46 +++++++++++++++ works/life/computer-network-experiment/server.cpp | 65 ++-------------------- 4 files changed, 70 insertions(+), 61 deletions(-) create mode 100644 works/life/computer-network-experiment/Output.cpp create mode 100644 works/life/computer-network-experiment/Output.h (limited to 'works/life/computer-network-experiment/server.cpp') diff --git a/works/life/computer-network-experiment/CMakeLists.txt b/works/life/computer-network-experiment/CMakeLists.txt index 923de1f..ecd8764 100644 --- a/works/life/computer-network-experiment/CMakeLists.txt +++ b/works/life/computer-network-experiment/CMakeLists.txt @@ -9,7 +9,7 @@ set(CMAKE_CXX_STANDARD 17) find_package(fmt CONFIG REQUIRED) find_package(Microsoft.GSL CONFIG REQUIRED) -add_library(base STATIC StringUtil.cpp) +add_library(base STATIC StringUtil.cpp Output.cpp) target_link_libraries(base PUBLIC Microsoft.GSL::GSL fmt::fmt) add_executable(client client.cpp) diff --git a/works/life/computer-network-experiment/Output.cpp b/works/life/computer-network-experiment/Output.cpp new file mode 100644 index 0000000..41e14e6 --- /dev/null +++ b/works/life/computer-network-experiment/Output.cpp @@ -0,0 +1,18 @@ +#include "Output.h" + +folly::MPMCQueue output_queue; + +void OutputThread() { + while (true) { + Output output; + output_queue.blockingRead(output); + switch (output.type) { + case OutputType::Error: + std::wcerr << output.message; + break; + default: + std::wcout << output.message; + break; + } + } +} diff --git a/works/life/computer-network-experiment/Output.h b/works/life/computer-network-experiment/Output.h new file mode 100644 index 0000000..0e53363 --- /dev/null +++ b/works/life/computer-network-experiment/Output.h @@ -0,0 +1,46 @@ +#pragma once +#include "StringUtil.hpp" + +#include + +#include +#include +#include + +enum class OutputType { Normal, Error }; + +struct Output { + Output() = default; + Output(std::wstring message, OutputType type = OutputType::Normal) + : message(std::move(message)), type(type) {} + + CRU_DEFAULT_COPY(Output) + CRU_DEFAULT_MOVE(Output) + ~Output() = default; + + std::wstring message; + OutputType type; +}; + +extern folly::MPMCQueue output_queue; + +inline void SendOutput(Output output) { + output_queue.blockingWrite(std::move(output)); +} + +inline void SendOutput(std::wstring output) { + SendOutput(std::move(output)); +} + +template +void SendOutput(std::wstring_view format, Args &&...args) { + output_queue.blockingWrite(fmt::format(format, std::forward(args)...)); +} + +template +void SendOutput(OutputType type, std::wstring_view format, Args &&...args) { + output_queue.blockingWrite( + Output{fmt::format(format, std::forward(args)...), type}); +} + +void OutputThread(); diff --git a/works/life/computer-network-experiment/server.cpp b/works/life/computer-network-experiment/server.cpp index de5bcd3..4942bb5 100644 --- a/works/life/computer-network-experiment/server.cpp +++ b/works/life/computer-network-experiment/server.cpp @@ -2,6 +2,8 @@ * This is the server program. */ +#include "Output.h" + #include #include #include @@ -9,71 +11,14 @@ #include #include -#include -#include -#include - #include #include -#include "StringUtil.hpp" -#include "fmt/core.h" - #pragma comment(lib, "Ws2_32.lib") const auto bind_address = "127.0.0.1"; // control bind address const u_short port = 1234; // control bind port -enum class OutputType { Normal, Error }; - -struct Output { - Output() = default; - Output(std::wstring message, OutputType type = OutputType::Normal) - : message(std::move(message)), type(type) {} - - CRU_DEFAULT_COPY(Output) - CRU_DEFAULT_MOVE(Output) - ~Output() = default; - - std::wstring message; - OutputType type; -}; - -folly::MPMCQueue output_queue; - -void SendOutput(std::wstring output) { - output_queue.blockingWrite(std::move(output)); -} - -void SendOutput(Output output) { - output_queue.blockingWrite(std::move(output)); -} - -template -void SendOutput(std::wstring_view format, Args &&...args) { - output_queue.blockingWrite(fmt::format(format, std::forward(args)...)); -} - -template -void SendOutput(OutputType type, std::wstring_view format, Args &&...args) { - output_queue.blockingWrite( - {fmt::format(format, std::forward(args)...), type}); -} - -void OutputThread() { - while (true) { - Output output; - output_queue.blockingRead(output); - switch (output.type) { - case OutputType::Error: - std::wcerr << output.message; - break; - default: - std::wcout << output.message; - break; - } - } -} [[noreturn]] void PrintErrorMessageAndExit(std::wstring_view message, @@ -82,15 +27,15 @@ PrintErrorMessageAndExit(std::wstring_view message, SendOutput(L"{}\n", message); if (error_code) { - std::cerr << L"Error code is " << std::hex << *error_code << L'\n'; + SendOutput(OutputType::Error, L"Error code is {}.\n", *error_code); wchar_t buffer[500]; if (!FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ARGUMENT_ARRAY | FORMAT_MESSAGE_IGNORE_INSERTS, nullptr, *error_code, 0, buffer, 500, nullptr)) { - std::wcerr << L"Failed to format error message.\n"; + SendOutput(OutputType::Error, L"Failed to format error message.\n"); } else { - std::wcerr << buffer << L'\n'; + SendOutput(OutputType::Error, L"{}\n", buffer); } } -- cgit v1.2.3 From f0309ee1e5cd268091f59f3aa377beca77d76c5c Mon Sep 17 00:00:00 2001 From: crupest Date: Sun, 6 Jun 2021 22:05:28 +0800 Subject: import(life): ... --- .../computer-network-experiment/CMakeLists.txt | 2 +- works/life/computer-network-experiment/Common.cpp | 48 ++++++++++++++++++++ works/life/computer-network-experiment/Common.h | 37 +++++++++++++++ works/life/computer-network-experiment/Output.cpp | 4 +- works/life/computer-network-experiment/Output.h | 17 ++++--- works/life/computer-network-experiment/server.cpp | 52 +++++----------------- 6 files changed, 106 insertions(+), 54 deletions(-) create mode 100644 works/life/computer-network-experiment/Common.cpp create mode 100644 works/life/computer-network-experiment/Common.h (limited to 'works/life/computer-network-experiment/server.cpp') diff --git a/works/life/computer-network-experiment/CMakeLists.txt b/works/life/computer-network-experiment/CMakeLists.txt index ecd8764..dc82736 100644 --- a/works/life/computer-network-experiment/CMakeLists.txt +++ b/works/life/computer-network-experiment/CMakeLists.txt @@ -9,7 +9,7 @@ set(CMAKE_CXX_STANDARD 17) find_package(fmt CONFIG REQUIRED) find_package(Microsoft.GSL CONFIG REQUIRED) -add_library(base STATIC StringUtil.cpp Output.cpp) +add_library(base STATIC Common.cpp StringUtil.cpp Output.cpp) target_link_libraries(base PUBLIC Microsoft.GSL::GSL fmt::fmt) add_executable(client client.cpp) diff --git a/works/life/computer-network-experiment/Common.cpp b/works/life/computer-network-experiment/Common.cpp new file mode 100644 index 0000000..02ac550 --- /dev/null +++ b/works/life/computer-network-experiment/Common.cpp @@ -0,0 +1,48 @@ +#include "Common.h" + +#include "Output.h" + +#ifdef WIN32 +#include +#include +#pragma comment(lib, "Ws2_32.lib") +#endif + +[[noreturn]] void PrintErrorMessageAndExit(StringView message, + bool print_last_error) { + + SendOutput(CRUT("{}\n"), message); + + if (print_last_error) { +#ifdef WIN32 + auto error_code = WSAGetLastError(); + SendOutput(OutputType::Error, CRUT("Error code is {}.\n"), error_code); + wchar_t buffer[500]; + if (!FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_ARGUMENT_ARRAY | + FORMAT_MESSAGE_IGNORE_INSERTS, + nullptr, error_code, 0, buffer, 500, nullptr)) { + SendOutput(OutputType::Error, CRUT("Failed to format error message.\n")); + } else { + SendOutput(OutputType::Error, CRUT("{}\n"), buffer); + } +#else +#endif + } + +#ifdef WIN32 + WSACleanup(); +#endif + + std::exit(1); +} + +#ifdef WIN32 +void InitWSA() { + WSADATA wsa_data; + + if (WSAStartup(MAKEWORD(2, 2), &wsa_data)) { // initialize wsa + PrintErrorMessageAndExit(CRUT("Failed to initialize wsa.")); + } +} +#endif diff --git a/works/life/computer-network-experiment/Common.h b/works/life/computer-network-experiment/Common.h new file mode 100644 index 0000000..1f4fa23 --- /dev/null +++ b/works/life/computer-network-experiment/Common.h @@ -0,0 +1,37 @@ +#pragma once +#include "StringUtil.hpp" + +#include +#include +#include + +#ifdef WIN32 +using Char = wchar_t; +using String = std::wstring; +using StringView = std::wstring_view; +inline auto &input_stream = std::wcin; +inline auto &output_stream = std::wcout; +inline auto &error_stream = std::wcerr; +#define CRUT(string_literal) L##string_literal + +inline String ConvertCharString(std::string_view s) { + return cru::ToUtf16WString(s); +} +#else +using Char = char; +using String = std::string; +using StringView = std::string_view; +inline auto &input_stream = std::cin; +inline auto &output_stream = std::cout; +inline auto &error_stream = std::cerr; +#define CRUT(string_literal) string_literal + +inline String ConvertCharString(std::string_view s) { return s; } +#endif + +[[noreturn]] void PrintErrorMessageAndExit(StringView message, + bool print_last_error = true); + +#ifdef WIN32 +void InitWSA(); +#endif diff --git a/works/life/computer-network-experiment/Output.cpp b/works/life/computer-network-experiment/Output.cpp index 41e14e6..ac4a47b 100644 --- a/works/life/computer-network-experiment/Output.cpp +++ b/works/life/computer-network-experiment/Output.cpp @@ -8,10 +8,10 @@ void OutputThread() { output_queue.blockingRead(output); switch (output.type) { case OutputType::Error: - std::wcerr << output.message; + error_stream << output.message; break; default: - std::wcout << output.message; + output_stream << output.message; break; } } diff --git a/works/life/computer-network-experiment/Output.h b/works/life/computer-network-experiment/Output.h index 0e53363..157e5c8 100644 --- a/works/life/computer-network-experiment/Output.h +++ b/works/life/computer-network-experiment/Output.h @@ -1,24 +1,25 @@ #pragma once +#include "Common.h" #include "StringUtil.hpp" -#include - #include #include #include +#include + enum class OutputType { Normal, Error }; struct Output { Output() = default; - Output(std::wstring message, OutputType type = OutputType::Normal) + Output(String message, OutputType type = OutputType::Normal) : message(std::move(message)), type(type) {} CRU_DEFAULT_COPY(Output) CRU_DEFAULT_MOVE(Output) ~Output() = default; - std::wstring message; + String message; OutputType type; }; @@ -28,17 +29,15 @@ inline void SendOutput(Output output) { output_queue.blockingWrite(std::move(output)); } -inline void SendOutput(std::wstring output) { - SendOutput(std::move(output)); -} +inline void SendOutput(String output) { SendOutput(std::move(output)); } template -void SendOutput(std::wstring_view format, Args &&...args) { +void SendOutput(StringView format, Args &&...args) { output_queue.blockingWrite(fmt::format(format, std::forward(args)...)); } template -void SendOutput(OutputType type, std::wstring_view format, Args &&...args) { +void SendOutput(OutputType type, StringView format, Args &&...args) { output_queue.blockingWrite( Output{fmt::format(format, std::forward(args)...), type}); } diff --git a/works/life/computer-network-experiment/server.cpp b/works/life/computer-network-experiment/server.cpp index 4942bb5..297114b 100644 --- a/works/life/computer-network-experiment/server.cpp +++ b/works/life/computer-network-experiment/server.cpp @@ -11,42 +11,12 @@ #include #include -#include -#include - -#pragma comment(lib, "Ws2_32.lib") - const auto bind_address = "127.0.0.1"; // control bind address const u_short port = 1234; // control bind port - -[[noreturn]] void -PrintErrorMessageAndExit(std::wstring_view message, - std::optional error_code = std::nullopt) { - - SendOutput(L"{}\n", message); - - if (error_code) { - SendOutput(OutputType::Error, L"Error code is {}.\n", *error_code); - wchar_t buffer[500]; - if (!FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM | - FORMAT_MESSAGE_ARGUMENT_ARRAY | - FORMAT_MESSAGE_IGNORE_INSERTS, - nullptr, *error_code, 0, buffer, 500, nullptr)) { - SendOutput(OutputType::Error, L"Failed to format error message.\n"); - } else { - SendOutput(OutputType::Error, L"{}\n", buffer); - } - } - - WSACleanup(); - - std::exit(1); -} - void ResponseThreadProc(int socket, sockaddr_in address) { auto address_string = inet_ntoa(address.sin_addr); - SendOutput(L"Connected to {}!\n", cru::ToUtf16WString(address_string)); + SendOutput(CRUT("Connected to {}!\n"), ConvertCharString(address_string)); const std::string_view buffer = "Love you!!! By crupest!"; @@ -72,23 +42,21 @@ void ResponseThreadProc(int socket, sockaddr_in address) { byte_count_sent += byte_actually_sent; } - SendOutput(L"Succeeded to send message to {} !\n", - cru::ToUtf16WString(address_string)); + SendOutput(CRUT("Succeeded to send message to {} !\n"), + ConvertCharString(address_string)); closesocket(socket); } int main() { - WSADATA wsa_data; - - if (WSAStartup(MAKEWORD(2, 2), &wsa_data)) { // initialize wsa - PrintErrorMessageAndExit(L"Failed to initialize wsa.", WSAGetLastError()); - } +#ifdef WIN32 + InitWSA(); +#endif int server_socket; if ((server_socket = socket(AF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) { - PrintErrorMessageAndExit(L"Failed to create socket.", WSAGetLastError()); + PrintErrorMessageAndExit(CRUT("Failed to create socket.")); } sockaddr_in server_address; @@ -100,11 +68,11 @@ int main() { if (bind(server_socket, reinterpret_cast(&server_address), sizeof(sockaddr_in)) == SOCKET_ERROR) { - PrintErrorMessageAndExit(L"Failed to bind.", WSAGetLastError()); + PrintErrorMessageAndExit(CRUT("Failed to bind.")); } if (listen(server_socket, SOMAXCONN) == SOCKET_ERROR) { - PrintErrorMessageAndExit(L"Failed to listen.", WSAGetLastError()); + PrintErrorMessageAndExit(CRUT("Failed to listen.")); } while (true) { @@ -116,7 +84,7 @@ int main() { &sin_size); if (client_socket == INVALID_SOCKET) { - PrintErrorMessageAndExit(L"Failed to accecpt", WSAGetLastError()); + PrintErrorMessageAndExit(CRUT("Failed to accecpt")); } std::thread response_thread(ResponseThreadProc, client_socket, -- cgit v1.2.3 From fcd2304e04dd3fabdefce4293b1b76ad99f8ed73 Mon Sep 17 00:00:00 2001 From: crupest Date: Sun, 6 Jun 2021 22:13:43 +0800 Subject: import(life): ... --- .../computer-network-experiment/CMakeLists.txt | 8 +++-- works/life/computer-network-experiment/Common.cpp | 15 ++++++++- works/life/computer-network-experiment/Common.h | 2 ++ works/life/computer-network-experiment/client.cpp | 38 +++++++--------------- works/life/computer-network-experiment/server.cpp | 16 ++++----- 5 files changed, 40 insertions(+), 39 deletions(-) (limited to 'works/life/computer-network-experiment/server.cpp') diff --git a/works/life/computer-network-experiment/CMakeLists.txt b/works/life/computer-network-experiment/CMakeLists.txt index dc82736..4d4277c 100644 --- a/works/life/computer-network-experiment/CMakeLists.txt +++ b/works/life/computer-network-experiment/CMakeLists.txt @@ -10,10 +10,14 @@ set(CMAKE_CXX_STANDARD 17) find_package(fmt CONFIG REQUIRED) find_package(Microsoft.GSL CONFIG REQUIRED) add_library(base STATIC Common.cpp StringUtil.cpp Output.cpp) -target_link_libraries(base PUBLIC Microsoft.GSL::GSL fmt::fmt) +target_link_libraries(base PUBLIC Microsoft.GSL::GSL fmt::fmt Folly::folly) +if(WIN32) +target_link_libraries(base PUBLIC Ws2_32) +endif() add_executable(client client.cpp) +target_link_libraries(client PRIVATE base) add_executable(server server.cpp) find_package(folly CONFIG REQUIRED) -target_link_libraries(server PRIVATE base Folly::folly) +target_link_libraries(server PRIVATE base) diff --git a/works/life/computer-network-experiment/Common.cpp b/works/life/computer-network-experiment/Common.cpp index 02ac550..2e01dcb 100644 --- a/works/life/computer-network-experiment/Common.cpp +++ b/works/life/computer-network-experiment/Common.cpp @@ -5,7 +5,6 @@ #ifdef WIN32 #include #include -#pragma comment(lib, "Ws2_32.lib") #endif [[noreturn]] void PrintErrorMessageAndExit(StringView message, @@ -46,3 +45,17 @@ void InitWSA() { } } #endif + +int main() { +#ifdef WIN32 + InitWSA(); +#endif + + int c = Main(); + +#ifdef WIN32 + WSACleanup(); +#endif + + return c; +} diff --git a/works/life/computer-network-experiment/Common.h b/works/life/computer-network-experiment/Common.h index 1f4fa23..45a7da1 100644 --- a/works/life/computer-network-experiment/Common.h +++ b/works/life/computer-network-experiment/Common.h @@ -14,6 +14,8 @@ inline auto &output_stream = std::wcout; inline auto &error_stream = std::wcerr; #define CRUT(string_literal) L##string_literal +int Main(); + inline String ConvertCharString(std::string_view s) { return cru::ToUtf16WString(s); } diff --git a/works/life/computer-network-experiment/client.cpp b/works/life/computer-network-experiment/client.cpp index 926ece6..aeb352f 100644 --- a/works/life/computer-network-experiment/client.cpp +++ b/works/life/computer-network-experiment/client.cpp @@ -2,32 +2,22 @@ * This is the client program. */ +#include "Common.h" +#include "Output.h" + +#ifdef WIN32 #include -#include -#include #include - -#pragma comment(lib, "ws2_32.lib") +#endif const auto connect_address = "127.0.0.1"; // control connect address const u_short port = 1234; // control connect port -int main() { - WSADATA wsa_data; - - if (WSAStartup(MAKEWORD(2, 2), &wsa_data)) // initialize wsa - { - std::cerr << "WSA start up error!\n"; - WSACleanup(); - return 1; - } - +int Main() { int client_socket; if ((client_socket = socket(AF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) { - std::cerr << "Failed to create socket!\n"; - WSACleanup(); - return 1; + PrintErrorMessageAndExit(CRUT("Failed to create socket!\n")); } sockaddr_in server_address; @@ -39,28 +29,22 @@ int main() { if (connect(client_socket, (sockaddr *)&server_address, sizeof(sockaddr)) == SOCKET_ERROR) { - std::cerr << "Failed to connect!"; - WSACleanup(); - return 1; + PrintErrorMessageAndExit(CRUT("Failed to connect!")); } const int buffer_size = 100; - char * buffer = new char[buffer_size]; + char *buffer = new char[buffer_size]; int received_number = recv(client_socket, buffer, buffer_size, 0); if (received_number == -1) { - std::cerr << "Failed to recv."; - WSACleanup(); - return 1; + PrintErrorMessageAndExit(CRUT("Failed to recv.")); } std::string s(buffer, received_number); - std::cout << "Received message:\n" << s; + SendOutput(CRUT("Received message:\n")); closesocket(client_socket); - WSACleanup(); - return 0; } diff --git a/works/life/computer-network-experiment/server.cpp b/works/life/computer-network-experiment/server.cpp index 297114b..9cf655a 100644 --- a/works/life/computer-network-experiment/server.cpp +++ b/works/life/computer-network-experiment/server.cpp @@ -2,15 +2,17 @@ * This is the server program. */ +#include "Common.h" #include "Output.h" -#include -#include #include -#include -#include #include +#ifdef WIN32 +#include +#include +#endif + const auto bind_address = "127.0.0.1"; // control bind address const u_short port = 1234; // control bind port @@ -48,11 +50,7 @@ void ResponseThreadProc(int socket, sockaddr_in address) { closesocket(socket); } -int main() { -#ifdef WIN32 - InitWSA(); -#endif - +int Main() { int server_socket; if ((server_socket = socket(AF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) { -- cgit v1.2.3 From 7a269e185fc59e5f81e7c91e6e75891ba2ca4b3b Mon Sep 17 00:00:00 2001 From: crupest Date: Sun, 6 Jun 2021 23:43:52 +0800 Subject: import(life): ... --- works/life/computer-network-experiment/Common.cpp | 13 +++++++++++++ works/life/computer-network-experiment/Common.h | 8 +++++--- works/life/computer-network-experiment/client.cpp | 11 ++++++++--- works/life/computer-network-experiment/server.cpp | 23 ++++++++++++++--------- 4 files changed, 40 insertions(+), 15 deletions(-) (limited to 'works/life/computer-network-experiment/server.cpp') diff --git a/works/life/computer-network-experiment/Common.cpp b/works/life/computer-network-experiment/Common.cpp index 2e01dcb..59c2f0f 100644 --- a/works/life/computer-network-experiment/Common.cpp +++ b/works/life/computer-network-experiment/Common.cpp @@ -5,6 +5,11 @@ #ifdef WIN32 #include #include +#else +#include +#include +#include +#include #endif [[noreturn]] void PrintErrorMessageAndExit(StringView message, @@ -46,6 +51,14 @@ void InitWSA() { } #endif +int Close(int socket) { +#ifdef WIN32 + return closesocket(socket); +#else + return close(socket); +#endif +} + int main() { #ifdef WIN32 InitWSA(); diff --git a/works/life/computer-network-experiment/Common.h b/works/life/computer-network-experiment/Common.h index 45a7da1..4e30439 100644 --- a/works/life/computer-network-experiment/Common.h +++ b/works/life/computer-network-experiment/Common.h @@ -14,8 +14,6 @@ inline auto &output_stream = std::wcout; inline auto &error_stream = std::wcerr; #define CRUT(string_literal) L##string_literal -int Main(); - inline String ConvertCharString(std::string_view s) { return cru::ToUtf16WString(s); } @@ -28,12 +26,16 @@ inline auto &output_stream = std::cout; inline auto &error_stream = std::cerr; #define CRUT(string_literal) string_literal -inline String ConvertCharString(std::string_view s) { return s; } +inline String ConvertCharString(std::string_view s) { return String(s); } #endif +int Main(); + [[noreturn]] void PrintErrorMessageAndExit(StringView message, bool print_last_error = true); #ifdef WIN32 void InitWSA(); #endif + +int Close(int socket); diff --git a/works/life/computer-network-experiment/client.cpp b/works/life/computer-network-experiment/client.cpp index aeb352f..f209171 100644 --- a/works/life/computer-network-experiment/client.cpp +++ b/works/life/computer-network-experiment/client.cpp @@ -8,6 +8,11 @@ #ifdef WIN32 #include #include +#else +#include +#include +#include + #endif const auto connect_address = "127.0.0.1"; // control connect address @@ -16,7 +21,7 @@ const u_short port = 1234; // control connect port int Main() { int client_socket; - if ((client_socket = socket(AF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) { + if ((client_socket = socket(AF_INET, SOCK_STREAM, 0)) == -1) { PrintErrorMessageAndExit(CRUT("Failed to create socket!\n")); } @@ -28,7 +33,7 @@ int Main() { memset(&(server_address.sin_zero), 0, sizeof(server_address.sin_zero)); if (connect(client_socket, (sockaddr *)&server_address, sizeof(sockaddr)) == - SOCKET_ERROR) { + -1) { PrintErrorMessageAndExit(CRUT("Failed to connect!")); } @@ -45,6 +50,6 @@ int Main() { SendOutput(CRUT("Received message:\n")); - closesocket(client_socket); + Close(client_socket); return 0; } diff --git a/works/life/computer-network-experiment/server.cpp b/works/life/computer-network-experiment/server.cpp index 9cf655a..ff03783 100644 --- a/works/life/computer-network-experiment/server.cpp +++ b/works/life/computer-network-experiment/server.cpp @@ -11,6 +11,11 @@ #ifdef WIN32 #include #include +#else +#include +#include +#include + #endif const auto bind_address = "127.0.0.1"; // control bind address @@ -35,9 +40,9 @@ void ResponseThreadProc(int socket, sockaddr_in address) { buffer.size() - byte_count_sent, 0); // send failed - if (byte_actually_sent == SOCKET_ERROR) { - std::cerr << "Failed to send!\n"; - closesocket(socket); + if (byte_actually_sent == -1) { + SendOutput(OutputType::Error, CRUT("Failed to send!\n")); + Close(socket); break; } @@ -47,13 +52,13 @@ void ResponseThreadProc(int socket, sockaddr_in address) { SendOutput(CRUT("Succeeded to send message to {} !\n"), ConvertCharString(address_string)); - closesocket(socket); + Close(socket); } int Main() { int server_socket; - if ((server_socket = socket(AF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) { + if ((server_socket = socket(AF_INET, SOCK_STREAM, 0)) == -1) { PrintErrorMessageAndExit(CRUT("Failed to create socket.")); } @@ -65,23 +70,23 @@ int Main() { memset(&(server_address.sin_zero), 0, sizeof(server_address.sin_zero)); if (bind(server_socket, reinterpret_cast(&server_address), - sizeof(sockaddr_in)) == SOCKET_ERROR) { + sizeof(sockaddr_in)) == -1) { PrintErrorMessageAndExit(CRUT("Failed to bind.")); } - if (listen(server_socket, SOMAXCONN) == SOCKET_ERROR) { + if (listen(server_socket, SOMAXCONN) == -1) { PrintErrorMessageAndExit(CRUT("Failed to listen.")); } while (true) { sockaddr_in client_address; int client_socket; - int sin_size = sizeof(sockaddr_in); + unsigned sin_size = sizeof(sockaddr_in); client_socket = accept(server_socket, reinterpret_cast(&client_address), &sin_size); - if (client_socket == INVALID_SOCKET) { + if (client_socket == -1) { PrintErrorMessageAndExit(CRUT("Failed to accecpt")); } -- cgit v1.2.3 From 2de4663d385ab54c5d8b8adc68611ad67636f56f Mon Sep 17 00:00:00 2001 From: crupest Date: Sun, 6 Jun 2021 23:51:32 +0800 Subject: import(life): ... --- works/life/computer-network-experiment/Output.cpp | 2 +- works/life/computer-network-experiment/Output.h | 5 ++--- works/life/computer-network-experiment/server.cpp | 7 ++++++- 3 files changed, 9 insertions(+), 5 deletions(-) (limited to 'works/life/computer-network-experiment/server.cpp') diff --git a/works/life/computer-network-experiment/Output.cpp b/works/life/computer-network-experiment/Output.cpp index ac4a47b..2968c19 100644 --- a/works/life/computer-network-experiment/Output.cpp +++ b/works/life/computer-network-experiment/Output.cpp @@ -1,6 +1,6 @@ #include "Output.h" -folly::MPMCQueue output_queue; +folly::MPMCQueue output_queue(100); void OutputThread() { while (true) { diff --git a/works/life/computer-network-experiment/Output.h b/works/life/computer-network-experiment/Output.h index 157e5c8..b81dbfd 100644 --- a/works/life/computer-network-experiment/Output.h +++ b/works/life/computer-network-experiment/Output.h @@ -29,10 +29,9 @@ inline void SendOutput(Output output) { output_queue.blockingWrite(std::move(output)); } -inline void SendOutput(String output) { SendOutput(std::move(output)); } +inline void SendOutput(String output) { SendOutput(Output{std::move(output)}); } -template -void SendOutput(StringView format, Args &&...args) { +template void SendOutput(StringView format, Args &&...args) { output_queue.blockingWrite(fmt::format(format, std::forward(args)...)); } diff --git a/works/life/computer-network-experiment/server.cpp b/works/life/computer-network-experiment/server.cpp index ff03783..a5a7a9b 100644 --- a/works/life/computer-network-experiment/server.cpp +++ b/works/life/computer-network-experiment/server.cpp @@ -56,6 +56,8 @@ void ResponseThreadProc(int socket, sockaddr_in address) { } int Main() { + std::thread output_thread(OutputThread); + int server_socket; if ((server_socket = socket(AF_INET, SOCK_STREAM, 0)) == -1) { @@ -84,7 +86,10 @@ int Main() { unsigned sin_size = sizeof(sockaddr_in); client_socket = accept(server_socket, reinterpret_cast(&client_address), - &sin_size); +#ifdef WIN32 + reinterpret_cast +#endif + (&sin_size)); if (client_socket == -1) { PrintErrorMessageAndExit(CRUT("Failed to accecpt")); -- cgit v1.2.3 From da6c0e6194578538ce0bcd1b9815696b96153f6b Mon Sep 17 00:00:00 2001 From: crupest Date: Mon, 7 Jun 2021 14:05:51 +0800 Subject: import(life): ... --- works/life/computer-network-experiment/Common.cpp | 19 +++++----- works/life/computer-network-experiment/Common.h | 4 ++- works/life/computer-network-experiment/Output.cpp | 43 ++++++++++++++++++----- works/life/computer-network-experiment/Output.h | 6 ++++ works/life/computer-network-experiment/client.cpp | 4 +-- works/life/computer-network-experiment/server.cpp | 12 +++---- 6 files changed, 62 insertions(+), 26 deletions(-) (limited to 'works/life/computer-network-experiment/server.cpp') diff --git a/works/life/computer-network-experiment/Common.cpp b/works/life/computer-network-experiment/Common.cpp index 59c2f0f..fbdb2c8 100644 --- a/works/life/computer-network-experiment/Common.cpp +++ b/works/life/computer-network-experiment/Common.cpp @@ -34,9 +34,7 @@ #endif } -#ifdef WIN32 - WSACleanup(); -#endif + BeforeExit(); std::exit(1); } @@ -51,7 +49,7 @@ void InitWSA() { } #endif -int Close(int socket) { +int CloseSocket(int socket) { #ifdef WIN32 return closesocket(socket); #else @@ -59,16 +57,21 @@ int Close(int socket) { #endif } -int main() { +void BeforeExit() { #ifdef WIN32 - InitWSA(); + WSACleanup(); #endif - int c = Main(); + SignalAndWaitForOutputThreadStop(); +} +int main() { #ifdef WIN32 - WSACleanup(); + InitWSA(); #endif + int c = Main(); + + BeforeExit(); return c; } diff --git a/works/life/computer-network-experiment/Common.h b/works/life/computer-network-experiment/Common.h index 4e30439..e5612fd 100644 --- a/works/life/computer-network-experiment/Common.h +++ b/works/life/computer-network-experiment/Common.h @@ -38,4 +38,6 @@ int Main(); void InitWSA(); #endif -int Close(int socket); +int CloseSocket(int socket); + +void BeforeExit(); diff --git a/works/life/computer-network-experiment/Output.cpp b/works/life/computer-network-experiment/Output.cpp index 2968c19..8efb525 100644 --- a/works/life/computer-network-experiment/Output.cpp +++ b/works/life/computer-network-experiment/Output.cpp @@ -1,18 +1,43 @@ #include "Output.h" +#include "folly/CancellationToken.h" folly::MPMCQueue output_queue(100); +folly::CancellationSource cancellation_source; + +std::thread output_thread(OutputThread); + +void PrintOutput(const Output &output) { + switch (output.type) { + case OutputType::Error: + error_stream << output.message; + break; + default: + output_stream << output.message; + break; + } +} + void OutputThread() { while (true) { - Output output; - output_queue.blockingRead(output); - switch (output.type) { - case OutputType::Error: - error_stream << output.message; - break; - default: - output_stream << output.message; - break; + if (cancellation_source.getToken().isCancellationRequested()) { + while (true) { + Output output; + if (output_queue.readIfNotEmpty(output)) { + PrintOutput(output); + } else { + return; + } + } } + + Output output; + if (output_queue.readIfNotEmpty(output)) + PrintOutput(output); } } + +void SignalAndWaitForOutputThreadStop() { + cancellation_source.requestCancellation(); + output_thread.join(); +} diff --git a/works/life/computer-network-experiment/Output.h b/works/life/computer-network-experiment/Output.h index b81dbfd..22b913a 100644 --- a/works/life/computer-network-experiment/Output.h +++ b/works/life/computer-network-experiment/Output.h @@ -5,7 +5,9 @@ #include #include #include +#include +#include #include enum class OutputType { Normal, Error }; @@ -42,3 +44,7 @@ void SendOutput(OutputType type, StringView format, Args &&...args) { } void OutputThread(); + +void SignalAndWaitForOutputThreadStop(); + +extern std::thread output_thread; diff --git a/works/life/computer-network-experiment/client.cpp b/works/life/computer-network-experiment/client.cpp index f209171..5d5075e 100644 --- a/works/life/computer-network-experiment/client.cpp +++ b/works/life/computer-network-experiment/client.cpp @@ -48,8 +48,8 @@ int Main() { std::string s(buffer, received_number); - SendOutput(CRUT("Received message:\n")); + SendOutput(CRUT("Received message:\n{}\n"), ConvertCharString(s)); - Close(client_socket); + CloseSocket(client_socket); return 0; } diff --git a/works/life/computer-network-experiment/server.cpp b/works/life/computer-network-experiment/server.cpp index a5a7a9b..14987f3 100644 --- a/works/life/computer-network-experiment/server.cpp +++ b/works/life/computer-network-experiment/server.cpp @@ -42,22 +42,20 @@ void ResponseThreadProc(int socket, sockaddr_in address) { // send failed if (byte_actually_sent == -1) { SendOutput(OutputType::Error, CRUT("Failed to send!\n")); - Close(socket); + CloseSocket(socket); break; } byte_count_sent += byte_actually_sent; } - SendOutput(CRUT("Succeeded to send message to {} !\n"), + SendOutput(CRUT("Succeeded to send message to {}!\n"), ConvertCharString(address_string)); - Close(socket); + CloseSocket(socket); } int Main() { - std::thread output_thread(OutputThread); - int server_socket; if ((server_socket = socket(AF_INET, SOCK_STREAM, 0)) == -1) { @@ -81,6 +79,8 @@ int Main() { } while (true) { + SendOutput(CRUT("Now start to accept incoming connection.\n")); + sockaddr_in client_address; int client_socket; unsigned sin_size = sizeof(sockaddr_in); @@ -92,7 +92,7 @@ int Main() { (&sin_size)); if (client_socket == -1) { - PrintErrorMessageAndExit(CRUT("Failed to accecpt")); + PrintErrorMessageAndExit(CRUT("Failed to accecpt.")); } std::thread response_thread(ResponseThreadProc, client_socket, -- cgit v1.2.3 From 868936626cd32590b697cc5ba0c67e032deabd38 Mon Sep 17 00:00:00 2001 From: crupest Date: Mon, 7 Jun 2021 19:24:39 +0800 Subject: import(life): ... --- works/life/computer-network-experiment/Output.cpp | 27 ++++++++++++++++++++--- works/life/computer-network-experiment/Output.h | 22 +++++++++++++++--- works/life/computer-network-experiment/client.cpp | 3 ++- works/life/computer-network-experiment/server.cpp | 7 +++--- 4 files changed, 49 insertions(+), 10 deletions(-) (limited to 'works/life/computer-network-experiment/server.cpp') diff --git a/works/life/computer-network-experiment/Output.cpp b/works/life/computer-network-experiment/Output.cpp index 8efb525..e3dc69e 100644 --- a/works/life/computer-network-experiment/Output.cpp +++ b/works/life/computer-network-experiment/Output.cpp @@ -1,5 +1,9 @@ #include "Output.h" -#include "folly/CancellationToken.h" + +#include + +#include +#include folly::MPMCQueue output_queue(100); @@ -8,12 +12,29 @@ folly::CancellationSource cancellation_source; std::thread output_thread(OutputThread); void PrintOutput(const Output &output) { + std::basic_ostream *stream; + switch (output.type) { case OutputType::Error: - error_stream << output.message; + stream = &error_stream; break; default: - output_stream << output.message; + stream = &output_stream; + break; + } + + switch (output.color) { + case OutputColor::Normal: + (*stream) << output.message; + break; + case OutputColor::Green: + (*stream) << CRUT("\x1b[32m") << output.message << CRUT("\x1b[0m"); + break; + case OutputColor::Red: + (*stream) << CRUT("\x1b[31m") << output.message << CRUT("\x1b[0m"); + break; + case OutputColor::Yellow: + (*stream) << CRUT("\x1b[33m") << output.message << CRUT("\x1b[0m"); break; } } diff --git a/works/life/computer-network-experiment/Output.h b/works/life/computer-network-experiment/Output.h index 22b913a..2d16eb0 100644 --- a/works/life/computer-network-experiment/Output.h +++ b/works/life/computer-network-experiment/Output.h @@ -3,19 +3,28 @@ #include "StringUtil.hpp" #include +#include #include #include -#include -#include #include +#include enum class OutputType { Normal, Error }; +enum class OutputColor { Normal, Green, Red, Yellow }; struct Output { Output() = default; Output(String message, OutputType type = OutputType::Normal) - : message(std::move(message)), type(type) {} + : message(std::move(message)), type(type), + color(type == OutputType::Error ? OutputColor::Red + : OutputColor::Normal) {} + + Output(String message, OutputColor color) + : message(std::move(message)), type(OutputType::Normal), color(color) {} + + Output(String message, OutputType type, OutputColor color) + : message(std::move(message)), type(type), color(color) {} CRU_DEFAULT_COPY(Output) CRU_DEFAULT_MOVE(Output) @@ -23,6 +32,7 @@ struct Output { String message; OutputType type; + OutputColor color; }; extern folly::MPMCQueue output_queue; @@ -43,6 +53,12 @@ void SendOutput(OutputType type, StringView format, Args &&...args) { Output{fmt::format(format, std::forward(args)...), type}); } +template +void SendOutput(OutputColor color, StringView format, Args &&...args) { + output_queue.blockingWrite( + Output{fmt::format(format, std::forward(args)...), color}); +} + void OutputThread(); void SignalAndWaitForOutputThreadStop(); diff --git a/works/life/computer-network-experiment/client.cpp b/works/life/computer-network-experiment/client.cpp index 5d5075e..922ecdc 100644 --- a/works/life/computer-network-experiment/client.cpp +++ b/works/life/computer-network-experiment/client.cpp @@ -48,7 +48,8 @@ int Main() { std::string s(buffer, received_number); - SendOutput(CRUT("Received message:\n{}\n"), ConvertCharString(s)); + SendOutput(OutputColor::Green, CRUT("Received message:\n")); + SendOutput(OutputColor::Normal, CRUT("{}\n"), ConvertCharString(s)); CloseSocket(client_socket); return 0; diff --git a/works/life/computer-network-experiment/server.cpp b/works/life/computer-network-experiment/server.cpp index 14987f3..f12630a 100644 --- a/works/life/computer-network-experiment/server.cpp +++ b/works/life/computer-network-experiment/server.cpp @@ -49,7 +49,7 @@ void ResponseThreadProc(int socket, sockaddr_in address) { byte_count_sent += byte_actually_sent; } - SendOutput(CRUT("Succeeded to send message to {}!\n"), + SendOutput(OutputColor::Green, CRUT("Succeeded to send message to {}!\n"), ConvertCharString(address_string)); CloseSocket(socket); @@ -78,9 +78,10 @@ int Main() { PrintErrorMessageAndExit(CRUT("Failed to listen.")); } - while (true) { - SendOutput(CRUT("Now start to accept incoming connection.\n")); + SendOutput(OutputColor::Green, + CRUT("Now start to accept incoming connection.\n")); + while (true) { sockaddr_in client_address; int client_socket; unsigned sin_size = sizeof(sockaddr_in); -- cgit v1.2.3 From 04bc88aca6157dfd1d2309b353d9feb64bcc8800 Mon Sep 17 00:00:00 2001 From: crupest Date: Mon, 7 Jun 2021 19:43:25 +0800 Subject: import(life): ... --- works/life/computer-network-experiment/Output.cpp | 9 ++++++--- works/life/computer-network-experiment/server.cpp | 1 - 2 files changed, 6 insertions(+), 4 deletions(-) (limited to 'works/life/computer-network-experiment/server.cpp') diff --git a/works/life/computer-network-experiment/Output.cpp b/works/life/computer-network-experiment/Output.cpp index e3dc69e..2989c98 100644 --- a/works/life/computer-network-experiment/Output.cpp +++ b/works/life/computer-network-experiment/Output.cpp @@ -28,13 +28,16 @@ void PrintOutput(const Output &output) { (*stream) << output.message; break; case OutputColor::Green: - (*stream) << CRUT("\x1b[32m") << output.message << CRUT("\x1b[0m"); + (*stream) << CRUT("\x1b[32m") << output.message << CRUT("\x1b[39m") + << std::flush; break; case OutputColor::Red: - (*stream) << CRUT("\x1b[31m") << output.message << CRUT("\x1b[0m"); + (*stream) << CRUT("\x1b[31m") << output.message << CRUT("\x1b[39m") + << std::flush; break; case OutputColor::Yellow: - (*stream) << CRUT("\x1b[33m") << output.message << CRUT("\x1b[0m"); + (*stream) << CRUT("\x1b[33m") << output.message << CRUT("\x1b[39m") + << std::flush; break; } } diff --git a/works/life/computer-network-experiment/server.cpp b/works/life/computer-network-experiment/server.cpp index f12630a..ac80125 100644 --- a/works/life/computer-network-experiment/server.cpp +++ b/works/life/computer-network-experiment/server.cpp @@ -15,7 +15,6 @@ #include #include #include - #endif const auto bind_address = "127.0.0.1"; // control bind address -- cgit v1.2.3 From c10e08c3896343cc7ddffe1dd7b1d09da2f8548e Mon Sep 17 00:00:00 2001 From: crupest Date: Mon, 7 Jun 2021 20:20:10 +0800 Subject: import(life): ... --- works/life/computer-network-experiment/Common.cpp | 70 +++++++++++++++++++++++ works/life/computer-network-experiment/Common.h | 5 ++ works/life/computer-network-experiment/Output.cpp | 11 ++++ works/life/computer-network-experiment/Output.h | 3 +- works/life/computer-network-experiment/client.cpp | 19 +++--- works/life/computer-network-experiment/server.cpp | 29 ++-------- 6 files changed, 100 insertions(+), 37 deletions(-) (limited to 'works/life/computer-network-experiment/server.cpp') diff --git a/works/life/computer-network-experiment/Common.cpp b/works/life/computer-network-experiment/Common.cpp index 5be3971..22f8b63 100644 --- a/works/life/computer-network-experiment/Common.cpp +++ b/works/life/computer-network-experiment/Common.cpp @@ -12,6 +12,8 @@ #include #endif +#include + [[noreturn]] void PrintErrorMessageAndExit(StringView message, bool print_last_error) { @@ -40,6 +42,7 @@ } #ifdef WIN32 +namespace { void InitWSA() { WSADATA wsa_data; @@ -47,6 +50,8 @@ void InitWSA() { PrintErrorMessageAndExit(CRUT("Failed to initialize wsa.")); } } +} // namespace + #endif int CloseSocket(int socket) { @@ -65,6 +70,71 @@ void BeforeExit() { SignalAndWaitForOutputThreadStop(); } +String ReadInputLine() { + String line; + std::getline(input_stream, line); + return line; +} + +void SafeSend(int socket, std::string_view buffer) { + const int total_byte_count = buffer.size(); + int byte_count_sent = 0; + int retry_count = 0; + + while (true) { + // Now we have sent all data. + if (byte_count_sent == total_byte_count) + break; + + auto byte_actually_sent = send(socket, buffer.data() + byte_count_sent, + buffer.size() - byte_count_sent, 0); + + // send failed + if (byte_actually_sent == -1) { + SendOutput(OutputType::Error, CRUT("Failed to send!\n")); + CloseSocket(socket); + break; + } + + byte_count_sent += byte_actually_sent; + } +} + +std::string SafeReadUntil(int socket, char c, std::string &rest) { + std::string result = rest; + + const int buffer_size = 100; + char *buffer = new char[buffer_size]; + + while (true) { + int received_number = recv(socket, buffer, buffer_size, 0); + + if (received_number == -1) { + PrintErrorMessageAndExit(CRUT("Failed to recv.")); + } + + bool b = false; + + for (int i = 0; i < received_number; i++) { + if (buffer[i] == '\n') { + result.append(buffer, i); + rest = std::string(buffer + i + 1, received_number - i - 1); + b = true; + break; + } + } + + if (b) + break; + + result.append(buffer, received_number); + } + + delete[] buffer; + + return result; +} + int main() { #ifdef WIN32 HANDLE h = GetStdHandle(STD_OUTPUT_HANDLE); diff --git a/works/life/computer-network-experiment/Common.h b/works/life/computer-network-experiment/Common.h index e5612fd..c3b6094 100644 --- a/works/life/computer-network-experiment/Common.h +++ b/works/life/computer-network-experiment/Common.h @@ -41,3 +41,8 @@ void InitWSA(); int CloseSocket(int socket); void BeforeExit(); + +String ReadInputLine(); + +void SafeSend(int socket, std::string_view buffer); +std::string SafeReadUntil(int socket, char c, std::string& rest); diff --git a/works/life/computer-network-experiment/Output.cpp b/works/life/computer-network-experiment/Output.cpp index 2989c98..db97e5e 100644 --- a/works/life/computer-network-experiment/Output.cpp +++ b/works/life/computer-network-experiment/Output.cpp @@ -2,9 +2,12 @@ #include +#include #include #include +std::mutex m; + folly::MPMCQueue output_queue(100); folly::CancellationSource cancellation_source; @@ -44,6 +47,8 @@ void PrintOutput(const Output &output) { void OutputThread() { while (true) { + m.lock(); + if (cancellation_source.getToken().isCancellationRequested()) { while (true) { Output output; @@ -58,6 +63,8 @@ void OutputThread() { Output output; if (output_queue.readIfNotEmpty(output)) PrintOutput(output); + + m.unlock(); } } @@ -65,3 +72,7 @@ void SignalAndWaitForOutputThreadStop() { cancellation_source.requestCancellation(); output_thread.join(); } + +std::lock_guard BlockOutputThread() { + return std::lock_guard(m); +} diff --git a/works/life/computer-network-experiment/Output.h b/works/life/computer-network-experiment/Output.h index 2d16eb0..689c3d3 100644 --- a/works/life/computer-network-experiment/Output.h +++ b/works/life/computer-network-experiment/Output.h @@ -8,6 +8,7 @@ #include #include +#include #include enum class OutputType { Normal, Error }; @@ -63,4 +64,4 @@ void OutputThread(); void SignalAndWaitForOutputThreadStop(); -extern std::thread output_thread; +std::lock_guard BlockOutputThread(); diff --git a/works/life/computer-network-experiment/client.cpp b/works/life/computer-network-experiment/client.cpp index 922ecdc..2494bae 100644 --- a/works/life/computer-network-experiment/client.cpp +++ b/works/life/computer-network-experiment/client.cpp @@ -37,19 +37,16 @@ int Main() { PrintErrorMessageAndExit(CRUT("Failed to connect!")); } - const int buffer_size = 100; - char *buffer = new char[buffer_size]; - - int received_number = recv(client_socket, buffer, buffer_size, 0); - - if (received_number == -1) { - PrintErrorMessageAndExit(CRUT("Failed to recv.")); + String name; + { + auto guard = BlockOutputThread(); + output_stream << CRUT("Please input your name:"); + name = ReadInputLine(); } - std::string s(buffer, received_number); - - SendOutput(OutputColor::Green, CRUT("Received message:\n")); - SendOutput(OutputColor::Normal, CRUT("{}\n"), ConvertCharString(s)); + String name_data = ConvertCharString(name); + SafeSend(client_socket, + std::string_view{name_data.data(), name_data.size() + 1}); CloseSocket(client_socket); return 0; diff --git a/works/life/computer-network-experiment/server.cpp b/works/life/computer-network-experiment/server.cpp index ac80125..2877c61 100644 --- a/works/life/computer-network-experiment/server.cpp +++ b/works/life/computer-network-experiment/server.cpp @@ -22,34 +22,13 @@ const u_short port = 1234; // control bind port void ResponseThreadProc(int socket, sockaddr_in address) { auto address_string = inet_ntoa(address.sin_addr); - SendOutput(CRUT("Connected to {}!\n"), ConvertCharString(address_string)); - const std::string_view buffer = "Love you!!! By crupest!"; + std::string rest; - const int total_byte_count = buffer.size(); - int byte_count_sent = 0; - int retry_count = 0; + std::string name = SafeReadUntil(socket, '\n', rest); - while (true) { - // Now we have sent all data. - if (byte_count_sent == total_byte_count) - break; - - auto byte_actually_sent = send(socket, buffer.data() + byte_count_sent, - buffer.size() - byte_count_sent, 0); - - // send failed - if (byte_actually_sent == -1) { - SendOutput(OutputType::Error, CRUT("Failed to send!\n")); - CloseSocket(socket); - break; - } - - byte_count_sent += byte_actually_sent; - } - - SendOutput(OutputColor::Green, CRUT("Succeeded to send message to {}!\n"), - ConvertCharString(address_string)); + SendOutput(CRUT("Connected to {}, whose name is {}."), + ConvertCharString(address_string), ConvertCharString(name)); CloseSocket(socket); } -- cgit v1.2.3 From 08325faff52d0704c6f17d065ca8d72ea07ca6a0 Mon Sep 17 00:00:00 2001 From: crupest Date: Mon, 7 Jun 2021 21:13:44 +0800 Subject: import(life): ... --- works/life/computer-network-experiment/server.cpp | 68 ++++++++++++++++++++--- 1 file changed, 59 insertions(+), 9 deletions(-) (limited to 'works/life/computer-network-experiment/server.cpp') diff --git a/works/life/computer-network-experiment/server.cpp b/works/life/computer-network-experiment/server.cpp index 2877c61..3c87ea0 100644 --- a/works/life/computer-network-experiment/server.cpp +++ b/works/life/computer-network-experiment/server.cpp @@ -4,8 +4,13 @@ #include "Common.h" #include "Output.h" +#include "fmt/core.h" + +#include +#include #include +#include #include #ifdef WIN32 @@ -20,20 +25,61 @@ const auto bind_address = "127.0.0.1"; // control bind address const u_short port = 1234; // control bind port -void ResponseThreadProc(int socket, sockaddr_in address) { - auto address_string = inet_ntoa(address.sin_addr); +struct Connection { + std::thread thread; + int socket; + sockaddr_in address; + String address_string; + String name; + folly::ProducerConsumerQueue send_queue{100}; + folly::CancellationSource cancellation_source; +}; + +void ResponseThreadProc(Connection *connection) { + auto host = ConvertCharString(inet_ntoa(connection->address.sin_addr)); + auto port = htons(connection->address.sin_port); + connection->address_string = fmt::format(CRUT("{}:{}"), host, port); std::string rest; - std::string name = SafeReadUntil(socket, '\n', rest); - + std::string n = SafeReadUntil(connection->socket, '\n', rest); + connection->name = ConvertCharString(n); SendOutput(CRUT("Connected to {}, whose name is {}."), - ConvertCharString(address_string), ConvertCharString(name)); + connection->address_string, connection->name); + + std::thread revieve_thread( + [](Connection *connection) { + std::string rest; + while (true) { + if (connection->cancellation_source.isCancellationRequested()) { + break; + } + + std::string s = SafeReadUntil(connection->socket, '\n', rest); - CloseSocket(socket); + SendOutput(CRUT("{}({}) send a message:\n{}\n"), connection->name, + connection->address_string, ConvertCharString(s)); + } + }, + connection); + + while (true) { + if (connection->cancellation_source.isCancellationRequested()) { + break; + } + + std::string s; + if (connection->send_queue.read(s)) { + SafeSend(connection->socket, s); + } + } + + CloseSocket(connection->socket); } int Main() { + std::vector connections; + int server_socket; if ((server_socket = socket(AF_INET, SOCK_STREAM, 0)) == -1) { @@ -74,8 +120,12 @@ int Main() { PrintErrorMessageAndExit(CRUT("Failed to accecpt.")); } - std::thread response_thread(ResponseThreadProc, client_socket, - client_address); - response_thread.detach(); + Connection connection; + connection.socket = client_socket; + connection.address = client_address; + connections.push_back(std::move(connection)); + + connection.thread = std::thread(ResponseThreadProc, &connections.back()); + connection.thread.detach(); } } -- cgit v1.2.3 From 9c42586bc406015be0145576fd6cb3586686b4ca Mon Sep 17 00:00:00 2001 From: crupest Date: Mon, 7 Jun 2021 23:24:43 +0800 Subject: import(life): ... --- .../computer-network-experiment/CMakeLists.txt | 2 +- works/life/computer-network-experiment/Common.cpp | 8 +-- works/life/computer-network-experiment/Common.h | 5 +- works/life/computer-network-experiment/IO.cpp | 80 ++++++++++++++++++++++ works/life/computer-network-experiment/IO.h | 66 ++++++++++++++++++ works/life/computer-network-experiment/Output.cpp | 76 -------------------- works/life/computer-network-experiment/Output.h | 67 ------------------ works/life/computer-network-experiment/client.cpp | 2 +- works/life/computer-network-experiment/server.cpp | 24 +++++-- 9 files changed, 172 insertions(+), 158 deletions(-) create mode 100644 works/life/computer-network-experiment/IO.cpp create mode 100644 works/life/computer-network-experiment/IO.h delete mode 100644 works/life/computer-network-experiment/Output.cpp delete mode 100644 works/life/computer-network-experiment/Output.h (limited to 'works/life/computer-network-experiment/server.cpp') diff --git a/works/life/computer-network-experiment/CMakeLists.txt b/works/life/computer-network-experiment/CMakeLists.txt index 4d4277c..09c1ea4 100644 --- a/works/life/computer-network-experiment/CMakeLists.txt +++ b/works/life/computer-network-experiment/CMakeLists.txt @@ -9,7 +9,7 @@ set(CMAKE_CXX_STANDARD 17) find_package(fmt CONFIG REQUIRED) find_package(Microsoft.GSL CONFIG REQUIRED) -add_library(base STATIC Common.cpp StringUtil.cpp Output.cpp) +add_library(base STATIC Common.cpp StringUtil.cpp IO.cpp) target_link_libraries(base PUBLIC Microsoft.GSL::GSL fmt::fmt Folly::folly) if(WIN32) target_link_libraries(base PUBLIC Ws2_32) diff --git a/works/life/computer-network-experiment/Common.cpp b/works/life/computer-network-experiment/Common.cpp index e4615fc..ec6fd1a 100644 --- a/works/life/computer-network-experiment/Common.cpp +++ b/works/life/computer-network-experiment/Common.cpp @@ -1,6 +1,6 @@ #include "Common.h" -#include "Output.h" +#include "IO.h" #ifdef WIN32 #include @@ -70,12 +70,6 @@ void BeforeExit() { SignalAndWaitForOutputThreadStop(); } -String ReadInputLine() { - String line; - std::getline(input_stream, line); - return line; -} - void SafeSend(int socket, std::string_view buffer) { const int total_byte_count = buffer.size(); int byte_count_sent = 0; diff --git a/works/life/computer-network-experiment/Common.h b/works/life/computer-network-experiment/Common.h index 6886e38..d2f2e52 100644 --- a/works/life/computer-network-experiment/Common.h +++ b/works/life/computer-network-experiment/Common.h @@ -2,6 +2,7 @@ #include "StringUtil.hpp" #include +#include #include #include @@ -9,6 +10,7 @@ using Char = wchar_t; using String = std::wstring; using StringView = std::wstring_view; +using StringStream = std::wstringstream; inline auto &input_stream = std::wcin; inline auto &output_stream = std::wcout; inline auto &error_stream = std::wcerr; @@ -25,6 +27,7 @@ inline std::string ConvertCharStringBack(StringView s) { using Char = char; using String = std::string; using StringView = std::string_view; +using StringStream = std::stringstream; inline auto &input_stream = std::cin; inline auto &output_stream = std::cout; inline auto &error_stream = std::cerr; @@ -43,7 +46,5 @@ int CloseSocket(int socket); void BeforeExit(); -String ReadInputLine(); - void SafeSend(int socket, std::string_view buffer); std::string SafeReadUntil(int socket, char c, std::string &rest); diff --git a/works/life/computer-network-experiment/IO.cpp b/works/life/computer-network-experiment/IO.cpp new file mode 100644 index 0000000..0c20537 --- /dev/null +++ b/works/life/computer-network-experiment/IO.cpp @@ -0,0 +1,80 @@ +#include "IO.h" + +#include + +#include +#include +#include +#include + +folly::MPMCQueue output_queue(100); + +folly::CancellationSource cancellation_source; + +std::thread io_thread; + +void PrintOutput(const Output &output) { + std::basic_ostream *stream; + + switch (output.type) { + case OutputType::Error: + stream = &error_stream; + break; + default: + stream = &output_stream; + break; + } + + switch (output.color) { + case OutputColor::Normal: + (*stream) << output.message; + break; + case OutputColor::Green: + (*stream) << CRUT("\x1b[32m") << output.message << CRUT("\x1b[39m") + << std::flush; + break; + case OutputColor::Red: + (*stream) << CRUT("\x1b[31m") << output.message << CRUT("\x1b[39m") + << std::flush; + break; + case OutputColor::Yellow: + (*stream) << CRUT("\x1b[33m") << output.message << CRUT("\x1b[39m") + << std::flush; + break; + } +} + +String ReadInputLine() { + String line; + std::getline(input_stream, line); + return line; +} + +void IOThread() { + while (true) { + if (cancellation_source.isCancellationRequested()) { + while (true) { + Output output; + if (output_queue.readIfNotEmpty(output)) { + PrintOutput(output); + } else { + return; + } + } + } + + Output output; + while (output_queue.readIfNotEmpty(output)) + PrintOutput(output); + + PrintOutput({CRUT("> ")}); + OnInputLine(ReadInputLine()); + } +} + +void SignalAndWaitForOutputThreadStop() { + cancellation_source.requestCancellation(); + io_thread.join(); +} + +void StartIOThread() { io_thread = std::thread(IOThread); } diff --git a/works/life/computer-network-experiment/IO.h b/works/life/computer-network-experiment/IO.h new file mode 100644 index 0000000..b0cf489 --- /dev/null +++ b/works/life/computer-network-experiment/IO.h @@ -0,0 +1,66 @@ +#pragma once +#include "Common.h" +#include "StringUtil.hpp" + +#include +#include +#include +#include + +#include +#include + +enum class OutputType { Normal, Error }; +enum class OutputColor { Normal, Green, Red, Yellow }; + +struct Output { + Output() = default; + Output(String message, OutputType type = OutputType::Normal) + : message(std::move(message)), type(type), + color(type == OutputType::Error ? OutputColor::Red + : OutputColor::Normal) {} + + Output(String message, OutputColor color) + : message(std::move(message)), type(OutputType::Normal), color(color) {} + + Output(String message, OutputType type, OutputColor color) + : message(std::move(message)), type(type), color(color) {} + + CRU_DEFAULT_COPY(Output) + CRU_DEFAULT_MOVE(Output) + ~Output() = default; + + String message; + OutputType type; + OutputColor color; +}; + +extern folly::MPMCQueue output_queue; + +inline void SendOutput(Output output) { + output_queue.blockingWrite(std::move(output)); +} + +inline void SendOutput(String output) { SendOutput(Output{std::move(output)}); } + +template void SendOutput(StringView format, Args &&...args) { + output_queue.blockingWrite(fmt::format(format, std::forward(args)...)); +} + +template +void SendOutput(OutputType type, StringView format, Args &&...args) { + output_queue.blockingWrite( + Output{fmt::format(format, std::forward(args)...), type}); +} + +template +void SendOutput(OutputColor color, StringView format, Args &&...args) { + output_queue.blockingWrite( + Output{fmt::format(format, std::forward(args)...), color}); +} + +void SignalAndWaitForOutputThreadStop(); + +void OnInputLine(StringView line); + +void StartIOThread(); diff --git a/works/life/computer-network-experiment/Output.cpp b/works/life/computer-network-experiment/Output.cpp deleted file mode 100644 index fbbd6ba..0000000 --- a/works/life/computer-network-experiment/Output.cpp +++ /dev/null @@ -1,76 +0,0 @@ -#include "Output.h" - -#include - -#include -#include -#include - -std::mutex m; - -folly::MPMCQueue output_queue(100); - -folly::CancellationSource cancellation_source; - -std::thread output_thread(OutputThread); - -void PrintOutput(const Output &output) { - std::basic_ostream *stream; - - switch (output.type) { - case OutputType::Error: - stream = &error_stream; - break; - default: - stream = &output_stream; - break; - } - - switch (output.color) { - case OutputColor::Normal: - (*stream) << output.message; - break; - case OutputColor::Green: - (*stream) << CRUT("\x1b[32m") << output.message << CRUT("\x1b[39m") - << std::flush; - break; - case OutputColor::Red: - (*stream) << CRUT("\x1b[31m") << output.message << CRUT("\x1b[39m") - << std::flush; - break; - case OutputColor::Yellow: - (*stream) << CRUT("\x1b[33m") << output.message << CRUT("\x1b[39m") - << std::flush; - break; - } -} - -void OutputThread() { - while (true) { - std::lock_guard guard(m); - - if (cancellation_source.getToken().isCancellationRequested()) { - while (true) { - Output output; - if (output_queue.readIfNotEmpty(output)) { - PrintOutput(output); - } else { - return; - } - } - } - - Output output; - if (output_queue.readIfNotEmpty(output)) - PrintOutput(output); - } -} - -void SignalAndWaitForOutputThreadStop() { - cancellation_source.requestCancellation(); - output_thread.join(); -} - -std::lock_guard BlockOutputThread() { - return std::lock_guard(m); -} diff --git a/works/life/computer-network-experiment/Output.h b/works/life/computer-network-experiment/Output.h deleted file mode 100644 index 689c3d3..0000000 --- a/works/life/computer-network-experiment/Output.h +++ /dev/null @@ -1,67 +0,0 @@ -#pragma once -#include "Common.h" -#include "StringUtil.hpp" - -#include -#include -#include -#include - -#include -#include -#include - -enum class OutputType { Normal, Error }; -enum class OutputColor { Normal, Green, Red, Yellow }; - -struct Output { - Output() = default; - Output(String message, OutputType type = OutputType::Normal) - : message(std::move(message)), type(type), - color(type == OutputType::Error ? OutputColor::Red - : OutputColor::Normal) {} - - Output(String message, OutputColor color) - : message(std::move(message)), type(OutputType::Normal), color(color) {} - - Output(String message, OutputType type, OutputColor color) - : message(std::move(message)), type(type), color(color) {} - - CRU_DEFAULT_COPY(Output) - CRU_DEFAULT_MOVE(Output) - ~Output() = default; - - String message; - OutputType type; - OutputColor color; -}; - -extern folly::MPMCQueue output_queue; - -inline void SendOutput(Output output) { - output_queue.blockingWrite(std::move(output)); -} - -inline void SendOutput(String output) { SendOutput(Output{std::move(output)}); } - -template void SendOutput(StringView format, Args &&...args) { - output_queue.blockingWrite(fmt::format(format, std::forward(args)...)); -} - -template -void SendOutput(OutputType type, StringView format, Args &&...args) { - output_queue.blockingWrite( - Output{fmt::format(format, std::forward(args)...), type}); -} - -template -void SendOutput(OutputColor color, StringView format, Args &&...args) { - output_queue.blockingWrite( - Output{fmt::format(format, std::forward(args)...), color}); -} - -void OutputThread(); - -void SignalAndWaitForOutputThreadStop(); - -std::lock_guard BlockOutputThread(); diff --git a/works/life/computer-network-experiment/client.cpp b/works/life/computer-network-experiment/client.cpp index c25a26b..a8ce8cf 100644 --- a/works/life/computer-network-experiment/client.cpp +++ b/works/life/computer-network-experiment/client.cpp @@ -3,7 +3,7 @@ */ #include "Common.h" -#include "Output.h" +#include "IO.h" #ifdef WIN32 #include diff --git a/works/life/computer-network-experiment/server.cpp b/works/life/computer-network-experiment/server.cpp index 3c87ea0..7008c7b 100644 --- a/works/life/computer-network-experiment/server.cpp +++ b/works/life/computer-network-experiment/server.cpp @@ -3,8 +3,7 @@ */ #include "Common.h" -#include "Output.h" -#include "fmt/core.h" +#include "IO.h" #include #include @@ -25,7 +24,15 @@ const auto bind_address = "127.0.0.1"; // control bind address const u_short port = 1234; // control bind port +void PrintHelp() { + SendOutput(CRUT( + "Input and run one of following command:\n\t> NOTHING -> Continue and " + "print new messages.\n\t> list -> List all connected client.\n\t> send " + "[i] [message] -> Send messages to client with number i.\n")); +} + struct Connection { + int id; std::thread thread; int socket; sockaddr_in address; @@ -35,6 +42,8 @@ struct Connection { folly::CancellationSource cancellation_source; }; +std::vector connections; + void ResponseThreadProc(Connection *connection) { auto host = ConvertCharString(inet_ntoa(connection->address.sin_addr)); auto port = htons(connection->address.sin_port); @@ -77,9 +86,11 @@ void ResponseThreadProc(Connection *connection) { CloseSocket(connection->socket); } -int Main() { - std::vector connections; +void OnInputLine(StringView line) { StringStream ss{String(line)}; + ss. + } +int Main() { int server_socket; if ((server_socket = socket(AF_INET, SOCK_STREAM, 0)) == -1) { @@ -105,6 +116,10 @@ int Main() { SendOutput(OutputColor::Green, CRUT("Now start to accept incoming connection.\n")); + StartIOThread(); + + int current_id = 1; + while (true) { sockaddr_in client_address; int client_socket; @@ -121,6 +136,7 @@ int Main() { } Connection connection; + connection.id = current_id++; connection.socket = client_socket; connection.address = client_address; connections.push_back(std::move(connection)); -- cgit v1.2.3 From 68cac85ea58c69301645167f92d94bdb6e360753 Mon Sep 17 00:00:00 2001 From: crupest Date: Tue, 8 Jun 2021 10:16:22 +0800 Subject: import(life): ... --- works/life/computer-network-experiment/IO.cpp | 3 +- works/life/computer-network-experiment/IO.h | 2 + works/life/computer-network-experiment/client.cpp | 59 ++++++++++++-- works/life/computer-network-experiment/server.cpp | 94 ++++++++++++++++++++--- 4 files changed, 138 insertions(+), 20 deletions(-) (limited to 'works/life/computer-network-experiment/server.cpp') diff --git a/works/life/computer-network-experiment/IO.cpp b/works/life/computer-network-experiment/IO.cpp index 0c20537..5d3fe12 100644 --- a/works/life/computer-network-experiment/IO.cpp +++ b/works/life/computer-network-experiment/IO.cpp @@ -9,9 +9,10 @@ folly::MPMCQueue output_queue(100); +namespace { folly::CancellationSource cancellation_source; - std::thread io_thread; +} void PrintOutput(const Output &output) { std::basic_ostream *stream; diff --git a/works/life/computer-network-experiment/IO.h b/works/life/computer-network-experiment/IO.h index b0cf489..1658b78 100644 --- a/works/life/computer-network-experiment/IO.h +++ b/works/life/computer-network-experiment/IO.h @@ -64,3 +64,5 @@ void SignalAndWaitForOutputThreadStop(); void OnInputLine(StringView line); void StartIOThread(); + +String ReadInputLine(); \ No newline at end of file diff --git a/works/life/computer-network-experiment/client.cpp b/works/life/computer-network-experiment/client.cpp index a8ce8cf..c206856 100644 --- a/works/life/computer-network-experiment/client.cpp +++ b/works/life/computer-network-experiment/client.cpp @@ -5,6 +5,9 @@ #include "Common.h" #include "IO.h" +#include +#include + #ifdef WIN32 #include #include @@ -12,12 +15,29 @@ #include #include #include - #endif const auto connect_address = "127.0.0.1"; // control connect address const u_short port = 1234; // control connect port +namespace { +folly::ProducerConsumerQueue send_queue(100); +folly::CancellationSource cancellation_source; +} // namespace + +void PrintHelp() { + SendOutput(CRUT("Input anything to send to server. Or just enter to receive " + "lastest messages from server.\n")); +} + +void OnInputLine(StringView line) { + if (line.empty()) { + return; + } else { + send_queue.write(ConvertCharStringBack(line) + '\n'); + } +} + int Main() { int client_socket; @@ -37,17 +57,42 @@ int Main() { PrintErrorMessageAndExit(CRUT("Failed to connect!")); } - String name; - { - auto guard = BlockOutputThread(); - output_stream << CRUT("Please input your name:"); - name = ReadInputLine(); - } + output_stream << CRUT("Please input your name:\n> "); + String name = ReadInputLine(); + + PrintHelp(); + + StartIOThread(); name.push_back(CRUT('\n')); auto name_data = ConvertCharStringBack(name); SafeSend(client_socket, name_data); + std::thread receive_thread([client_socket] { + std::string rest; + while (true) { + if (cancellation_source.isCancellationRequested()) { + break; + } + + std::string s = SafeReadUntil(client_socket, '\n', rest); + + SendOutput(CRUT("Recived a message:\n{}\n"), ConvertCharString(s)); + } + }); + receive_thread.detach(); + + while (true) { + if (cancellation_source.isCancellationRequested()) { + break; + } + + std::string s; + if (send_queue.read(s)) { + SafeSend(client_socket, s); + } + } + CloseSocket(client_socket); return 0; } diff --git a/works/life/computer-network-experiment/server.cpp b/works/life/computer-network-experiment/server.cpp index 7008c7b..9654687 100644 --- a/works/life/computer-network-experiment/server.cpp +++ b/works/life/computer-network-experiment/server.cpp @@ -8,6 +8,8 @@ #include #include +#include +#include #include #include #include @@ -42,7 +44,20 @@ struct Connection { folly::CancellationSource cancellation_source; }; -std::vector connections; +std::vector> connections; + +void PrintConnections() { + if (connections.empty()) { + SendOutput(CRUT("Currently there is no connection.\n")); + } + + String s; + for (const auto &connection : connections) { + s += fmt::format(CRUT("{}: {}({})\n"), connection->id, connection->name, + connection->address_string); + } + SendOutput(s); +} void ResponseThreadProc(Connection *connection) { auto host = ConvertCharString(inet_ntoa(connection->address.sin_addr)); @@ -53,7 +68,7 @@ void ResponseThreadProc(Connection *connection) { std::string n = SafeReadUntil(connection->socket, '\n', rest); connection->name = ConvertCharString(n); - SendOutput(CRUT("Connected to {}, whose name is {}."), + SendOutput(OutputColor::Green, CRUT("Connected to {}, whose name is {}.\n"), connection->address_string, connection->name); std::thread revieve_thread( @@ -71,6 +86,7 @@ void ResponseThreadProc(Connection *connection) { } }, connection); + revieve_thread.detach(); while (true) { if (connection->cancellation_source.isCancellationRequested()) { @@ -86,9 +102,60 @@ void ResponseThreadProc(Connection *connection) { CloseSocket(connection->socket); } -void OnInputLine(StringView line) { StringStream ss{String(line)}; - ss. - } +void OnInputLine(StringView line) { + StringStream ss{String(line)}; + + ss >> std::ws; + if (ss.eof()) + return; + + String command; + ss >> command; + + if (command == CRUT("list")) { + if (!ss.eof()) { + SendOutput(OutputType::Error, + CRUT("List command can't have arguments!\n")); + PrintHelp(); + } else { + PrintConnections(); + } + return; + } else if (command == CRUT("send")) { + int id; + ss >> id; + if (!ss) { + SendOutput(OutputType::Error, CRUT("Send format error!\n")); + PrintHelp(); + return; + } + + String message; + getline(ss, message); + + if (message.empty()) { + SendOutput(OutputType::Error, CRUT("Send message can't be empty.!\n")); + PrintHelp(); + return; + } + + auto i = std::find_if( + connections.begin(), connections.end(), + [id](const std::unique_ptr &c) { return c->id == id; }); + + if (i == connections.end()) { + SendOutput(OutputType::Error, CRUT("No connection with such id.!\n")); + return; + } + + (*i)->send_queue.write(ConvertCharStringBack(message) + "\n"); + return; + } else { + SendOutput(OutputType::Error, CRUT("Unkown command!\n")); + PrintHelp(); + return; + } +} int Main() { int server_socket; @@ -116,6 +183,8 @@ int Main() { SendOutput(OutputColor::Green, CRUT("Now start to accept incoming connection.\n")); + PrintHelp(); + StartIOThread(); int current_id = 1; @@ -135,13 +204,14 @@ int Main() { PrintErrorMessageAndExit(CRUT("Failed to accecpt.")); } - Connection connection; - connection.id = current_id++; - connection.socket = client_socket; - connection.address = client_address; - connections.push_back(std::move(connection)); + connections.push_back(std::make_unique()); + const std::unique_ptr &connection = connections.back(); - connection.thread = std::thread(ResponseThreadProc, &connections.back()); - connection.thread.detach(); + connection->id = current_id++; + connection->socket = client_socket; + connection->address = client_address; + connection->thread = + std::thread(ResponseThreadProc, connections.back().get()); + connection->thread.detach(); } } -- cgit v1.2.3 From f7d73f1bf08aaca98e789a6d6c56d672cc0c4c9f Mon Sep 17 00:00:00 2001 From: crupest Date: Tue, 8 Jun 2021 18:25:36 +0800 Subject: import(life): ... --- works/life/computer-network-experiment/Common.cpp | 33 ++++++++++------------ works/life/computer-network-experiment/Common.h | 6 ++-- works/life/computer-network-experiment/client.cpp | 11 ++++++-- works/life/computer-network-experiment/server.cpp | 34 ++++++++++++++++++----- 4 files changed, 53 insertions(+), 31 deletions(-) (limited to 'works/life/computer-network-experiment/server.cpp') diff --git a/works/life/computer-network-experiment/Common.cpp b/works/life/computer-network-experiment/Common.cpp index ec6fd1a..1df4d56 100644 --- a/works/life/computer-network-experiment/Common.cpp +++ b/works/life/computer-network-experiment/Common.cpp @@ -1,6 +1,7 @@ #include "Common.h" #include "IO.h" +#include #ifdef WIN32 #include @@ -70,7 +71,7 @@ void BeforeExit() { SignalAndWaitForOutputThreadStop(); } -void SafeSend(int socket, std::string_view buffer) { +bool SafeSend(int socket, std::string_view buffer) { const int total_byte_count = buffer.size(); int byte_count_sent = 0; int retry_count = 0; @@ -78,55 +79,49 @@ void SafeSend(int socket, std::string_view buffer) { while (true) { // Now we have sent all data. if (byte_count_sent == total_byte_count) - break; + return true; auto byte_actually_sent = send(socket, buffer.data() + byte_count_sent, buffer.size() - byte_count_sent, 0); // send failed if (byte_actually_sent == -1) { - SendOutput(OutputType::Error, CRUT("Failed to send!\n")); - CloseSocket(socket); - break; + return false; } byte_count_sent += byte_actually_sent; } } -std::string SafeReadUntil(int socket, char c, std::string &rest) { - std::string result = rest; +bool SafeReadUntil(int socket, char c, std::string &data, std::string &rest) { + data = rest; const int buffer_size = 100; - char *buffer = new char[buffer_size]; + char buffer[buffer_size]; while (true) { int received_number = recv(socket, buffer, buffer_size, 0); if (received_number == -1) { - PrintErrorMessageAndExit(CRUT("Failed to recv.")); + return false; } - bool b = false; + bool end = false; for (int i = 0; i < received_number; i++) { if (buffer[i] == c) { - result.append(buffer, i); + data.append(buffer, i); rest = std::string(buffer + i + 1, received_number - i - 1); - b = true; + end = true; break; } } - if (b) - break; + if (end) + return true; - result.append(buffer, received_number); + data.append(buffer, received_number); } - - delete[] buffer; - - return result; } int main() { diff --git a/works/life/computer-network-experiment/Common.h b/works/life/computer-network-experiment/Common.h index a4472c7..1e6c277 100644 --- a/works/life/computer-network-experiment/Common.h +++ b/works/life/computer-network-experiment/Common.h @@ -48,5 +48,7 @@ int CloseSocket(int socket); void BeforeExit(); -void SafeSend(int socket, std::string_view buffer); -std::string SafeReadUntil(int socket, char c, std::string &rest); +// Return false for error. +bool SafeSend(int socket, std::string_view buffer); +// Return false for error. +bool SafeReadUntil(int socket, char c, std::string &data, std::string &rest); diff --git a/works/life/computer-network-experiment/client.cpp b/works/life/computer-network-experiment/client.cpp index c206856..73ae52f 100644 --- a/works/life/computer-network-experiment/client.cpp +++ b/works/life/computer-network-experiment/client.cpp @@ -75,9 +75,12 @@ int Main() { break; } - std::string s = SafeReadUntil(client_socket, '\n', rest); + std::string data; + if (!SafeReadUntil(client_socket, '\n', data, rest)) { + PrintErrorMessageAndExit(CRUT("Failed to receive message.\n")); + } - SendOutput(CRUT("Recived a message:\n{}\n"), ConvertCharString(s)); + SendOutput(CRUT("Recived a message:\n{}\n"), ConvertCharString(data)); } }); receive_thread.detach(); @@ -89,7 +92,9 @@ int Main() { std::string s; if (send_queue.read(s)) { - SafeSend(client_socket, s); + if (!SafeSend(client_socket, s)) { + PrintErrorMessageAndExit(CRUT("Failed to send message to server.")); + } } } diff --git a/works/life/computer-network-experiment/server.cpp b/works/life/computer-network-experiment/server.cpp index 9654687..03d27ad 100644 --- a/works/life/computer-network-experiment/server.cpp +++ b/works/life/computer-network-experiment/server.cpp @@ -36,6 +36,7 @@ void PrintHelp() { struct Connection { int id; std::thread thread; + std::thread receive_thread; int socket; sockaddr_in address; String address_string; @@ -65,13 +66,19 @@ void ResponseThreadProc(Connection *connection) { connection->address_string = fmt::format(CRUT("{}:{}"), host, port); std::string rest; + std::string name_data; + if (!SafeReadUntil(connection->socket, '\n', name_data, rest)) { + SendOutput(OutputType::Error, CRUT("Failed to read name of {}.\n"), + connection->address_string); + CloseSocket(connection->socket); + return; + } - std::string n = SafeReadUntil(connection->socket, '\n', rest); - connection->name = ConvertCharString(n); + connection->name = ConvertCharString(name_data); SendOutput(OutputColor::Green, CRUT("Connected to {}, whose name is {}.\n"), connection->address_string, connection->name); - std::thread revieve_thread( + connection->receive_thread = std::thread( [](Connection *connection) { std::string rest; while (true) { @@ -79,14 +86,22 @@ void ResponseThreadProc(Connection *connection) { break; } - std::string s = SafeReadUntil(connection->socket, '\n', rest); + std::string data; + + if (!SafeReadUntil(connection->socket, '\n', data, rest)) { + SendOutput(OutputType::Error, + CRUT("Failed read data from socket of {}({}).\n"), + connection->name, connection->address_string); + connection->cancellation_source.requestCancellation(); + return; + } SendOutput(CRUT("{}({}) send a message:\n{}\n"), connection->name, - connection->address_string, ConvertCharString(s)); + connection->address_string, ConvertCharString(data)); } }, connection); - revieve_thread.detach(); + connection->receive_thread.detach(); while (true) { if (connection->cancellation_source.isCancellationRequested()) { @@ -95,7 +110,12 @@ void ResponseThreadProc(Connection *connection) { std::string s; if (connection->send_queue.read(s)) { - SafeSend(connection->socket, s); + if (!SafeSend(connection->socket, s)) { + SendOutput(OutputType::Error, CRUT("Failed send data to {}({}).\n"), + connection->name, connection->address_string); + connection->cancellation_source.requestCancellation(); + break; + } } } -- cgit v1.2.3 From 33ece81cf15b3cb8d4a005194acc67c3a3e5ad35 Mon Sep 17 00:00:00 2001 From: crupest Date: Tue, 8 Jun 2021 20:50:30 +0800 Subject: import(life): ... --- .../computer-network-experiment/ReadWriteLock.cpp | 1 - works/life/computer-network-experiment/TODO.md | 6 +++-- works/life/computer-network-experiment/server.cpp | 26 +++++++++++++++++++++- 3 files changed, 29 insertions(+), 4 deletions(-) (limited to 'works/life/computer-network-experiment/server.cpp') diff --git a/works/life/computer-network-experiment/ReadWriteLock.cpp b/works/life/computer-network-experiment/ReadWriteLock.cpp index 3509e0b..46d2857 100644 --- a/works/life/computer-network-experiment/ReadWriteLock.cpp +++ b/works/life/computer-network-experiment/ReadWriteLock.cpp @@ -2,7 +2,6 @@ #include #include -#include namespace cru { ReadWriteLock::ReadWriteLock() { diff --git a/works/life/computer-network-experiment/TODO.md b/works/life/computer-network-experiment/TODO.md index 77bf0fc..248a2d7 100644 --- a/works/life/computer-network-experiment/TODO.md +++ b/works/life/computer-network-experiment/TODO.md @@ -1,4 +1,6 @@ -1. Apply read-write lock to connections. -2. Remove dead connection from connection list. +1. Apply read-write lock to connections. ✅ +2. Remove dead connection from connection list. ✅ 3. Handle SIGINT gracefully. 4. Add close method to protocol. +5. Add help and exit command. +6. Use multiprocess to show output. diff --git a/works/life/computer-network-experiment/server.cpp b/works/life/computer-network-experiment/server.cpp index 03d27ad..065687c 100644 --- a/works/life/computer-network-experiment/server.cpp +++ b/works/life/computer-network-experiment/server.cpp @@ -4,6 +4,7 @@ #include "Common.h" #include "IO.h" +#include "ReadWriteLock.h" #include #include @@ -26,12 +27,14 @@ const auto bind_address = "127.0.0.1"; // control bind address const u_short port = 1234; // control bind port +namespace { void PrintHelp() { SendOutput(CRUT( "Input and run one of following command:\n\t> NOTHING -> Continue and " "print new messages.\n\t> list -> List all connected client.\n\t> send " "[i] [message] -> Send messages to client with number i.\n")); } +} // namespace struct Connection { int id; @@ -45,9 +48,24 @@ struct Connection { folly::CancellationSource cancellation_source; }; +namespace { +cru::ReadWriteLock connections_lock; std::vector> connections; +void RemoveConnection(int id) { + connections_lock.WriteLock(); + connections.erase( + std::remove_if(connections.begin(), connections.end(), + [id](const std::unique_ptr &connection) { + return connection->id == id; + }), + connections.end()); + + connections_lock.WriteUnlock(); +} + void PrintConnections() { + connections_lock.ReadLock(); if (connections.empty()) { SendOutput(CRUT("Currently there is no connection.\n")); } @@ -58,7 +76,9 @@ void PrintConnections() { connection->address_string); } SendOutput(s); + connections_lock.ReadUnlock(); } +} // namespace void ResponseThreadProc(Connection *connection) { auto host = ConvertCharString(inet_ntoa(connection->address.sin_addr)); @@ -120,6 +140,8 @@ void ResponseThreadProc(Connection *connection) { } CloseSocket(connection->socket); + + RemoveConnection(connection->id); } void OnInputLine(StringView line) { @@ -164,7 +186,7 @@ void OnInputLine(StringView line) { [id](const std::unique_ptr &c) { return c->id == id; }); if (i == connections.end()) { - SendOutput(OutputType::Error, CRUT("No connection with such id.!\n")); + SendOutput(OutputType::Error, CRUT("No connection with such id.\n")); return; } @@ -224,6 +246,7 @@ int Main() { PrintErrorMessageAndExit(CRUT("Failed to accecpt.")); } + connections_lock.WriteLock(); connections.push_back(std::make_unique()); const std::unique_ptr &connection = connections.back(); @@ -233,5 +256,6 @@ int Main() { connection->thread = std::thread(ResponseThreadProc, connections.back().get()); connection->thread.detach(); + connections_lock.WriteUnlock(); } } -- cgit v1.2.3