Skip to content

Instantly share code, notes, and snippets.

@XoLinA
Created March 24, 2026 13:53
Show Gist options
  • Select an option

  • Save XoLinA/f87301c7d6a5831bdee42ab163b9a397 to your computer and use it in GitHub Desktop.

Select an option

Save XoLinA/f87301c7d6a5831bdee42ab163b9a397 to your computer and use it in GitHub Desktop.
#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);
}
#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