-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: implement chatroom and docker file and readme.md
- Loading branch information
0 parents
commit 205dc4d
Showing
5 changed files
with
289 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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"] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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"] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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> | ||
|
||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} |