From 285c6490f388c5485756763b6e03100cf59026b3 Mon Sep 17 00:00:00 2001 From: Lars Ivar Hatledal Date: Thu, 2 May 2024 13:19:14 +0200 Subject: [PATCH] add readExact --- .clang-format | 66 ++++++++++++++++++++ include/Socket.hpp | 40 +++++++----- src/Socket.cpp | 103 ++++++++++++++++++------------- tests/integration/run_server.cpp | 4 +- tests/test_socketlib.cpp | 47 ++++++++++++-- 5 files changed, 194 insertions(+), 66 deletions(-) create mode 100644 .clang-format diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..2860af1 --- /dev/null +++ b/.clang-format @@ -0,0 +1,66 @@ +# Generated from CLion C/C++ Code Style settings +BasedOnStyle: LLVM +AccessModifierOffset: -4 +AlignAfterOpenBracket: Align +AlignConsecutiveAssignments: None +AlignOperands: Align +AllowAllArgumentsOnNextLine: false +AllowAllConstructorInitializersOnNextLine: false +AllowAllParametersOfDeclarationOnNextLine: false +AllowShortBlocksOnASingleLine: Always +AllowShortCaseLabelsOnASingleLine: false +AllowShortFunctionsOnASingleLine: All +AllowShortIfStatementsOnASingleLine: Always +AllowShortLambdasOnASingleLine: All +AllowShortLoopsOnASingleLine: true +AlwaysBreakAfterReturnType: None +AlwaysBreakTemplateDeclarations: Yes +BreakBeforeBraces: Custom +BraceWrapping: + AfterCaseLabel: false + AfterClass: false + AfterControlStatement: Never + AfterEnum: false + AfterFunction: false + AfterNamespace: false + AfterUnion: false + BeforeCatch: false + BeforeElse: false + IndentBraces: false + SplitEmptyFunction: false + SplitEmptyRecord: true +BreakBeforeBinaryOperators: None +BreakBeforeTernaryOperators: true +BreakConstructorInitializers: BeforeColon +BreakInheritanceList: BeforeColon +ColumnLimit: 0 +CompactNamespaces: false +ContinuationIndentWidth: 8 +IndentCaseLabels: true +IndentPPDirectives: None +IndentWidth: 4 +KeepEmptyLinesAtTheStartOfBlocks: true +MaxEmptyLinesToKeep: 2 +NamespaceIndentation: All +ObjCSpaceAfterProperty: false +ObjCSpaceBeforeProtocolList: true +PointerAlignment: Left +ReflowComments: false +SpaceAfterCStyleCast: true +SpaceAfterLogicalNot: false +SpaceAfterTemplateKeyword: false +SpaceBeforeAssignmentOperators: true +SpaceBeforeCpp11BracedList: false +SpaceBeforeCtorInitializerColon: false +SpaceBeforeInheritanceColon: false +SpaceBeforeParens: ControlStatements +SpaceBeforeRangeBasedForLoopColon: true +SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: 0 +SpacesInAngles: false +SpacesInCStyleCastParentheses: false +SpacesInContainerLiterals: false +SpacesInParentheses: false +SpacesInSquareBrackets: false +TabWidth: 4 +UseTab: Never diff --git a/include/Socket.hpp b/include/Socket.hpp index 8085e7c..5ba3d83 100644 --- a/include/Socket.hpp +++ b/include/Socket.hpp @@ -10,28 +10,38 @@ class Connection { public: - virtual bool read(std::vector &buffer, size_t *bytesRead = nullptr) = 0; + virtual bool read(std::vector& buffer, + size_t* bytesRead = nullptr) = 0; - virtual bool read(unsigned char *buffer, size_t size, size_t *bytesRead = nullptr) = 0; + virtual bool read(unsigned char* buffer, size_t size, + size_t* bytesRead = nullptr) = 0; - virtual bool write(const std::string &message) = 0; + virtual bool readExact(std::vector& buffer) = 0; - virtual bool write(const std::vector &data) = 0; + virtual bool readExact(unsigned char* buffer, size_t size) = 0; + + virtual bool write(const std::string& message) = 0; + + virtual bool write(const std::vector& data) = 0; virtual ~Connection() = default; }; -class Socket : public Connection { +class Socket: public Connection { public: Socket(); - bool read(std::vector &buffer, size_t *bytesRead) override; + bool read(std::vector& buffer, size_t* bytesRead) override; + + bool read(unsigned char* buffer, size_t size, size_t* bytesRead) override; - bool read(unsigned char *buffer, size_t size, size_t *bytesRead) override; + bool readExact(std::vector& buffer) override; - bool write(const std::string &message) override; + bool readExact(unsigned char* buffer, size_t size) override; - bool write(const std::vector &data) override; + bool write(const std::string& message) override; + + bool write(const std::vector& data) override; void close(); @@ -44,20 +54,19 @@ class Socket : public Connection { std::unique_ptr accept(); - bool connect(const std::string &ip, int port); + bool connect(const std::string& ip, int port); private: struct Impl; std::unique_ptr pimpl_; }; - -class ClientSocket : public Socket { +class ClientSocket: public Socket { public: - bool connect(const std::string &ip, int port); + bool connect(const std::string& ip, int port); }; -class ServerSocket : public Socket { +class ServerSocket: public Socket { public: void bind(int port); @@ -66,5 +75,4 @@ class ServerSocket : public Socket { std::unique_ptr accept(); }; - -#endif//SPHEROSIM_SOCKET_HPP +#endif// SPHEROSIM_SOCKET_HPP diff --git a/src/Socket.cpp b/src/Socket.cpp index 2553a41..808011c 100644 --- a/src/Socket.cpp +++ b/src/Socket.cpp @@ -26,7 +26,7 @@ struct Socket::Impl { sockfd = socket(AF_INET, SOCK_STREAM, 0); } - bool connect(const std::string &ip, int port) { + bool connect(const std::string& ip, int port) { sockaddr_in serv_addr{}; serv_addr.sin_family = AF_INET; serv_addr.sin_port = htons(port); @@ -34,7 +34,8 @@ struct Socket::Impl { return false; } - if (::connect(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) { + if (::connect(sockfd, (struct sockaddr*) &serv_addr, sizeof(serv_addr)) < + 0) { return false; } @@ -49,7 +50,7 @@ struct Socket::Impl { serv_addr.sin_addr.s_addr = INADDR_ANY; serv_addr.sin_port = htons(port); - if (::bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) { + if (::bind(sockfd, (struct sockaddr*) &serv_addr, sizeof(serv_addr)) < 0) { std::cerr << "Bind failed" << std::endl; exit(1); } @@ -66,7 +67,8 @@ struct Socket::Impl { sockaddr_in client_addr{}; socklen_t addrlen = sizeof(client_addr); - SOCKET new_sock = ::accept(sockfd, (struct sockaddr *) &client_addr, &addrlen); + SOCKET new_sock = + ::accept(sockfd, (struct sockaddr*) &client_addr, &addrlen); #ifdef _WIN32 if (new_sock == INVALID_SOCKET) { @@ -84,20 +86,44 @@ struct Socket::Impl { return conn; } - bool read(unsigned char *buffer, size_t size, size_t *bytesRead) { + bool read(unsigned char* buffer, size_t size, size_t* bytesRead) { #ifdef _WIN32 - auto read = recv(sockfd, reinterpret_cast(buffer), size, 0); - if (bytesRead) *bytesRead = read; + auto read = recv(sockfd, reinterpret_cast(buffer), size, 0); + if (bytesRead) + *bytesRead = read; return read != SOCKET_ERROR && read != 0; #else - auto read = ::read(sockfd, reinterpret_cast(buffer), size); - if (bytesRead) *bytesRead = read; + auto read = ::read(sockfd, reinterpret_cast(buffer), size); + if (bytesRead) + *bytesRead = read; return read != -1; #endif } - bool write(const std::string &buffer) { + bool readExact(unsigned char* buffer, size_t size) { + + int totalBytesReceived = 0; + while (totalBytesReceived < size) { +#ifdef _WIN32 + auto read = recv(sockfd, reinterpret_cast(buffer), size, 0); + if (read == SOCKET_ERROR || read == 0) { + return false; + } + totalBytesReceived += read; +#else + auto read = ::read(sockfd, reinterpret_cast(buffer), size); + if (read == -1 || read == 0) { + return false; + } + totalBytesReceived += read; +#endif + } + + return true; + } + + bool write(const std::string& buffer) { #ifdef _WIN32 return send(sockfd, buffer.data(), buffer.size(), 0) != SOCKET_ERROR; @@ -106,10 +132,11 @@ struct Socket::Impl { #endif } - bool write(const std::vector &buffer) { + bool write(const std::vector& buffer) { #ifdef _WIN32 - return send(sockfd, reinterpret_cast(buffer.data()), buffer.size(), 0) != SOCKET_ERROR; + return send(sockfd, reinterpret_cast(buffer.data()), + buffer.size(), 0) != SOCKET_ERROR; #else return ::write(sockfd, buffer.data(), buffer.size()) != -1; #endif @@ -148,69 +175,57 @@ struct Socket::Impl { bool closed{false}; }; -Socket::Socket() : pimpl_(std::make_unique()) {} +Socket::Socket(): pimpl_(std::make_unique()) {} - -bool Socket::read(std::vector &buffer, size_t *bytesRead) { +bool Socket::read(std::vector& buffer, size_t* bytesRead) { return pimpl_->read(buffer.data(), buffer.size(), bytesRead); } -bool Socket::read(unsigned char *buffer, size_t size, size_t *bytesRead) { +bool Socket::read(unsigned char* buffer, size_t size, size_t* bytesRead) { return pimpl_->read(buffer, size, bytesRead); } -bool Socket::write(const std::string &buffer) { +bool Socket::readExact(std::vector& buffer) { - return pimpl_->write(buffer); + return pimpl_->readExact(buffer.data(), buffer.size()); } -bool Socket::write(const std::vector &buffer) { +bool Socket::readExact(unsigned char* buffer, size_t size) { - return pimpl_->write(buffer); + return pimpl_->readExact(buffer, size); } -bool Socket::connect(const std::string &ip, int port) { - - return pimpl_->connect(ip, port); -} +bool Socket::write(const std::string& buffer) { return pimpl_->write(buffer); } -void Socket::bind(int port) { +bool Socket::write(const std::vector& buffer) { - pimpl_->bind(port); + return pimpl_->write(buffer); } -void Socket::listen(int backlog) { +bool Socket::connect(const std::string& ip, int port) { - pimpl_->listen(backlog); + return pimpl_->connect(ip, port); } -std::unique_ptr Socket::accept() { +void Socket::bind(int port) { pimpl_->bind(port); } - return pimpl_->accept(); -} +void Socket::listen(int backlog) { pimpl_->listen(backlog); } -void Socket::close() { +std::unique_ptr Socket::accept() { return pimpl_->accept(); } - pimpl_->close(); -} +void Socket::close() { pimpl_->close(); } Socket::~Socket() = default; +bool ClientSocket::connect(const std::string& ip, int port) { -bool ClientSocket::connect(const std::string &ip, int port) { return Socket::connect(ip, port); } -void ServerSocket::bind(int port) { - Socket::bind(port); -} +void ServerSocket::bind(int port) { Socket::bind(port); } -void ServerSocket::listen(int backlog) { - Socket::listen(backlog); -} +void ServerSocket::listen(int backlog) { Socket::listen(backlog); } -std::unique_ptr ServerSocket::accept() { - return Socket::accept(); -} +std::unique_ptr ServerSocket::accept() { return Socket::accept(); } diff --git a/tests/integration/run_server.cpp b/tests/integration/run_server.cpp index 6c3a1d5..8dd7392 100644 --- a/tests/integration/run_server.cpp +++ b/tests/integration/run_server.cpp @@ -1,9 +1,9 @@ #include "Socket.hpp" +#include #include #include #include -#include void socketHandler(std::unique_ptr conn) { size_t n; @@ -27,7 +27,7 @@ int main() { std::unique_ptr conn; try { conn = server.accept(); - } catch (const std::exception &e) { + } catch (const std::exception& e) { continue; } diff --git a/tests/test_socketlib.cpp b/tests/test_socketlib.cpp index 0c26438..b8d9beb 100644 --- a/tests/test_socketlib.cpp +++ b/tests/test_socketlib.cpp @@ -1,6 +1,7 @@ #include "Socket.hpp" +#include #include #include @@ -13,7 +14,7 @@ namespace { return "Per"; } - std::string generateResponse(const std::string &msg) { + std::string generateResponse(const std::string& msg) { return "Hello " + msg + "!"; } @@ -34,7 +35,7 @@ namespace { } }// namespace -TEST_CASE("socketlib") { +TEST_CASE("socketlib read") { ServerSocket server; ClientSocket client; @@ -48,19 +49,21 @@ TEST_CASE("socketlib") { socketHandler(std::move(conn)); }); + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + std::thread clientThread([&client] { REQUIRE(client.connect("127.0.0.1", 8080)); std::string message = "Per"; + std::string expectedResponse = generateResponse(message); + client.write(message); size_t bytesRead = 0; std::vector buffer(1024); REQUIRE(client.read(buffer, &bytesRead)); - std::string expectedResponse = generateResponse(message); REQUIRE(bytesRead == expectedResponse.size()); - std::string response(buffer.begin(), buffer.begin() + static_cast(bytesRead)); CHECK(response == expectedResponse); @@ -70,3 +73,39 @@ TEST_CASE("socketlib") { server.close(); serverThread.join(); } + +TEST_CASE("socketlib readexact") { + + ServerSocket server; + ClientSocket client; + + std::thread serverThread([&server] { + server.bind(8080); + server.listen(); + + std::unique_ptr conn; + REQUIRE_NOTHROW(conn = server.accept()); + socketHandler(std::move(conn)); + }); + + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + + std::thread clientThread([&client] { + REQUIRE(client.connect("127.0.0.1", 8080)); + + std::string message = "Per"; + std::string expectedResponse = generateResponse(message); + client.write(message); + + std::vector buffer(expectedResponse.size()); + REQUIRE(client.readExact(buffer)); + + std::string response(buffer.begin(), buffer.end()); + + CHECK(response == expectedResponse); + }); + + clientThread.join(); + server.close(); + serverThread.join(); +}