Skip to content

Commit

Permalink
feat: implement chatroom and docker file and readme.md
Browse files Browse the repository at this point in the history
  • Loading branch information
bilet-13 committed Aug 5, 2024
0 parents commit 205dc4d
Show file tree
Hide file tree
Showing 5 changed files with 289 additions and 0 deletions.
17 changes: 17 additions & 0 deletions build/dockerfile.client
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Use an official GCC image as a base
FROM gcc:latest

# Set the working directory in the container
WORKDIR /app

# Copy the source directory contents into the container at /app
COPY src/ .

# Compile the client code
RUN g++ -std=c++11 -o client client.cpp -pthread

# Set the entrypoint to the client executable
ENTRYPOINT ["./client"]

# Default argument to the client executable (can be overridden)
CMD ["default_username"]
14 changes: 14 additions & 0 deletions build/dockerfile.server
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Use an official GCC image as a base
FROM gcc:latest

# Set the working directory in the container
WORKDIR /app

# Copy the source directory contents into the container at /app
COPY src/ .

# Compile the server code
RUN g++ -std=c++11 -o server server.cpp -pthread

# Run the server
CMD ["./server"]
46 changes: 46 additions & 0 deletions readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# Simple Chatroom

This is a simple terminal-based chatroom application implemented in C++. It allows multiple users to connect to a server and chat with each other in real-time.

## Features

- Real-time messaging between multiple clients
- Each client shows messages from all users, including their own messages
- Notifications when users join or leave the chatroom

## Prerequisites

- Docker
- C++11 or higher
- A C++ compiler (e.g., `g++`)

## Getting Started

Follow these instructions to set up and run the chatroom application using Docker.

### Building Docker Images and Run

1. **Clone the repository:**

```bash
git clone <repository-url>
cd <repository-directory>
```
2. **Build the Docker image for the server and clients:**

```bash
docker build -f build/Dockerfile.server -t chatroom-server .
docker build -f build/Dockerfile.client -t chatroom-client .

```

3. **Start the server:**
```bash
docker run --name chatroom-server -d chatroom-server
```

4. **Open a new terminal window for each client and run:**
```bash
docker run --rm -it chatroom-client ./client <username>

```
89 changes: 89 additions & 0 deletions src/client.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
#include <iostream>
#include <string>
#include <thread>
#include <netinet/in.h>
#include <unistd.h>
#include <arpa/inet.h>

class Client {
public:
Client(const std::string& server_ip, int port, const std::string& username);
void start();

private:
void receive_messages();
void send_messages();

std::string server_ip;
int port;
std::string username;
int client_socket;
};

Client::Client(const std::string& server_ip, int port, const std::string& username)
: server_ip(server_ip), port(port), username(username) {
client_socket = socket(AF_INET, SOCK_STREAM, 0);
if (client_socket == -1) {
throw std::runtime_error("Cannot create socket");
}

sockaddr_in hint;
hint.sin_family = AF_INET;
hint.sin_port = htons(port);
inet_pton(AF_INET, server_ip.c_str(), &hint.sin_addr);

if (connect(client_socket, (sockaddr*)&hint, sizeof(hint)) == -1) {
throw std::runtime_error("Cannot connect to server");
}

// Send username to server
send(client_socket, username.c_str(), username.size() + 1, 0);
}

void Client::start() {
std::thread(&Client::receive_messages, this).detach();
send_messages();
}

void Client::receive_messages() {
char buffer[4096];
while (true) {
memset(buffer, 0, 4096);
int bytes_received = recv(client_socket, buffer, 4096, 0);
if (bytes_received <= 0) {
std::cout << "Server disconnected\n";
close(client_socket);
break;
}
std::string msg(buffer, 0, bytes_received);
std::cout << msg << std::endl;
}
}

void Client::send_messages() {
std::string user_input;
while (true) {
std::getline(std::cin, user_input);
if (user_input == "quit") break;
std::string message = username + ": " + user_input;
send(client_socket, message.c_str(), message.size() + 1, 0);
}
}

int main(int argc, char *argv[]) {
if (argc != 2) {
std::cerr << "Usage: ./client <username>\n";
return -1;
}

std::string username = argv[1];

try {
Client client("127.0.0.1", 54001, username); // Use the same port as the server
client.start();
} catch (const std::exception& ex) {
std::cerr << "Error: " << ex.what() << std::endl;
return -1;
}
return 0;
}
123 changes: 123 additions & 0 deletions src/server.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
#include <iostream>
#include <vector>
#include <thread>
#include <mutex>
#include <netinet/in.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <unordered_map>

class Server {
public:
Server(int port);
void start();

private:
void accept_clients();
void handle_client(int client_socket);
void broadcast_message(const std::string& message);

int port;
int server_socket;
std::unordered_map<int, std::string> clients; // Map client socket to username
std::mutex clients_mutex;
};

Server::Server(int port) : port(port) {
server_socket = socket(AF_INET, SOCK_STREAM, 0);
if (server_socket == -1) {
throw std::runtime_error("Cannot create socket");
}

sockaddr_in hint;
hint.sin_family = AF_INET;
hint.sin_port = htons(port);
hint.sin_addr.s_addr = INADDR_ANY;

if (bind(server_socket, (sockaddr*)&hint, sizeof(hint)) == -1) {
close(server_socket);
throw std::runtime_error("Cannot bind to IP/Port");
}

if (listen(server_socket, SOMAXCONN) == -1) {
close(server_socket);
throw std::runtime_error("Cannot listen");
}
}

void Server::start() {
std::thread(&Server::accept_clients, this).detach();
std::cout << "Server started on port " << port << std::endl;
while (true);
}

void Server::accept_clients() {
while (true) {
sockaddr_in client;
socklen_t client_size = sizeof(client);
int client_socket = accept(server_socket, (sockaddr*)&client, &client_size);
if (client_socket == -1) {
std::cerr << "Problem with client connecting\n";
continue;
}

// Receive username from client
char username_buffer[4096];
memset(username_buffer, 0, 4096);
recv(client_socket, username_buffer, 4096, 0);
std::string username(username_buffer);

{
std::lock_guard<std::mutex> lock(clients_mutex);
clients[client_socket] = username;
}

// Announce the new client
std::string join_message = username + " joined the chatroom";
broadcast_message(join_message);

std::thread(&Server::handle_client, this, client_socket).detach();
}
}

void Server::handle_client(int client_socket) {
char buffer[4096];
while (true) {
memset(buffer, 0, 4096);
int bytes_received = recv(client_socket, buffer, 4096, 0);
if (bytes_received <= 0) {
close(client_socket);
{
std::lock_guard<std::mutex> lock(clients_mutex);
std::string username = clients[client_socket];
clients.erase(client_socket);

// Announce the client has left
std::string leave_message = username + " left the chatroom";
broadcast_message(leave_message);
}
break;
}

std::string msg(buffer, 0, bytes_received);
broadcast_message(msg);
}
}

void Server::broadcast_message(const std::string& message) {
std::lock_guard<std::mutex> lock(clients_mutex);
for (const auto& client : clients) {
send(client.first, message.c_str(), message.size() + 1, 0);
}
}

int main() {
try {
Server server(54001); // Use port 54001 or another port that is available
server.start();
} catch (const std::exception& ex) {
std::cerr << "Error: " << ex.what() << std::endl;
return -1;
}
return 0;
}

0 comments on commit 205dc4d

Please sign in to comment.