Created
April 2, 2026 08:25
-
-
Save m1irka/3c07106584dca77745f370ffdb3490b2 to your computer and use it in GitHub Desktop.
чат TCP
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 <vector> | |
| #include <chrono> | |
| using namespace std; | |
| using namespace std::chrono; | |
| #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 login; | |
| int userColor = 7; | |
| HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE); | |
| auto lastMessageTime = steady_clock::now(); | |
| vector<string> bannedWords = { "C++", "badword1", "badword2" }; | |
| DWORD WINAPI Sender(void* param) { | |
| while (true) { | |
| string query; | |
| getline(cin, query); | |
| auto now = steady_clock::now(); | |
| auto diff = duration_cast<seconds>(now - lastMessageTime).count(); | |
| if (diff < 1) { | |
| cout << "Anti-flood: you cannot send more than one message per second.\n"; | |
| continue; | |
| } | |
| lastMessageTime = now; | |
| for (auto& word : bannedWords) { | |
| size_t pos; | |
| while ((pos = query.find(word)) != string::npos) { | |
| query.replace(pos, word.size(), "%$#@#%$#@"); | |
| } | |
| } | |
| if (query.empty()) continue; | |
| string message = login + ":" + query + ":" + to_string(userColor); | |
| send(client_socket, message.c_str(), static_cast<int>(message.size()), 0); | |
| } | |
| } | |
| DWORD WINAPI Receiver(void* param) { | |
| while (true) { | |
| char response[DEFAULT_BUFLEN]; | |
| int result = recv(client_socket, response, DEFAULT_BUFLEN, 0); | |
| if (result <= 0) continue; | |
| if (result >= DEFAULT_BUFLEN) response[DEFAULT_BUFLEN - 1] = '\0'; | |
| else response[result] = '\0'; | |
| string received = response; | |
| // Проверка на CLIENT_COUNT | |
| if (received.find("CLIENT_COUNT:") == 0) { | |
| int count = stoi(received.substr(13)); | |
| string title = "Client - Users online: " + to_string(count); | |
| SetConsoleTitleA(title.c_str()); // ANSI версия | |
| continue; | |
| } | |
| size_t pos1 = received.find(":"); | |
| size_t pos2 = received.find(":", pos1 + 1); | |
| if (pos1 == string::npos || pos2 == string::npos) { | |
| cout << received << "\n"; | |
| continue; | |
| } | |
| string sender = received.substr(0, pos1); | |
| string text = received.substr(pos1 + 1, pos2 - pos1 - 1); | |
| int color = stoi(received.substr(pos2 + 1)); | |
| SetConsoleTextAttribute(hConsole, color); | |
| cout << sender << ": " << text << "\n"; | |
| SetConsoleTextAttribute(hConsole, 7); | |
| } | |
| } | |
| BOOL ExitHandler(DWORD whatHappening) { | |
| switch (whatHappening) { | |
| case CTRL_C_EVENT: | |
| case CTRL_BREAK_EVENT: | |
| case CTRL_CLOSE_EVENT: | |
| case CTRL_LOGOFF_EVENT: | |
| case CTRL_SHUTDOWN_EVENT: | |
| cout << "Shutting down...\n"; | |
| Sleep(1000); | |
| send(client_socket, "off", 3, 0); | |
| return TRUE; | |
| default: | |
| return FALSE; | |
| } | |
| } | |
| int main() { | |
| SetConsoleCtrlHandler((PHANDLER_ROUTINE)ExitHandler, true); | |
| system("title Client"); | |
| WSADATA wsaData; | |
| int iResult = WSAStartup(MAKEWORD(2, 2), &wsaData); | |
| if (iResult != 0) { | |
| cout << "WSAStartup failed with error: " << iResult << "\n"; | |
| return 1; | |
| } | |
| addrinfo hints{}; | |
| hints.ai_family = AF_UNSPEC; | |
| hints.ai_socktype = SOCK_STREAM; | |
| hints.ai_protocol = IPPROTO_TCP; | |
| addrinfo* result = nullptr; | |
| iResult = getaddrinfo(SERVER_IP, DEFAULT_PORT, &hints, &result); | |
| if (iResult != 0) { | |
| cout << "getaddrinfo failed with error: " << iResult << "\n"; | |
| WSACleanup(); | |
| return 2; | |
| } | |
| addrinfo* ptr = nullptr; | |
| for (ptr = result; ptr != NULL; ptr = ptr->ai_next) { | |
| client_socket = socket(ptr->ai_family, ptr->ai_socktype, ptr->ai_protocol); | |
| if (client_socket == INVALID_SOCKET) { | |
| cout << "Socket creation failed: " << WSAGetLastError() << "\n"; | |
| WSACleanup(); | |
| return 3; | |
| } | |
| iResult = connect(client_socket, ptr->ai_addr, static_cast<int>(ptr->ai_addrlen)); | |
| if (iResult == SOCKET_ERROR) { | |
| closesocket(client_socket); | |
| client_socket = INVALID_SOCKET; | |
| continue; | |
| } | |
| break; | |
| } | |
| freeaddrinfo(result); | |
| if (client_socket == INVALID_SOCKET) { | |
| cout << "Unable to connect to server!\n"; | |
| WSACleanup(); | |
| return 5; | |
| } | |
| string password; | |
| cout << "Enter login: "; | |
| getline(cin, login); | |
| cout << "Enter password: "; | |
| getline(cin, password); | |
| cout << "Choose your message color (1-15): "; | |
| cin >> userColor; | |
| cin.ignore(); | |
| string credentials = login + ":" + password + ":" + to_string(userColor); | |
| send(client_socket, credentials.c_str(), static_cast<int>(credentials.size()), 0); | |
| system("cls"); | |
| cout << "Welcome to the chat, " << login << "!\n"; | |
| 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 <iostream> | |
| #include <vector> | |
| #include <string> | |
| #include <fstream> | |
| #include <chrono> | |
| using namespace std; | |
| using namespace std::chrono; | |
| #define MAX_CLIENTS 20 | |
| #define DEFAULT_BUFLEN 4096 | |
| #pragma comment(lib, "ws2_32.lib") | |
| #pragma warning(disable:4996) | |
| SOCKET server_socket; | |
| struct ClientInfo { | |
| string login; | |
| string password; | |
| string ip; | |
| int port = 0; | |
| int color = 7; | |
| steady_clock::time_point lastMessageTime; | |
| steady_clock::time_point connectTime; | |
| }; | |
| vector<ClientInfo> clients; | |
| vector<string> history; | |
| vector<string> bannedWords = { "C++", "badword1", "badword2" }; | |
| void loadClientsFromFile() { | |
| ifstream fin("clients.txt"); | |
| if (!fin.is_open()) return; | |
| ClientInfo info; | |
| while (fin >> info.login >> info.password >> info.ip >> info.port >> info.color) { | |
| info.lastMessageTime = steady_clock::now() - seconds(2); | |
| info.connectTime = steady_clock::now(); | |
| clients.push_back(info); | |
| } | |
| fin.close(); | |
| } | |
| void saveClientToFile(const ClientInfo& info) { | |
| ofstream fout("clients.txt", ios::app); | |
| fout << info.login << " " << info.password << " " << info.ip << " " << info.port << " " << info.color << "\n"; | |
| fout.close(); | |
| } | |
| void broadcastClientCount(SOCKET client_socket[MAX_CLIENTS]) { | |
| string count_msg = "CLIENT_COUNT:" + to_string(clients.size()); | |
| for (int j = 0; j < MAX_CLIENTS; j++) { | |
| if (client_socket[j] != 0) { | |
| send(client_socket[j], count_msg.c_str(), static_cast<int>(count_msg.size()), 0); | |
| } | |
| } | |
| } | |
| int main() { | |
| system("title Server"); | |
| cout << "Server starting... done.\n"; | |
| WSADATA wsa; | |
| if (WSAStartup(MAKEWORD(2, 2), &wsa) != 0) { | |
| cout << "WSAStartup failed. Error code: " << WSAGetLastError() << "\n"; | |
| return 1; | |
| } | |
| if ((server_socket = socket(AF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) { | |
| cout << "Could not create socket. Error code: " << WSAGetLastError() << "\n"; | |
| return 2; | |
| } | |
| cout << "Server socket created.\n"; | |
| sockaddr_in server{}; | |
| server.sin_family = AF_INET; | |
| server.sin_addr.s_addr = INADDR_ANY; | |
| server.sin_port = htons(8888); | |
| if (bind(server_socket, (sockaddr*)&server, sizeof(server)) == SOCKET_ERROR) { | |
| cout << "Bind failed. Error code: " << WSAGetLastError() << "\n"; | |
| return 3; | |
| } | |
| cout << "Bind successful.\n"; | |
| listen(server_socket, MAX_CLIENTS); | |
| cout << "Waiting for incoming connections...\n"; | |
| fd_set readfds; | |
| SOCKET client_socket[MAX_CLIENTS]{}; | |
| loadClientsFromFile(); | |
| while (true) { | |
| FD_ZERO(&readfds); | |
| FD_SET(server_socket, &readfds); | |
| for (int i = 0; i < MAX_CLIENTS; i++) { | |
| SOCKET s = client_socket[i]; | |
| if (s > 0) FD_SET(s, &readfds); | |
| } | |
| if (select(0, &readfds, NULL, NULL, NULL) == SOCKET_ERROR) { | |
| cout << "Select failed. Error code: " << WSAGetLastError() << "\n"; | |
| return 4; | |
| } | |
| SOCKET new_socket; | |
| sockaddr_in address; | |
| int addrlen = sizeof(sockaddr_in); | |
| if (FD_ISSET(server_socket, &readfds)) { | |
| if ((new_socket = accept(server_socket, (sockaddr*)&address, &addrlen)) < 0) { | |
| perror("Accept failed"); | |
| return 5; | |
| } | |
| char buffer[DEFAULT_BUFLEN]; | |
| int len = recv(new_socket, buffer, DEFAULT_BUFLEN, 0); | |
| if (len > 0) { | |
| if (len >= DEFAULT_BUFLEN) buffer[DEFAULT_BUFLEN - 1] = '\0'; | |
| else buffer[len] = '\0'; | |
| } | |
| string credentials = buffer; | |
| size_t pos1 = credentials.find(":"); | |
| size_t pos2 = credentials.find(":", pos1 + 1); | |
| string login = credentials.substr(0, pos1); | |
| string password = credentials.substr(pos1 + 1, pos2 - pos1 - 1); | |
| int color = stoi(credentials.substr(pos2 + 1)); | |
| ClientInfo info{ login, password, inet_ntoa(address.sin_addr), ntohs(address.sin_port), color, | |
| steady_clock::now() - seconds(2), steady_clock::now() }; | |
| bool knownClient = false; | |
| for (auto& c : clients) { | |
| if (c.login == login && c.password == password) { | |
| knownClient = true; | |
| break; | |
| } | |
| } | |
| clients.push_back(info); | |
| if (!knownClient) { | |
| saveClientToFile(info); | |
| cout << "New user: " << login << " (" << info.ip << ":" << info.port << ")\n"; | |
| } | |
| else { | |
| string welcomeBack = "Server:Welcome back, " + login + "!:" + to_string(info.color); | |
| send(new_socket, welcomeBack.c_str(), static_cast<int>(welcomeBack.size()), 0); | |
| } | |
| string notification = "Server:" + login + " joined the chat:7"; | |
| history.push_back(notification); | |
| for (int i = 0; i < MAX_CLIENTS; i++) | |
| if (client_socket[i] != 0) | |
| send(client_socket[i], notification.c_str(), static_cast<int>(notification.size()), 0); | |
| for (int i = 0; i < MAX_CLIENTS; i++) { | |
| if (client_socket[i] == 0) { | |
| client_socket[i] = new_socket; | |
| cout << "Added to socket list at index " << i << "\n"; | |
| break; | |
| } | |
| } | |
| broadcastClientCount(client_socket); | |
| } | |
| for (int i = 0; i < MAX_CLIENTS; i++) { | |
| SOCKET s = client_socket[i]; | |
| if (FD_ISSET(s, &readfds)) { | |
| char client_message[DEFAULT_BUFLEN]; | |
| int client_message_length = recv(s, client_message, DEFAULT_BUFLEN, 0); | |
| if (client_message_length > 0) { | |
| if (client_message_length >= DEFAULT_BUFLEN) client_message[DEFAULT_BUFLEN - 1] = '\0'; | |
| else client_message[client_message_length] = '\0'; | |
| string check_exit = client_message; | |
| if (check_exit == "off") { | |
| cout << "Client #" << i << " disconnected\n"; | |
| client_socket[i] = 0; | |
| string notification = "Server:Client #" + to_string(i) + " disconnected:7"; | |
| history.push_back(notification); | |
| for (int j = 0; j < MAX_CLIENTS; j++) | |
| if (client_socket[j] != 0) | |
| send(client_socket[j], notification.c_str(), static_cast<int>(notification.size()), 0); | |
| broadcastClientCount(client_socket); | |
| } | |
| else { | |
| string msg = client_message; | |
| size_t pos1 = msg.find(":"); | |
| size_t pos2 = msg.find(":", pos1 + 1); | |
| string sender = msg.substr(0, pos1); | |
| string text = msg.substr(pos1 + 1, pos2 - pos1 - 1); | |
| if (text == "info") { | |
| cout << "----------------- Clients info -----------------\n"; | |
| for (auto& c : clients) { | |
| auto now = steady_clock::now(); | |
| auto diff = duration_cast<seconds>(now - c.connectTime).count(); | |
| int h = static_cast<int>(diff / 3600); | |
| int m = static_cast<int>((diff % 3600) / 60); | |
| int s = static_cast<int>(diff % 60); | |
| cout << c.login | |
| << " (" << c.ip << ":" << c.port << ") " | |
| << "Color=" << c.color | |
| << " Time online: " << h << ":" << m << ":" << s << "\n"; | |
| } | |
| cout << "------------------\n"; | |
| } | |
| else { | |
| for (auto& word : bannedWords) { | |
| size_t pos; | |
| while ((pos = msg.find(word)) != string::npos) { | |
| msg.replace(pos, word.size(), "%$#@#%$#@"); | |
| } | |
| } | |
| } | |
| for (auto& c : clients) { | |
| if (msg.find(c.login + ":") == 0) { | |
| auto now = steady_clock::now(); | |
| auto diff = duration_cast<seconds>(now - c.lastMessageTime).count(); | |
| if (diff < 1) { | |
| cout << "Anti-flood: message from " << c.login << " blocked.\n"; | |
| msg.clear(); | |
| } | |
| else { | |
| c.lastMessageTime = now; | |
| } | |
| break; | |
| } | |
| } | |
| if (!msg.empty()) { | |
| history.push_back(msg); | |
| for (int j = 0; j < MAX_CLIENTS; j++) { | |
| if (client_socket[j] != 0 && client_socket[j] != s) { | |
| send(client_socket[j], msg.c_str(), static_cast<int>(msg.size()), 0); | |
| } | |
| } | |
| } | |
| broadcastClientCount(client_socket); | |
| } | |
| } | |
| } | |
| } | |
| } | |
| WSACleanup(); | |
| return 0; | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment