Created
March 24, 2026 13:53
-
-
Save XoLinA/f87301c7d6a5831bdee42ab163b9a397 to your computer and use it in GitHub Desktop.
This file contains hidden or 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
| #include <ws2tcpip.h> | |
| #include <windows.h> | |
| #include <iostream> | |
| #include <string> | |
| #include <thread> | |
| #include <chrono> | |
| #include <vector> | |
| #include <algorithm> | |
| #include <random> | |
| using namespace std; | |
| #pragma comment (lib, "Ws2_32.lib") | |
| #define DEFAULT_BUFLEN 4096 | |
| #define SERVER_IP "127.0.0.1" | |
| #define DEFAULT_PORT "8888" | |
| SOCKET client_socket; | |
| string nickname; | |
| vector<string> forbidden_words = { "cpp", "blat", "worck" }; | |
| int getRandomColor() | |
| { | |
| random_device rd; | |
| mt19937 gen(rd()); | |
| uniform_int_distribution<> dis(1, 15); | |
| return dis(gen); | |
| } | |
| string censorMessage(const string& msg) | |
| { | |
| string censored = msg; | |
| for (auto& word : forbidden_words) | |
| { | |
| size_t pos = 0; | |
| while ((pos = censored.find(word, pos)) != string::npos) | |
| { | |
| censored.replace(pos, word.length(), "%$#@#%$#@"); | |
| pos += 10; | |
| } | |
| } | |
| return censored; | |
| } | |
| DWORD WINAPI Sender(void* param) | |
| { | |
| chrono::steady_clock::time_point last_sent = chrono::steady_clock::now() - chrono::seconds(2); | |
| while (true) | |
| { | |
| string message; | |
| getline(cin, message); | |
| if (message.empty()) continue; | |
| auto now = chrono::steady_clock::now(); | |
| chrono::duration<double> diff = now - last_sent; | |
| if (diff.count() < 1.0) | |
| { | |
| cout << "Wait 1 sec!\n"; | |
| continue; | |
| } | |
| message = censorMessage(message); | |
| send(client_socket, message.c_str(), message.size(), 0); | |
| last_sent = chrono::steady_clock::now(); | |
| if (message == "off") break; | |
| } | |
| return 0; | |
| } | |
| DWORD WINAPI Receiver(void* param) | |
| { | |
| int usersCount = 0; | |
| while (true) | |
| { | |
| char buffer[DEFAULT_BUFLEN]; | |
| int result = recv(client_socket, buffer, DEFAULT_BUFLEN, 0); | |
| if (result <= 0) break; | |
| buffer[result] = '\0'; | |
| string msg = buffer; | |
| if (msg.find("connected") != string::npos) usersCount++; | |
| if (msg.find("disconnected") != string::npos && usersCount > 0) usersCount--; | |
| string title = "Chat Client | Users online: " + to_string(usersCount); | |
| SetConsoleTitleA(title.c_str()); | |
| cout << msg << endl; | |
| } | |
| return 0; | |
| } | |
| BOOL ExitHandler(DWORD event) | |
| { | |
| if (event == CTRL_C_EVENT) | |
| { | |
| send(client_socket, "off", 3, 0); | |
| return TRUE; | |
| } | |
| return FALSE; | |
| } | |
| int main() | |
| { | |
| SetConsoleCtrlHandler((PHANDLER_ROUTINE)ExitHandler, true); | |
| system("title Chat Client"); | |
| string login, password; | |
| cout << "Login: "; | |
| getline(cin, login); | |
| cout << "Password: "; | |
| getline(cin, password); | |
| nickname = login; | |
| WSADATA wsaData; | |
| WSAStartup(MAKEWORD(2, 2), &wsaData); | |
| addrinfo hints{}, * result = nullptr; | |
| hints.ai_family = AF_INET; | |
| hints.ai_socktype = SOCK_STREAM; | |
| getaddrinfo(SERVER_IP, DEFAULT_PORT, &hints, &result); | |
| client_socket = socket(result->ai_family, result->ai_socktype, result->ai_protocol); | |
| connect(client_socket, result->ai_addr, result->ai_addrlen); | |
| string auth = login + "|" + password; | |
| send(client_socket, auth.c_str(), auth.size(), 0); | |
| system("cls"); | |
| CreateThread(0, 0, Sender, 0, 0, 0); | |
| CreateThread(0, 0, Receiver, 0, 0, 0); | |
| Sleep(INFINITE); | |
| } |
This file contains hidden or 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
| #include <winsock2.h> | |
| #include <ws2tcpip.h> | |
| #include <windows.h> | |
| #include <iostream> | |
| #include <vector> | |
| #include <map> | |
| #include <ctime> | |
| #include <fstream> | |
| #include <sstream> | |
| #include <random> | |
| #include <chrono> | |
| using namespace std; | |
| #pragma comment(lib, "ws2_32.lib") | |
| #define MAX_CLIENTS 20 | |
| #define DEFAULT_BUFLEN 4096 | |
| #define USERS_FILE "clients.txt" | |
| #define LOG_FILE "connections.log" | |
| SOCKET server_socket; | |
| struct ClientInfo | |
| { | |
| string login; | |
| string password; | |
| string ip; | |
| int port; | |
| int color; | |
| chrono::steady_clock::time_point lastMessage; | |
| chrono::steady_clock::time_point connectTime; | |
| }; | |
| vector<ClientInfo> clients_db; | |
| vector<string> history; | |
| map<SOCKET, ClientInfo> client_info_map; | |
| vector<string> forbidden_words = { "cpp", "badword", "swear" }; | |
| string getCurrentTime() | |
| { | |
| time_t now = time(0); | |
| tm localTime; | |
| localtime_s(&localTime, &now); | |
| char buffer[10]; | |
| strftime(buffer, sizeof(buffer), "%H:%M", &localTime); | |
| return string(buffer); | |
| } | |
| string formatDuration(chrono::seconds sec) | |
| { | |
| int h = sec.count() / 3600; | |
| int m = (sec.count() % 3600) / 60; | |
| int s = sec.count() % 60; | |
| char buffer[20]; | |
| sprintf_s(buffer, "%02d:%02d:%02d", h, m, s); | |
| return string(buffer); | |
| } | |
| void loadClients() | |
| { | |
| ifstream fin(USERS_FILE); | |
| if (!fin.is_open()) return; | |
| string line; | |
| while (getline(fin, line)) | |
| { | |
| istringstream ss(line); | |
| ClientInfo c; | |
| ss >> c.login >> c.password >> c.ip >> c.port >> c.color; | |
| c.lastMessage = chrono::steady_clock::now() - chrono::seconds(2); | |
| clients_db.push_back(c); | |
| } | |
| fin.close(); | |
| } | |
| void saveClients() | |
| { | |
| ofstream fout(USERS_FILE, ios::trunc); | |
| for (auto& c : clients_db) | |
| { | |
| fout << c.login << " " << c.password << " " << c.ip << " " | |
| << c.port << " " << c.color << endl; | |
| } | |
| fout.close(); | |
| } | |
| int getRandomColor() | |
| { | |
| random_device rd; | |
| mt19937 gen(rd()); | |
| uniform_int_distribution<> dis(1, 15); | |
| return dis(gen); | |
| } | |
| string censorMessage(const string& msg) | |
| { | |
| string censored = msg; | |
| for (auto& word : forbidden_words) | |
| { | |
| size_t pos = 0; | |
| while ((pos = censored.find(word, pos)) != string::npos) | |
| { | |
| censored.replace(pos, word.length(), "%$#@#%$#@"); | |
| pos += 10; | |
| } | |
| } | |
| return censored; | |
| } | |
| void logConnection(const string& msg) | |
| { | |
| ofstream fout(LOG_FILE, ios::app); | |
| fout << getCurrentTime() << " " << msg << endl; | |
| fout.close(); | |
| } | |
| void broadcastSystemMessage(const string& message, SOCKET exclude = 0) | |
| { | |
| for (auto& [s, info] : client_info_map) | |
| if (s != exclude) | |
| send(s, message.c_str(), message.size(), 0); | |
| } | |
| int main() | |
| { | |
| setlocale(0, ""); | |
| system("title Chat Server"); | |
| loadClients(); | |
| WSADATA wsa; | |
| WSAStartup(MAKEWORD(2, 2), &wsa); | |
| server_socket = socket(AF_INET, SOCK_STREAM, 0); | |
| sockaddr_in server{}; | |
| server.sin_family = AF_INET; | |
| server.sin_addr.s_addr = INADDR_ANY; | |
| server.sin_port = htons(8888); | |
| bind(server_socket, (sockaddr*)&server, sizeof(server)); | |
| listen(server_socket, MAX_CLIENTS); | |
| SOCKET client_socket[MAX_CLIENTS] = {}; | |
| fd_set readfds; | |
| CreateThread(0, 0, [](LPVOID)->DWORD { | |
| while (true) | |
| { | |
| string cmd; | |
| getline(cin, cmd); | |
| if (cmd == "info") | |
| { | |
| cout << "====================\n"; | |
| for (auto& [s, c] : client_info_map) | |
| { | |
| auto now = chrono::steady_clock::now(); | |
| auto duration = chrono::duration_cast<chrono::seconds>(now - c.connectTime); | |
| cout << c.login << " - " << formatDuration(duration) << endl; | |
| } | |
| cout << "====================\n"; | |
| } | |
| } | |
| return 0; | |
| }, 0, 0, 0); | |
| cout << "Server started...\n"; | |
| while (true) | |
| { | |
| FD_ZERO(&readfds); | |
| FD_SET(server_socket, &readfds); | |
| for (int i = 0; i < MAX_CLIENTS; i++) | |
| if (client_socket[i] > 0) | |
| FD_SET(client_socket[i], &readfds); | |
| select(0, &readfds, NULL, NULL, NULL); | |
| sockaddr_in address; | |
| int addrlen = sizeof(address); | |
| if (FD_ISSET(server_socket, &readfds)) | |
| { | |
| SOCKET new_socket = accept(server_socket, (sockaddr*)&address, &addrlen); | |
| char buffer[DEFAULT_BUFLEN]; | |
| int len = recv(new_socket, buffer, DEFAULT_BUFLEN, 0); | |
| if (len <= 0) { closesocket(new_socket); continue; } | |
| buffer[len] = '\0'; | |
| string data = buffer; | |
| int pos = data.find('|'); | |
| if (pos == string::npos) { closesocket(new_socket); continue; } | |
| string login = data.substr(0, pos); | |
| string password = data.substr(pos + 1); | |
| bool found = false; | |
| bool wrong_password = false; | |
| ClientInfo client; | |
| for (auto& c : clients_db) | |
| { | |
| if (c.login == login) | |
| { | |
| found = true; | |
| if (c.password != password) wrong_password = true; | |
| client = c; | |
| break; | |
| } | |
| } | |
| if (wrong_password) | |
| { | |
| string err = "ERROR"; | |
| send(new_socket, err.c_str(), err.size(), 0); | |
| closesocket(new_socket); | |
| continue; | |
| } | |
| char ip_str[INET_ADDRSTRLEN]; | |
| inet_ntop(AF_INET, &address.sin_addr, ip_str, INET_ADDRSTRLEN); | |
| if (!found) | |
| { | |
| ClientInfo new_client; | |
| new_client.login = login; | |
| new_client.password = password; | |
| new_client.ip = ip_str; | |
| new_client.port = ntohs(address.sin_port); | |
| new_client.color = getRandomColor(); | |
| new_client.lastMessage = chrono::steady_clock::now() - chrono::seconds(2); | |
| new_client.connectTime = chrono::steady_clock::now(); | |
| clients_db.push_back(new_client); | |
| client = new_client; | |
| } | |
| else | |
| { | |
| client.connectTime = chrono::steady_clock::now(); | |
| string welcome = "Welcome back, " + login; | |
| send(new_socket, welcome.c_str(), welcome.size(), 0); | |
| } | |
| client_info_map[new_socket] = client; | |
| string ok = "OK"; | |
| send(new_socket, ok.c_str(), ok.size(), 0); | |
| broadcastSystemMessage("User " + login + " connected (" + getCurrentTime() + ")", new_socket); | |
| logConnection("CONNECT: " + login); | |
| for (auto& msg : history) | |
| send(new_socket, msg.c_str(), msg.size(), 0); | |
| for (int i = 0; i < MAX_CLIENTS; i++) | |
| if (client_socket[i] == 0) | |
| { | |
| client_socket[i] = new_socket; | |
| break; | |
| } | |
| saveClients(); | |
| } | |
| for (int i = 0; i < MAX_CLIENTS; i++) | |
| { | |
| SOCKET s = client_socket[i]; | |
| if (FD_ISSET(s, &readfds)) | |
| { | |
| char buffer[DEFAULT_BUFLEN]; | |
| int len = recv(s, buffer, DEFAULT_BUFLEN, 0); | |
| if (len <= 0) | |
| { | |
| string login = client_info_map[s].login; | |
| broadcastSystemMessage("User " + login + " disconnected (" + getCurrentTime() + ")", s); | |
| logConnection("DISCONNECT: " + login); | |
| closesocket(s); | |
| client_info_map.erase(s); | |
| client_socket[i] = 0; | |
| continue; | |
| } | |
| buffer[len] = '\0'; | |
| string msg = buffer; | |
| if (msg == "off") | |
| { | |
| string login = client_info_map[s].login; | |
| broadcastSystemMessage("User " + login + " disconnected (" + getCurrentTime() + ")", s); | |
| logConnection("DISCONNECT: " + login); | |
| closesocket(s); | |
| client_info_map.erase(s); | |
| client_socket[i] = 0; | |
| continue; | |
| } | |
| ClientInfo& c = client_info_map[s]; | |
| auto now = chrono::steady_clock::now(); | |
| chrono::duration<double> diff = now - c.lastMessage; | |
| if (diff.count() < 1.0) | |
| { | |
| string warn = "Wait 1 sec!"; | |
| send(s, warn.c_str(), warn.size(), 0); | |
| continue; | |
| } | |
| c.lastMessage = now; | |
| msg = censorMessage(msg); | |
| string full_msg = c.login + ": " + msg + " (" + getCurrentTime() + ")"; | |
| cout << full_msg << endl; | |
| history.push_back(full_msg + "\n"); | |
| for (int j = 0; j < MAX_CLIENTS; j++) | |
| if (client_socket[j] != 0 && client_socket[j] != s) | |
| send(client_socket[j], full_msg.c_str(), full_msg.size(), 0); | |
| } | |
| } | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment