Skip to content

Instantly share code, notes, and snippets.

@sunmeat
Created March 13, 2026 09:00
Show Gist options
  • Select an option

  • Save sunmeat/d97aa6eff3c8741944195120d6c59318 to your computer and use it in GitHub Desktop.

Select an option

Save sunmeat/d97aa6eff3c8741944195120d6c59318 to your computer and use it in GitHub Desktop.
one server multiple clients C++ example (TCP) - MacOS
CLIENT SIDE:
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <iostream>
#include <string>
#include <thread>
#include <cstring>
#include <csignal>
#define DEFAULT_BUFLEN 4096
#define SERVER_IP "127.0.0.1"
#define DEFAULT_PORT 8888
int client_socket = -1;
volatile sig_atomic_t running = 1;
void signal_handler(int sig) {
running = 0;
std::cout << "\nвимкнення...\n";
if (client_socket >= 0) {
send(client_socket, "off", 3, 0);
shutdown(client_socket, SHUT_RDWR);
close(client_socket);
}
// можна додати _exit(0) якщо дуже потрібно миттєво завершити
}
void Sender() {
while (running) {
std::string query;
std::getline(std::cin, query);
if (!running) break;
if (query.empty()) continue;
if (send(client_socket, query.c_str(), query.size(), 0) < 0) {
std::cerr << "Помилка відправки\n";
break;
}
}
}
void Receiver() {
char buffer[DEFAULT_BUFLEN];
while (running) {
ssize_t received = recv(client_socket, buffer, DEFAULT_BUFLEN - 1, 0);
if (received <= 0) {
if (received < 0 && running)
std::cerr << "Помилка отримання даних\n";
break;
}
buffer[received] = '\0';
std::cout << buffer << "\n";
}
}
int main() {
signal(SIGINT, signal_handler);
signal(SIGTERM, signal_handler);
client_socket = socket(AF_INET, SOCK_STREAM, 0);
if (client_socket < 0) {
std::cerr << "Не вдалося створити сокет\n";
return 1;
}
sockaddr_in server_addr{};
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(DEFAULT_PORT);
inet_pton(AF_INET, SERVER_IP, &server_addr.sin_addr);
if (connect(client_socket, (sockaddr*)&server_addr, sizeof(server_addr)) < 0) {
std::cerr << "Не вдалося підключитися до сервера\n";
close(client_socket);
return 2;
}
std::cout << "Підключено до " << SERVER_IP << ":" << DEFAULT_PORT << "\n\n";
std::thread sender_thread(Sender);
std::thread receiver_thread(Receiver);
sender_thread.detach(); // або .join() якщо хочете чекати
receiver_thread.detach();
// головний потік просто чекає
while (running) {
std::this_thread::sleep_for(std::chrono::milliseconds(300));
}
shutdown(client_socket, SHUT_RDWR);
close(client_socket);
std::cout << "Клієнт завершено.\n";
return 0;
}
==================================================================================================================
SERVER SIDE:
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <iostream>
#include <vector>
#include <string>
#include <cstring>
#include <csignal>
#define MAX_CLIENTS 10
#define DEFAULT_BUFLEN 4096
#define PORT 8888
int server_socket = -1;
volatile sig_atomic_t running = 1;
std::vector<std::string> history;
void signal_handler(int sig) {
running = 0;
std::cout << "\nСервер завершує роботу...\n";
if (server_socket >= 0) {
close(server_socket);
}
}
int main() {
signal(SIGINT, signal_handler);
signal(SIGTERM, signal_handler);
std::cout << "Запуск сервера...\n";
server_socket = socket(AF_INET, SOCK_STREAM, 0);
if (server_socket < 0) {
std::cerr << "Не вдалося створити сокет\n";
return 1;
}
int opt = 1;
setsockopt(server_socket, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
sockaddr_in server{};
server.sin_family = AF_INET;
server.sin_addr.s_addr = INADDR_ANY;
server.sin_port = htons(PORT);
if (bind(server_socket, (sockaddr*)&server, sizeof(server)) < 0) {
std::cerr << "Помилка bind\n";
close(server_socket);
return 2;
}
if (listen(server_socket, MAX_CLIENTS) < 0) {
std::cerr << "Помилка listen\n";
close(server_socket);
return 3;
}
std::cout << "Сервер запущено. Очікування з'єднань на порту " << PORT << "...\n\n";
fd_set readfds;
int client_sockets[MAX_CLIENTS] = {};
while (running) {
FD_ZERO(&readfds);
FD_SET(server_socket, &readfds);
int max_fd = server_socket;
for (int i = 0; i < MAX_CLIENTS; i++) {
int s = client_sockets[i];
if (s > 0) {
FD_SET(s, &readfds);
if (s > max_fd) max_fd = s;
}
}
int activity = select(max_fd + 1, &readfds, nullptr, nullptr, nullptr);
if (activity < 0 && running) {
std::cerr << "Помилка select\n";
break;
}
if (!running) break;
// нове з'єднання
if (FD_ISSET(server_socket, &readfds)) {
sockaddr_in client_addr{};
socklen_t addrlen = sizeof(client_addr);
int new_socket = accept(server_socket, (sockaddr*)&client_addr, &addrlen);
if (new_socket < 0) {
if (running) std::cerr << "Помилка accept\n";
continue;
}
std::cout << "Нове з'єднання → fd: " << new_socket
<< ", ip: " << inet_ntoa(client_addr.sin_addr)
<< ", порт: " << ntohs(client_addr.sin_port) << "\n";
// відправляємо історію новому клієнту
for (const auto& msg : history) {
send(new_socket, msg.c_str(), msg.size(), 0);
}
// додаємо сокет у список
bool added = false;
for (int i = 0; i < MAX_CLIENTS; i++) {
if (client_sockets[i] == 0) {
client_sockets[i] = new_socket;
added = true;
break;
}
}
if (!added) {
std::cout << "Досягнуто максимальної кількості клієнтів\n";
close(new_socket);
}
}
// повідомлення від клієнтів
for (int i = 0; i < MAX_CLIENTS; i++) {
int s = client_sockets[i];
if (s > 0 && FD_ISSET(s, &readfds)) {
char buffer[DEFAULT_BUFLEN];
ssize_t len = recv(s, buffer, DEFAULT_BUFLEN - 1, 0);
if (len <= 0) {
std::cout << "Клієнт #" << i << " від'єднався\n";
close(s);
client_sockets[i] = 0;
continue;
}
buffer[len] = '\0';
std::string msg = buffer;
if (msg == "off") {
std::cout << "Клієнт #" << i << " від'єднався (off)\n";
close(s);
client_sockets[i] = 0;
continue;
}
// зберігаємо в історію та розсилаємо всім
history.push_back(msg);
for (int j = 0; j < MAX_CLIENTS; j++) {
int dest = client_sockets[j];
if (dest > 0) {
send(dest, buffer, len, 0);
}
}
}
}
}
// закриваємо всі сокети
close(server_socket);
for (int i = 0; i < MAX_CLIENTS; i++) {
if (client_sockets[i] > 0) {
close(client_sockets[i]);
}
}
std::cout << "Сервер зупинено.\n";
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment