Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion .github/workflows/cpp-build-test-coverage.yml
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,6 @@ jobs:

# -------------------------------------------------------
# Step 4: Run unit tests
# TODO: need to update, I think we should not hard-code the executable name here
# -------------------------------------------------------
- name: Run tests
run: |
Expand Down
21 changes: 11 additions & 10 deletions run.sh
Original file line number Diff line number Diff line change
Expand Up @@ -9,19 +9,19 @@ set -e # Exit immediately if a command fails
PROJECT_EXEC="./build/bin/cpp_lab_project"
BUILD_DIR="./build"

if [ ! -d "build" ]; then
echo "Build directory not found. Creating build directory..."
# if [ ! -d "build" ]; then
# echo "Build directory not found. Creating build directory..."

rm -rf build
mkdir build
cd build || exit
# rm -rf build
# mkdir build
# cd build || exit

cmake ..
cd ..
# cmake ..
# cd ..

else
echo "Build directory already exists."
fi
# else
# echo "Build directory already exists."
# fi

clear
echo "=============================="
Expand All @@ -38,6 +38,7 @@ done

echo ""
echo "===========>> Building project..."
cmake -G "Unix Makefiles" -B "$BUILD_DIR"
cmake --build "$BUILD_DIR"

echo ""
Expand Down
2 changes: 2 additions & 0 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ add_executable(${PROJECT_NAME}
main.cpp
${CORE_SOURCES}
${SOCKET_SOURCES}
${CONTROLLER_SOURCES}
${DS_SOURCES}
)

# Add header include paths
Expand Down
4 changes: 2 additions & 2 deletions src/controller/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
set(CONTROLLER_SOURCES

# PID
pid/pid.cpp
pid/PIDSim.cpp
${CMAKE_CURRENT_SOURCE_DIR}/pid/pid.cpp
${CMAKE_CURRENT_SOURCE_DIR}/pid/PIDSim.cpp
)

set(CONTROLLER_SOURCES ${CONTROLLER_SOURCES} PARENT_SCOPE)
44 changes: 22 additions & 22 deletions src/patterns/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,32 +1,32 @@
set(DS_SOURCES

# Structural Patterns
structural/Adapter.cpp
structural/Bridge.cpp
structural/Proxy.cpp
structural/Composite.cpp
structural/Flyweight.cpp
structural/Facade.cpp
structural/Decorator.cpp
${CMAKE_CURRENT_SOURCE_DIR}/structural/Adapter.cpp
${CMAKE_CURRENT_SOURCE_DIR}/structural/Bridge.cpp
${CMAKE_CURRENT_SOURCE_DIR}/structural/Proxy.cpp
${CMAKE_CURRENT_SOURCE_DIR}/structural/Composite.cpp
${CMAKE_CURRENT_SOURCE_DIR}/structural/Flyweight.cpp
${CMAKE_CURRENT_SOURCE_DIR}/structural/Facade.cpp
${CMAKE_CURRENT_SOURCE_DIR}/structural/Decorator.cpp

# Behavioral Patterns
behavioral/ChainOfCommand.cpp
behavioral/Command.cpp
behavioral/Iterator.cpp
behavioral/Mediator.cpp
behavioral/Memento.cpp
behavioral/Visitor.cpp
behavioral/TemplateMethod.cpp
behavioral/Strategy.cpp
behavioral/State.cpp
behavioral/Observer.cpp
${CMAKE_CURRENT_SOURCE_DIR}/behavioral/ChainOfCommand.cpp
${CMAKE_CURRENT_SOURCE_DIR}/behavioral/Command.cpp
${CMAKE_CURRENT_SOURCE_DIR}/behavioral/Iterator.cpp
${CMAKE_CURRENT_SOURCE_DIR}/behavioral/Mediator.cpp
${CMAKE_CURRENT_SOURCE_DIR}/behavioral/Memento.cpp
${CMAKE_CURRENT_SOURCE_DIR}/behavioral/Visitor.cpp
${CMAKE_CURRENT_SOURCE_DIR}/behavioral/TemplateMethod.cpp
${CMAKE_CURRENT_SOURCE_DIR}/behavioral/Strategy.cpp
${CMAKE_CURRENT_SOURCE_DIR}/behavioral/State.cpp
${CMAKE_CURRENT_SOURCE_DIR}/behavioral/Observer.cpp

# Creational Patterns
creational/Singleton.cpp
creational/FactoryMethod.cpp
creational/AbstractFactory.cpp
creational/Builder.cpp
creational/Prototype.cpp
${CMAKE_CURRENT_SOURCE_DIR}/creational/Singleton.cpp
${CMAKE_CURRENT_SOURCE_DIR}/creational/FactoryMethod.cpp
${CMAKE_CURRENT_SOURCE_DIR}/creational/AbstractFactory.cpp
${CMAKE_CURRENT_SOURCE_DIR}/creational/Builder.cpp
${CMAKE_CURRENT_SOURCE_DIR}/creational/Prototype.cpp
)

