Initial Commit
This commit is contained in:
parent
cd11cc4fbd
commit
ccc891f51a
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
/cmake-build-debug/
|
||||
/.idea/
|
||||
10
CMakeLists.txt
Normal file
10
CMakeLists.txt
Normal file
@ -0,0 +1,10 @@
|
||||
cmake_minimum_required(VERSION 3.26)
|
||||
project(Chat)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
set(CMAKE_EXE_LINKER_FLAGS -static)
|
||||
|
||||
link_libraries(ws2_32)
|
||||
|
||||
add_executable(Server server.cpp)
|
||||
add_executable(Client client.cpp)
|
||||
44
README.md
44
README.md
@ -1,2 +1,46 @@
|
||||
# DCN_Chat_Program
|
||||
[UIC Data Communication Workshop 24S Group Assignment2]
|
||||
|
||||
---
|
||||
|
||||
## Project Description
|
||||
Our purpose is to create a chat room that allows local area users to freely communicate with each other one-on-one as well as group chat while ensuring individual privacy.
|
||||
|
||||
We also give administrators the ability to manage users and all chatting groups to ensure a friendly chatting environment.
|
||||
|
||||
And if users do not know how to operate, we will also have a prompt to guide them through.
|
||||
|
||||
---
|
||||
|
||||
## Project Structure
|
||||
- **Server-Side:**
|
||||
1. **Accept** client connections & **Receive** messages.
|
||||
2. For each message, use regular expressions to **determine its message type & response** to sender.
|
||||
- **Client-Side:**
|
||||
1. **Send & Receive** Messages.
|
||||
2. **Announce available Username**
|
||||
3. **Client-Side Hints:** #help & #quit.
|
||||
|
||||
---
|
||||
|
||||
## Features
|
||||
1. **Public/private chatting:** Direct Message, Chatroom Message
|
||||
2. **Group Chatting:** create, add by passcode, delete by admin, delete user by admin
|
||||
3. **Server-side Commands:** delete client, shutdown Server
|
||||
4. **Client-side Hints:** Help & Get online client names
|
||||
5. **Retry Mechanism:** client username declaration, client connection
|
||||
6. **Only display messages posted by clients**
|
||||
|
||||
---
|
||||
|
||||
## Usage
|
||||
> Public / Private Chatting
|
||||
1. Public: `<msg>`
|
||||
2. Private: `@username <msg>`
|
||||
>Group Chatting: Unordered List (Hash)
|
||||
1. Create: `Group @[<usernames>] Group name, password`
|
||||
2. Add: `Group_add @Group name, password`
|
||||
3. Chat: `@[Group name] <msg>`
|
||||
4. Group Admin Only:
|
||||
1. Delete: `Group_del @Group name, password`
|
||||
2. Del People: `Group_delp @Group name, username, password`
|
||||
|
||||
221
client.cpp
Normal file
221
client.cpp
Normal file
@ -0,0 +1,221 @@
|
||||
#include <winsock2.h>
|
||||
#include <cstdio>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <iostream>
|
||||
|
||||
#define DEFAULT_PORT 5019
|
||||
#define DEFAULT_IP "127.0.0.1"
|
||||
#define BUFFER_SIZE 256
|
||||
#define MAX_CONNECT_ATTEMPT 10
|
||||
|
||||
void send_msg(SOCKET sock);
|
||||
void recv_msg(SOCKET sock);
|
||||
|
||||
std::string name, msg;
|
||||
|
||||
int main(){
|
||||
int attempts = 0;
|
||||
struct hostent* hp;
|
||||
struct sockaddr_in server_addr = {0};
|
||||
|
||||
SOCKET client_sock;
|
||||
WSADATA wsaData;
|
||||
|
||||
const char* server_name = DEFAULT_IP;
|
||||
unsigned short port = DEFAULT_PORT;
|
||||
|
||||
// Handle WSAStartup
|
||||
if (WSAStartup(0x202, &wsaData) == SOCKET_ERROR){
|
||||
fprintf(stderr, "WSAStartup failed with error %d\n", WSAGetLastError());
|
||||
WSACleanup();
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Initialize an IPV4 address & port for the client
|
||||
server_addr.sin_family = AF_INET;
|
||||
server_addr.sin_port = htons(port);
|
||||
|
||||
// Resolves host's IP address
|
||||
if (isalpha(server_name[0])){
|
||||
hp = gethostbyname(server_name);
|
||||
if (hp == nullptr){
|
||||
fprintf(stderr, "Cannot resolve address: %d\n", WSAGetLastError());
|
||||
WSACleanup();
|
||||
return -1;
|
||||
}
|
||||
memcpy(&(server_addr.sin_addr), hp->h_addr, hp->h_length);
|
||||
}
|
||||
else{
|
||||
server_addr.sin_addr.s_addr = inet_addr(server_name);
|
||||
}
|
||||
|
||||
// Initialize a TCP socket for client
|
||||
client_sock = socket(AF_INET, SOCK_STREAM, 0);
|
||||
if (client_sock == INVALID_SOCKET){
|
||||
fprintf(stderr, "socket() failed with error %d\n", WSAGetLastError());
|
||||
WSACleanup();
|
||||
return -1;
|
||||
}
|
||||
|
||||
printf("Client connecting to: %s\n", hp->h_name);
|
||||
|
||||
// Connection retry strategy
|
||||
while (attempts < MAX_CONNECT_ATTEMPT){
|
||||
if (connect(client_sock, (struct sockaddr*)&server_addr, sizeof(server_addr)) == SOCKET_ERROR) {
|
||||
fprintf(stderr, "connect() failed with error %d, attempt %d\n", WSAGetLastError(), attempts + 1);
|
||||
Sleep(1000);
|
||||
attempts++;
|
||||
}
|
||||
else{
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (attempts >= MAX_CONNECT_ATTEMPT) {
|
||||
fprintf(stderr, "Failed to connect after %d attempts\n", attempts);
|
||||
closesocket(client_sock);
|
||||
WSACleanup();
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Announce username to server
|
||||
char szBuff[BUFFER_SIZE] = {0};
|
||||
while(true){
|
||||
printf("Pick a username: ");
|
||||
std::getline(std::cin, name);
|
||||
if (name.length() > 32 || name.length() < 3){
|
||||
printf("Username shall have 3-32 characters. Please choose another one.\n");
|
||||
continue;
|
||||
}
|
||||
std::string my_name = "#New Client:" + name;
|
||||
send(client_sock, my_name.c_str(), my_name.length() + 1, 0);
|
||||
int msg_len = recv(client_sock, szBuff, sizeof(szBuff)-1, 0);
|
||||
printf("%s\n", szBuff);
|
||||
if(msg_len == my_name.length() + 1){
|
||||
break;
|
||||
}
|
||||
else{
|
||||
name.clear();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
printf("### Chat commands:\n");
|
||||
printf("Chatroom Message: msg\n");
|
||||
printf("Direct Message: @username msg\n");
|
||||
printf("Group Message: @[Group name] msg\n");
|
||||
printf("### Group commands:\n");
|
||||
printf("Create Group: Group @[user1 user2...] Group name, password\n");
|
||||
printf("Add into a Group: Group_add @Group name, password\n");
|
||||
printf("Delete a Group: Group_del @Group name, password\n");
|
||||
printf("Delete a Group Member: Group_delp @Group name, username, password\n");
|
||||
printf("### Hints:\n");
|
||||
printf("#clientList - Show the current online client list\n");
|
||||
printf("#help - Show this help message\n");
|
||||
printf("#quit - Quit the chat\n");
|
||||
|
||||
// Send & Recv messages
|
||||
std::thread snd(send_msg, client_sock);
|
||||
std::thread rcv(recv_msg, client_sock);
|
||||
|
||||
snd.join();
|
||||
rcv.join();
|
||||
|
||||
shutdown(client_sock, SD_SEND);
|
||||
closesocket(client_sock);
|
||||
WSACleanup();
|
||||
return 0;
|
||||
}
|
||||
|
||||
void send_msg(SOCKET sock){
|
||||
int msg_len;
|
||||
char szBuff[BUFFER_SIZE] = {0};
|
||||
while(true){
|
||||
// Get user input
|
||||
fgets(szBuff, sizeof(szBuff), stdin);
|
||||
szBuff[strcspn(szBuff, "\n")] = '\0';
|
||||
|
||||
if((int)strlen(szBuff) == 0){
|
||||
printf("You cannot send empty message!\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
// Handle commands
|
||||
if (std::string(szBuff) == "#help"){
|
||||
printf("### Chat commands:\n");
|
||||
printf("Chatroom Message: msg\n");
|
||||
printf("Direct Message: @username msg\n");
|
||||
printf("Group Message: @[Group name] msg\n");
|
||||
printf("### Group commands:\n");
|
||||
printf("Create Group: Group @[user1 user2...] Group name, password\n");
|
||||
printf("Add into a Group: Group_add @Group name, password\n");
|
||||
printf("Delete a Group: Group_del @Group name, password\n");
|
||||
printf("Delete a Group Member: Group_delp @Group name, username, password\n");
|
||||
printf("### Hints:\n");
|
||||
printf("#clientList - Show the current online client list\n");
|
||||
printf("#help - Show this help message\n");
|
||||
printf("#quit - Quit the chat\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
// Client List
|
||||
if (std::string(szBuff) == "#clientList"){
|
||||
msg = "[" + name + "] " + "#applyforclientList";
|
||||
}
|
||||
|
||||
// Quit
|
||||
else if (std::string(szBuff) == "#quit"){
|
||||
closesocket(sock);
|
||||
WSACleanup();
|
||||
exit(0);
|
||||
}
|
||||
|
||||
else{
|
||||
msg = "[" + name + "] " + szBuff;
|
||||
}
|
||||
|
||||
// Send input to server
|
||||
msg_len = send(sock, msg.c_str(), msg.length() + 1, 0);
|
||||
if (msg_len == SOCKET_ERROR){
|
||||
fprintf(stderr, "send() failed with error %d\n", WSAGetLastError());
|
||||
break;
|
||||
}
|
||||
|
||||
// Check if server closed connection
|
||||
if (msg_len == 0){
|
||||
closesocket(sock);
|
||||
printf("server closed connection\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void recv_msg(SOCKET sock){
|
||||
int msg_len;
|
||||
char szBuff[BUFFER_SIZE + name.length() + 1];
|
||||
|
||||
while(true){
|
||||
// Get respond from server
|
||||
msg_len = recv(sock, szBuff, sizeof(szBuff)-1, 0);
|
||||
if (msg_len == SOCKET_ERROR){
|
||||
fprintf(stderr, "recv() failed with error %d\nProgram will be closed in 3s.\n", WSAGetLastError());
|
||||
closesocket(sock);
|
||||
Sleep(3000);
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
// Check if server closed connection
|
||||
if (msg_len == 0){
|
||||
printf("server closed connection\nProgram will be closed in 3s.\n");
|
||||
closesocket(sock);
|
||||
Sleep(3000);
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
// Display other user's messages
|
||||
szBuff[msg_len] = '\0';
|
||||
if(strcmp(szBuff, msg.c_str()) != 0){
|
||||
printf("%s\n", szBuff);
|
||||
}
|
||||
}
|
||||
}
|
||||
477
server.cpp
Normal file
477
server.cpp
Normal file
@ -0,0 +1,477 @@
|
||||
#include <cstdio>
|
||||
#include <winsock2.h>
|
||||
#include <ws2tcpip.h>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <unordered_set>
|
||||
#include <unordered_map>
|
||||
#include <iostream>
|
||||
#include <thread>
|
||||
#include <sstream>
|
||||
#include <regex>
|
||||
|
||||
#define SERVER_PORT 5019
|
||||
#define BUFFER_SIZE 256
|
||||
#define MAX_CLIENT 128
|
||||
|
||||
void handle_conn(SOCKET sock);
|
||||
void handle_msg(const std::string &msg, SOCKET sender);
|
||||
void command_listener();
|
||||
void send_clients_list(SOCKET sock);
|
||||
|
||||
int client_count = 0;
|
||||
bool server_status = true;
|
||||
std::mutex mtx;
|
||||
std::unordered_map<std::string, int> msg_socks;
|
||||
struct Group {
|
||||
std::unordered_set<std::string> members;
|
||||
std::string password;
|
||||
std::string admin;
|
||||
};
|
||||
std::unordered_map<std::string, Group> groups;
|
||||
|
||||
int main(){
|
||||
WSADATA wsaData;
|
||||
SOCKET server_sock, msg_sock;
|
||||
struct sockaddr_in server_addr{}, client_addr{};
|
||||
|
||||
// WSAStartup
|
||||
if (WSAStartup(0x202, &wsaData) == SOCKET_ERROR){
|
||||
fprintf(stderr, "WSAStartup failed with error %d\n", WSAGetLastError());
|
||||
WSACleanup();
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Initialize the address structure for IPV4 listening
|
||||
server_addr.sin_family = AF_INET;
|
||||
server_addr.sin_addr.s_addr = INADDR_ANY;
|
||||
server_addr.sin_port = htons(SERVER_PORT);
|
||||
|
||||
// Create a TCP socket
|
||||
server_sock = socket(AF_INET, SOCK_STREAM, 0);
|
||||
if (server_sock == INVALID_SOCKET){
|
||||
fprintf(stderr, "socket() failed with error %d\n", WSAGetLastError());
|
||||
WSACleanup();
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Bind server socket to server_addr
|
||||
if (bind(server_sock, (struct sockaddr*)&server_addr, sizeof(server_addr)) == SOCKET_ERROR){
|
||||
fprintf(stderr, "bind() failed with error %d\n", WSAGetLastError());
|
||||
closesocket(server_sock);
|
||||
WSACleanup();
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Listen incoming connections
|
||||
if (listen(server_sock, MAX_CLIENT) == SOCKET_ERROR){
|
||||
fprintf(stderr, "listen() failed with error %d\n", WSAGetLastError());
|
||||
closesocket(server_sock);
|
||||
WSACleanup();
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Start the command listener thread
|
||||
std::thread command_thread(command_listener);
|
||||
command_thread.detach();
|
||||
|
||||
// Waiting for connections
|
||||
printf("Waiting for connections ........\n");
|
||||
|
||||
// Accept connections
|
||||
while (server_status){
|
||||
if(client_count < MAX_CLIENT){
|
||||
// Get client address
|
||||
int client_addr_len = sizeof(client_addr);
|
||||
msg_sock = accept(server_sock, (struct sockaddr*)&client_addr, &client_addr_len);
|
||||
if (msg_sock == INVALID_SOCKET) {
|
||||
fprintf(stderr, "accept() failed with error %d\n", WSAGetLastError());
|
||||
continue;
|
||||
}
|
||||
|
||||
// Successfully connected to client notice
|
||||
char addr_buffer[INET_ADDRSTRLEN];
|
||||
printf("accepted connection from %s, port %d\n",
|
||||
inet_ntop(AF_INET, &(client_addr.sin_addr), addr_buffer, INET_ADDRSTRLEN),
|
||||
htons(client_addr.sin_port));
|
||||
|
||||
// Accepting the connection in a new thread
|
||||
mtx.lock();
|
||||
std::thread th(handle_conn, msg_sock);
|
||||
th.detach();
|
||||
client_count++;
|
||||
mtx.unlock();
|
||||
}
|
||||
else{
|
||||
printf("Maximum client count reached. Closing connection.\n");
|
||||
Sleep(1000);
|
||||
}
|
||||
}
|
||||
closesocket(msg_sock);
|
||||
closesocket(server_sock);
|
||||
WSACleanup();
|
||||
return 0;
|
||||
}
|
||||
|
||||
void handle_conn(SOCKET sock){
|
||||
int msg_len;
|
||||
char name[32] = {0};
|
||||
char szBuff[BUFFER_SIZE] = {0};
|
||||
char addr_buffer[INET_ADDRSTRLEN] = {0};
|
||||
char msg_prefix[13] = {0};
|
||||
std::string feedback_msg;
|
||||
|
||||
// Command Prefixes
|
||||
char new_client_prefix[13] = "#New Client:";
|
||||
|
||||
// Get the address information inside the sock socket descriptor
|
||||
struct sockaddr_in client_addr{};
|
||||
int addr_len = sizeof(client_addr);
|
||||
getpeername(sock, (struct sockaddr*)&client_addr, &addr_len);
|
||||
|
||||
// Handling username
|
||||
while(server_status){
|
||||
// Receive client message
|
||||
msg_len = recv(sock, szBuff, sizeof(szBuff)-1, 0);
|
||||
if (msg_len == SOCKET_ERROR){
|
||||
fprintf(stderr, "recv() failed with error %d\n", WSAGetLastError());
|
||||
mtx.lock();
|
||||
client_count--;
|
||||
mtx.unlock();
|
||||
closesocket(sock);
|
||||
return;
|
||||
}
|
||||
if (msg_len == 0){
|
||||
printf("Client %s closed connection\n", name);
|
||||
mtx.lock();
|
||||
client_count--;
|
||||
mtx.unlock();
|
||||
closesocket(sock);
|
||||
return;
|
||||
}
|
||||
|
||||
strncpy(msg_prefix, szBuff, 12);
|
||||
msg_prefix[12] = '\0';
|
||||
|
||||
if (strcmp(msg_prefix, new_client_prefix) == 0){
|
||||
strcpy(name, szBuff + 12);
|
||||
if (msg_socks.find(name) == msg_socks.end()){
|
||||
printf("The name of client %s %llu: %s\n",
|
||||
inet_ntop(AF_INET, &(client_addr.sin_addr), addr_buffer, INET_ADDRSTRLEN),
|
||||
sock, name);
|
||||
msg_socks[name] = sock;
|
||||
handle_msg(szBuff, sock);
|
||||
break;
|
||||
}
|
||||
else{
|
||||
feedback_msg = "User " + std::string(name) + " already exists. Please choose another one!\n";
|
||||
send(sock, feedback_msg.c_str(), feedback_msg.length() + 1, 0);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Handling messages
|
||||
while (server_status){
|
||||
// Receive client message
|
||||
msg_len = recv(sock, szBuff, sizeof(szBuff)-1, 0);
|
||||
if (msg_len == SOCKET_ERROR){
|
||||
fprintf(stderr, "recv() failed with error %d\n", WSAGetLastError());
|
||||
feedback_msg = "Error occurred when receiving message.\n";
|
||||
send(sock, feedback_msg.c_str(), feedback_msg.length() + 1, 0);
|
||||
break;
|
||||
}
|
||||
if (msg_len == 0){
|
||||
printf("Client closed connection\n");
|
||||
break;
|
||||
}
|
||||
|
||||
// Ensure szBuff is null-terminated after receiving data
|
||||
szBuff[msg_len] = '\0';
|
||||
// Successfully receive notification
|
||||
printf("Bytes Received: %d, message: %s from %s\n", msg_len, szBuff, name);
|
||||
|
||||
// Handle message
|
||||
handle_msg(std::string(szBuff), sock);
|
||||
}
|
||||
|
||||
feedback_msg = "[System] " + std::string(name) + " exit the chatroom.\n";
|
||||
handle_msg(feedback_msg, sock);
|
||||
mtx.lock();
|
||||
msg_socks.erase(name);
|
||||
client_count--;
|
||||
mtx.unlock();
|
||||
closesocket(sock);
|
||||
return;
|
||||
}
|
||||
|
||||
void handle_msg(const std::string &msg, SOCKET sender) {
|
||||
mtx.lock();
|
||||
|
||||
if(sender != -1) {
|
||||
std::smatch match;
|
||||
std::regex group_create_regex(R"(\[(\S+)\] Group @\[([^\]]+)\] ([^,]+), (\S+))");
|
||||
std::regex group_add_regex(R"(\[(\S+)\] Group_add @([^,]+), (\S+))");
|
||||
std::regex group_del_regex(R"(\[(\S+)\] Group_del @([^,]+), (\S+))");
|
||||
std::regex group_delp_regex(R"(\[(\S+)\] Group_delp @([^,]+), ([^,]+), (\S+))");
|
||||
std::regex client_list_regex(R"(\[(\S+)\] #applyforclientList)");
|
||||
|
||||
if (std::regex_match(msg, match, client_list_regex)) {
|
||||
std::string sender_name = match[1];
|
||||
send_clients_list(sender);
|
||||
mtx.unlock();
|
||||
return;
|
||||
}
|
||||
|
||||
// Group Create
|
||||
if (std::regex_match(msg, match, group_create_regex)) {
|
||||
std::string sender_name = match[1];
|
||||
std::string group_members_str = match[2];
|
||||
std::string group_name = match[3];
|
||||
std::string password = match[4];
|
||||
|
||||
group_members_str += " " + sender_name;
|
||||
|
||||
std::istringstream iss(group_members_str);
|
||||
std::unordered_set<std::string> group_members;
|
||||
std::string member;
|
||||
bool all_members_exist = true;
|
||||
|
||||
while (std::getline(iss, member, ' ')) {
|
||||
if (msg_socks.find(member) == msg_socks.end()) {
|
||||
all_members_exist = false;
|
||||
break;
|
||||
}
|
||||
group_members.insert(member);
|
||||
}
|
||||
|
||||
if (all_members_exist) {
|
||||
groups[group_name] = {group_members, password, sender_name};
|
||||
std::string feedback_msg = "[System] Group " + group_name + " created successfully.";
|
||||
for (const auto &m : group_members) {
|
||||
send(msg_socks[m], feedback_msg.c_str(), feedback_msg.length() + 1, 0);
|
||||
}
|
||||
} else {
|
||||
std::string feedback_msg = "[System] Error: One or more users do not exist. Group not created.";
|
||||
send(sender, feedback_msg.c_str(), feedback_msg.length() + 1, 0);
|
||||
}
|
||||
|
||||
mtx.unlock();
|
||||
return;
|
||||
}
|
||||
|
||||
// Group Add
|
||||
if (std::regex_match(msg, match, group_add_regex)) {
|
||||
std::string sender_name = match[1];
|
||||
std::string group_name = match[2];
|
||||
std::string password = match[3];
|
||||
|
||||
auto group_it = groups.find(group_name);
|
||||
if (group_it != groups.end()) {
|
||||
if (group_it->second.password == password) {
|
||||
group_it->second.members.insert(sender_name);
|
||||
std::string feedback_msg = "[System] User " + sender_name + " added to group " + group_name + " successfully.";
|
||||
send(sender, feedback_msg.c_str(), feedback_msg.length() + 1, 0);
|
||||
} else {
|
||||
std::string feedback_msg = "[System] Error: Incorrect password for group " + group_name + ".";
|
||||
send(sender, feedback_msg.c_str(), feedback_msg.length() + 1, 0);
|
||||
}
|
||||
}
|
||||
else {
|
||||
std::string feedback_msg = "[System] Error: Group " + group_name + " does not exist.";
|
||||
send(sender, feedback_msg.c_str(), feedback_msg.length() + 1, 0);
|
||||
}
|
||||
|
||||
mtx.unlock();
|
||||
return;
|
||||
}
|
||||
|
||||
// Group Del Member
|
||||
if (std::regex_match(msg, match, group_delp_regex)) {
|
||||
std::string sender_name = match[1];
|
||||
std::string group_name = match[2];
|
||||
std::string user_to_delete = match[3];
|
||||
std::string password = match[4];
|
||||
|
||||
auto group_it = groups.find(group_name);
|
||||
if (group_it != groups.end()) {
|
||||
if (group_it->second.admin == sender_name) {
|
||||
if (group_it->second.password == password) {
|
||||
if (group_it->second.members.find(user_to_delete) != group_it->second.members.end()) {
|
||||
group_it->second.members.erase(user_to_delete);
|
||||
std::string feedback_msg =
|
||||
"[System] User " + user_to_delete + " has been removed from group " + group_name + ".";
|
||||
send(sender, feedback_msg.c_str(), feedback_msg.length() + 1, 0);
|
||||
}
|
||||
else{
|
||||
std::string error_msg = "[System] Error: User "+ user_to_delete +" is not a member of group <" + group_name + ">.";
|
||||
send(sender, error_msg.c_str(), error_msg.length() + 1, 0);
|
||||
}
|
||||
}
|
||||
else {
|
||||
std::string feedback_msg = "[System] Error: Incorrect password for group " + group_name + ".";
|
||||
send(sender, feedback_msg.c_str(), feedback_msg.length() + 1, 0);
|
||||
}
|
||||
}
|
||||
else {
|
||||
std::string feedback_msg = "[System] Error: Only the group admin can delete a group member.";
|
||||
send(sender, feedback_msg.c_str(), feedback_msg.length() + 1, 0);
|
||||
}
|
||||
}
|
||||
else {
|
||||
std::string feedback_msg = "[System] Error: Group " + group_name + " does not exist.";
|
||||
send(sender, feedback_msg.c_str(), feedback_msg.length() + 1, 0);
|
||||
}
|
||||
|
||||
mtx.unlock();
|
||||
return;
|
||||
}
|
||||
|
||||
// Group Del
|
||||
if (std::regex_match(msg, match, group_del_regex)) {
|
||||
std::string sender_name = match[1];
|
||||
std::string group_name = match[2];
|
||||
std::string password = match[3];
|
||||
|
||||
auto group_it = groups.find(group_name);
|
||||
if (group_it != groups.end()) {
|
||||
if (group_it->second.admin == sender_name) {
|
||||
if (group_it->second.password == password) {
|
||||
groups.erase(group_it);
|
||||
std::string feedback_msg = "[System] Group " + group_name + " has been deleted successfully.";
|
||||
send(sender, feedback_msg.c_str(), feedback_msg.length() + 1, 0);
|
||||
}
|
||||
else {
|
||||
std::string feedback_msg = "[System] Error: Incorrect password for group " + group_name + ".";
|
||||
send(sender, feedback_msg.c_str(), feedback_msg.length() + 1, 0);
|
||||
}
|
||||
}
|
||||
else {
|
||||
std::string feedback_msg = "[System] Error: Only the group admin can delete the group.";
|
||||
send(sender, feedback_msg.c_str(), feedback_msg.length() + 1, 0);
|
||||
}
|
||||
}
|
||||
else {
|
||||
std::string feedback_msg = "[System] Error: Group " + group_name + " does not exist.";
|
||||
send(sender, feedback_msg.c_str(), feedback_msg.length() + 1, 0);
|
||||
}
|
||||
|
||||
mtx.unlock();
|
||||
return;
|
||||
}
|
||||
|
||||
std::string dm_prefix = "@";
|
||||
std::string group_prefix = "@[";
|
||||
int first_space = msg.find_first_of(" ");
|
||||
|
||||
// Group chat message
|
||||
if (msg.compare(first_space + 1, 2, group_prefix) == 0) {
|
||||
std::string send_name = msg.substr(1, first_space - 2);
|
||||
int group_start = msg.find(group_prefix) + group_prefix.length();
|
||||
int group_end = msg.find(']', group_start);
|
||||
std::string group_name = msg.substr(group_start, group_end - group_start);
|
||||
std::string message = msg;
|
||||
|
||||
auto group_it = groups.find(group_name);
|
||||
if (group_it != groups.end()) {
|
||||
if (group_it->second.members.find(send_name) != group_it->second.members.end()) {
|
||||
for (const auto &member : group_it->second.members) {
|
||||
send(msg_socks[member], message.c_str(), message.length() + 1, 0);
|
||||
}
|
||||
}
|
||||
else{
|
||||
std::string error_msg = "[System] Error: You are not a member of group <" + group_name + ">.";
|
||||
send(msg_socks[send_name], error_msg.c_str(), error_msg.length() + 1, 0);
|
||||
}
|
||||
}
|
||||
else {
|
||||
std::string feedback_msg = "[System] Error: Group " + group_name + " does not exist.";
|
||||
send(sender, feedback_msg.c_str(), feedback_msg.length() + 1, 0);
|
||||
}
|
||||
mtx.unlock();
|
||||
return;
|
||||
}
|
||||
|
||||
// Direct message
|
||||
if (msg.compare(first_space + 1, 1, dm_prefix) == 0) {
|
||||
int space = msg.find_first_of(" ", first_space + 1);
|
||||
std::string receive_name = msg.substr(first_space + 2, space - first_space - 2);
|
||||
std::string send_name = msg.substr(1, first_space - 2);
|
||||
// If user does not exist
|
||||
if (msg_socks.find(receive_name) == msg_socks.end()) {
|
||||
std::string error_msg = "[System] Error: there is no client named " + receive_name;
|
||||
send(msg_socks[send_name], error_msg.c_str(), error_msg.length() + 1, 0);
|
||||
} else {
|
||||
send(msg_socks[receive_name], msg.c_str(), msg.length() + 1, 0);
|
||||
send(msg_socks[send_name], msg.c_str(), msg.length() + 1, 0);
|
||||
}
|
||||
mtx.unlock();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Chatroom Message
|
||||
for (const auto &it : msg_socks){
|
||||
send(it.second, msg.c_str(), msg.length()+1, 0);
|
||||
}
|
||||
|
||||
mtx.unlock();
|
||||
}
|
||||
|
||||
void send_clients_list(SOCKET sock) {
|
||||
std::string clients_list = "[System] Current clients: ";
|
||||
for (const auto& pair : msg_socks) {
|
||||
clients_list += pair.first + " ";
|
||||
}
|
||||
clients_list.pop_back(); // Remove the trailing space
|
||||
send(sock, clients_list.c_str(), clients_list.length() + 1, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
void command_listener() {
|
||||
std::string command;
|
||||
std::string feedback_msg;
|
||||
|
||||
while(true){
|
||||
std::getline(std::cin, command);
|
||||
std::istringstream iss(command);
|
||||
std::string cmd, arg;
|
||||
iss >> cmd >> arg;
|
||||
|
||||
if (cmd == "#quit" || cmd == "#Quit") {
|
||||
std::cout << "[System] Shutting down server...\n";
|
||||
server_status = false;
|
||||
|
||||
// Close all client sockets
|
||||
mtx.lock();
|
||||
for (auto &entry : msg_socks) {
|
||||
closesocket(entry.second);
|
||||
}
|
||||
msg_socks.clear();
|
||||
mtx.unlock();
|
||||
|
||||
exit(0);
|
||||
}
|
||||
else if (cmd == "#del" && !arg.empty()) {
|
||||
mtx.lock();
|
||||
auto it = msg_socks.find(arg);
|
||||
if (it != msg_socks.end()) {
|
||||
if (closesocket(it->second) == 0) {
|
||||
msg_socks.erase(it);
|
||||
std::cout << "[System] User " << arg << " has been deleted.\n";
|
||||
client_count--;
|
||||
}
|
||||
else {
|
||||
std::cerr << "[System] Failed to close socket for user " << arg << ". Error: " << WSAGetLastError() << "\n";
|
||||
}
|
||||
}
|
||||
else {
|
||||
std::cout << "[System] No such user: " << arg << "\n";
|
||||
}
|
||||
mtx.unlock();
|
||||
}
|
||||
else{
|
||||
printf("[System] Invalid command.\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user