set(DS_SOURCES ${DS_SOURCES} PARENT_SCOPE)
2 changes: 2 additions & 0 deletions src/socket/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ set(SOCKET_SOURCES
# Socket
${CMAKE_CURRENT_SOURCE_DIR}/simple_tcp/TCPServer.cpp
${CMAKE_CURRENT_SOURCE_DIR}/simple_tcp/SimpleTCPServer.cpp
${CMAKE_CURRENT_SOURCE_DIR}/simple_tcp/TCPClient.cpp
${CMAKE_CURRENT_SOURCE_DIR}/simple_tcp/SimpleTCPClient.cpp
)

set(SOCKET_SOURCES ${SOCKET_SOURCES} PARENT_SCOPE)
53 changes: 53 additions & 0 deletions src/socket/simple_tcp/SimpleTCPClient.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
#include <iostream>
#include <limits>
#include "ExampleRegistry.h"
#include "TCPClient.h"

namespace {

void run() {
try {
TCPClient client("127.0.0.1", 8080);

if (!client.connect()) {
std::cout << "connect failed\n";
return;
}
std::cout << "connected to " << "host: " << client.getHost()
<< " port:" << client.getPort() << "\n";
std::string response = client.receive();
std::cout << response << std::endl;
std::string msg;
while (true) {
if (!std::getline(std::cin, msg)) {
std::cout << "get line failed" << std::endl;
break;
};

// TODO: why we need \n here
client.send(msg + "\n");
response = client.receive();
std::cout << response;
}

client.close();
} catch (const std::exception& e) {
// Connection closed by foreign host
std::cout << "error: " << e.what() << std::endl;
}
}

} // namespace

class SimpleTCPClient : public IExample {
public:
std::string group() const override { return "socket/tcp"; }

std::string name() const override { return "SimpleTCPClient"; }

std::string description() const override { return "Simple TCP Client"; }

void execute() override { run(); }
};

REGISTER_EXAMPLE(SimpleTCPClient, "socket/tcp", "SimpleTCPClient");
85 changes: 85 additions & 0 deletions src/socket/simple_tcp/TCPClient.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
#include "TCPClient.h"

#include <sys/socket.h>
#include <stdexcept>

#include <arpa/inet.h>
#include <netinet/in.h>
#include <unistd.h>

TCPClient::TCPClient(const std::string& host, uint16_t port)
: host_{host}, port_{port}, client_fd_{-1} {}

bool TCPClient::connect() {
client_fd_ = socket(AF_INET, SOCK_STREAM, 0);
if (client_fd_ < 0) {
throw std::runtime_error("socket failed.");
}

// specifying the address
sockaddr_in serverAddress{};
serverAddress.sin_family = AF_INET;
serverAddress.sin_port = htons(port_);

// serverAddress.sin_addr.s_addr = INADDR_ANY;
if (::inet_pton(AF_INET, host_.c_str(), &serverAddress.sin_addr) <= 0) {
throw std::runtime_error("invalid address");
}

// sending connection request
return (
::connect(client_fd_,
reinterpret_cast<sockaddr*>(&serverAddress), // global syscall
sizeof(serverAddress)) == 0);
}

void TCPClient::send(const std::string& msg) {
if (client_fd_ < 0) {
throw std::runtime_error("socket not connected");
}

const char* data = msg.c_str();
size_t total = 0;
size_t len = msg.size();
while (total < len) {
ssize_t sent = ::send(client_fd_, data + total, len - total, 0);
if (sent <= 0) {
throw std::runtime_error("send failed");
}
total += sent;
}
}

std::string TCPClient::receive() {
if (client_fd_ < 0) {
throw std::runtime_error("socket not connected");
}

char buffer[1024];

ssize_t bytes = recv(client_fd_, buffer, sizeof(buffer), 0);
if (bytes < 0) {
throw std::runtime_error("recv failed");
}

if (bytes == 0) {
throw std::runtime_error("connection closed");
}

return std::string(buffer, bytes);
}

void TCPClient::close() {
if (client_fd_ >= 0) {
::close(client_fd_);
client_fd_ = -1;
}
}

const std::string& TCPClient::getHost() const {
return host_;
}

uint16_t TCPClient::getPort() const {
return port_;
}
20 changes: 20 additions & 0 deletions src/socket/simple_tcp/TCPClient.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#pragma once
#include <stdint.h>
#include <string>

class TCPClient {
public:
TCPClient(const std::string& host, uint16_t port);

bool connect();
void close();
void send(const std::string& msg);
std::string receive();

const std::string& getHost() const;
uint16_t getPort() const;
private:
std::string host_;
uint16_t port_;
int client_fd_;
};
56 changes: 36 additions & 20 deletions src/socket/simple_tcp/TCPServer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -88,47 +88,63 @@ void TCPServer::acceptLoop() {
handleClient(client_fd);
} catch (const std::exception& e) {
std::cerr << "[TCPServer] client error: " << e.what() << "\n";
close(client_fd);
}
}
}

// close connection after the client session end
close(client_fd);
void TCPServer::sendAll(int fd, const char* data, size_t len) {
size_t total = 0;
while (total < len) {
ssize_t sent = send(fd, data + total, len - total, 0);
if (sent <= 0) {
throw std::runtime_error("send failed");
}
total += sent;
}
}

void TCPServer::handleClient(int client_fd) {
char buffer[1024];

// send a notify to the client
std::string welcome =
"Connected to the TCP Server on port" + std::to_string(port_) + "\n";
if (send(client_fd, welcome.c_str(), welcome.size(), 0) < 0) {
throw std::runtime_error("send failed");
}
std::string welcome = "Connected to the TCP Server on port " +
std::to_string(port_) +
"\nType 'Q' to stop the connection.\n";

while (true) {
char buffer[1024];
std::string prompt =
"Type 'Q' to stop the connection. Enter your messages: ";
if (send(client_fd, prompt.c_str(), prompt.size(), 0) < 0) {
throw std::runtime_error("send failed");
}
sendAll(client_fd, welcome.c_str(), welcome.size());

while (true) {
// receive data from the client
int bytes = recv(client_fd, buffer, sizeof(buffer) - 1, 0);
if (bytes <= 0) {
// bytes == 0 : client closed connection
// bytes < 0 : error

if (bytes == 0) { // : client closed connection
std::cout << "[TCPServer] client closed connection\n";
break;
}

if (bytes < 0) { // : error
throw std::runtime_error("recv failed");
}

buffer[bytes] = '\0';

std::cout << "[TCPServer] Received: " << buffer << "\n";
// print out
std::cout << "client: " << buffer;

sendAll(client_fd, buffer, bytes);

// stop connection if client sends 'Q'
if (buffer[0] == 'Q') {
std::cout << "[TCPServer] Client disconnected\n";
std::string line(buffer, bytes);
line.erase(line.find_last_not_of("\r\n") + 1); // remove \r\n
if (line == "Q") {
std::cout << "[TCPServer] client disconnected\n";
break;
}
}

// close connection after the client session end
close(client_fd);
}

void TCPServer::start() noexcept(false) {
Expand Down
2 changes: 2 additions & 0 deletions src/socket/simple_tcp/TCPServer.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ class TCPServer {
*/
void handleClient(int client_fd);

static void sendAll(int fd, const char* data, size_t len);

private:
uint16_t port_; // TCP ports are in 0 - 65535
int server_fd_{-1}; // socket descriptor
Expand Down