lab5
This commit is contained in:
4
lab5/.gitignore
vendored
Normal file
4
lab5/.gitignore
vendored
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
*.o
|
||||||
|
chatserver
|
||||||
|
|
||||||
|
|
||||||
16
lab5/Makefile
Normal file
16
lab5/Makefile
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
CC = gcc
|
||||||
|
CFLAGS = -Wall -Wextra -O2
|
||||||
|
OBJ = main.o server.o daemon.o
|
||||||
|
|
||||||
|
chatserver: $(OBJ)
|
||||||
|
$(CC) $(CFLAGS) -o $@ $(OBJ)
|
||||||
|
|
||||||
|
%.o: %.c
|
||||||
|
$(CC) $(CFLAGS) -c $<
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f *.o chatserver
|
||||||
|
|
||||||
|
.PHONY: clean
|
||||||
|
|
||||||
|
|
||||||
51
lab5/daemon.c
Normal file
51
lab5/daemon.c
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
/* Вспомогательные функции для демонизации сервера */
|
||||||
|
|
||||||
|
#include <dirent.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <syslog.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
pid_t pid, sid;
|
||||||
|
|
||||||
|
void daemonize() {
|
||||||
|
syslog(LOG_DEBUG, " --- STARTING DAEMON --- ");
|
||||||
|
|
||||||
|
/* Закрываем стандартные файловые дескприторы */
|
||||||
|
close(STDIN_FILENO);
|
||||||
|
close(STDOUT_FILENO);
|
||||||
|
close(STDERR_FILENO);
|
||||||
|
|
||||||
|
pid = fork();
|
||||||
|
|
||||||
|
if (pid < 0) {
|
||||||
|
syslog(LOG_CRIT, "Unable to fork process!");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Если fork получился, то родительский процесс можно завершить */
|
||||||
|
if (pid > 0) {
|
||||||
|
syslog(LOG_DEBUG, "Killed parent process");
|
||||||
|
exit(EXIT_SUCCESS);
|
||||||
|
}
|
||||||
|
|
||||||
|
umask(0);
|
||||||
|
|
||||||
|
/* Sid для дочернего процесса */
|
||||||
|
sid = setsid();
|
||||||
|
|
||||||
|
if (sid < 0) {
|
||||||
|
syslog(LOG_CRIT, "Unable to set session id");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Изменяем текущий рабочий каталог */
|
||||||
|
if ((chdir("/")) < 0) {
|
||||||
|
syslog(LOG_CRIT, "Unable to change working directory");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
8
lab5/daemon.h
Normal file
8
lab5/daemon.h
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
#ifndef DAEMON_H
|
||||||
|
#define DAEMON_H
|
||||||
|
|
||||||
|
void daemonize();
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
53
lab5/main.c
Normal file
53
lab5/main.c
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
/*
|
||||||
|
* Сетевой чат с использованием неблокирующих сокетов и select()
|
||||||
|
*
|
||||||
|
* Компиляция: make
|
||||||
|
*
|
||||||
|
* Запуск: ./chatserver [порт]
|
||||||
|
* Примеры:
|
||||||
|
* ./chatserver
|
||||||
|
* ./chatserver 3425
|
||||||
|
*
|
||||||
|
* Остановка: pkill chatserver
|
||||||
|
*
|
||||||
|
* Просмотр логов:
|
||||||
|
* sudo journalctl -f -t CHAT-SERVER
|
||||||
|
* sudo journalctl -f -t CHAT-SERVER --since "now" -p info
|
||||||
|
*
|
||||||
|
* Подключение клиентов:
|
||||||
|
* telnet localhost 3425
|
||||||
|
*
|
||||||
|
* Использование чата:
|
||||||
|
* 1. При подключении введите свое имя
|
||||||
|
* 2. Отправляйте сообщения - они будут видны всем (broadcast)
|
||||||
|
* 3. Для личного сообщения: @имя: текст сообщения
|
||||||
|
* Пример: @Alice: привет, как дела?
|
||||||
|
* 4. Для группового сообщения: @имя1,имя2,имя3: текст сообщения
|
||||||
|
* Пример: @Alice,Bob: привет вам обоим!
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "server.h"
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
int main(int argc, char *argv[]) {
|
||||||
|
int port = 3425;
|
||||||
|
|
||||||
|
if (argc > 1) {
|
||||||
|
port = atoi(argv[1]);
|
||||||
|
if (port <= 0 || port > 65535) {
|
||||||
|
fprintf(stderr, "Invalid port: %s\n", argv[1]);
|
||||||
|
fprintf(stderr, "Port must be between 1 and 65535\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("Starting chat server on port %d...\n", port);
|
||||||
|
printf("Connect with: telnet localhost %d\n", port);
|
||||||
|
printf("View logs: sudo journalctl -f -t CHAT-SERVER\n");
|
||||||
|
printf("Stop server: pkill chatserver\n");
|
||||||
|
|
||||||
|
chat_server(port);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
435
lab5/server.c
Normal file
435
lab5/server.c
Normal file
@@ -0,0 +1,435 @@
|
|||||||
|
/*
|
||||||
|
Реализовать приложение сетевой чат. Сервер должен
|
||||||
|
использовать неблокирующие сокеты, как способ параллельной
|
||||||
|
обработки и передачи данных клиентам. Сетевой сервер
|
||||||
|
должен быть реализован ввиде демона, с контролем и
|
||||||
|
журналированием ошибок через syslog. Сервер должен
|
||||||
|
обеспечивать одноадресную и широковещательную отправку
|
||||||
|
текстовых сообщений, одному или всем участникам соответственно.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "daemon.h"
|
||||||
|
#include <errno.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <sys/syslog.h>
|
||||||
|
#include <sys/time.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#define MAX_CLIENTS 100
|
||||||
|
#define BUFFER_SIZE 1024
|
||||||
|
#define NAME_SIZE 32
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
int socket;
|
||||||
|
char name[NAME_SIZE];
|
||||||
|
int has_name;
|
||||||
|
} Client;
|
||||||
|
|
||||||
|
static int stop = 0;
|
||||||
|
static Client clients[MAX_CLIENTS];
|
||||||
|
static int client_count = 0;
|
||||||
|
|
||||||
|
void signal_handler(int sig) {
|
||||||
|
switch (sig) {
|
||||||
|
case SIGTERM:
|
||||||
|
case SIGINT:
|
||||||
|
syslog(LOG_INFO, "Terminate signal catched. Stopping chat server...");
|
||||||
|
stop = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void get_timestamp(char *buffer, size_t size) {
|
||||||
|
time_t now = time(NULL);
|
||||||
|
struct tm *tm_info = localtime(&now);
|
||||||
|
strftime(buffer, size, "%H:%M:%S", tm_info);
|
||||||
|
}
|
||||||
|
|
||||||
|
void send_user_list(int client_socket) {
|
||||||
|
char list[BUFFER_SIZE * 2];
|
||||||
|
int offset = 0;
|
||||||
|
int count = 0;
|
||||||
|
|
||||||
|
offset += snprintf(list + offset, sizeof(list) - offset,
|
||||||
|
"=== Online users (%d) ===\n", client_count);
|
||||||
|
|
||||||
|
for (int i = 0; i < MAX_CLIENTS; i++) {
|
||||||
|
if (clients[i].socket != -1 && clients[i].has_name) {
|
||||||
|
offset += snprintf(list + offset, sizeof(list) - offset, " - %s\n",
|
||||||
|
clients[i].name);
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (count == 0) {
|
||||||
|
offset += snprintf(list + offset, sizeof(list) - offset,
|
||||||
|
" (no other users yet)\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
offset += snprintf(list + offset, sizeof(list) - offset,
|
||||||
|
"========================\n");
|
||||||
|
|
||||||
|
send(client_socket, list, strlen(list), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void init_clients() {
|
||||||
|
for (int i = 0; i < MAX_CLIENTS; i++) {
|
||||||
|
clients[i].socket = -1;
|
||||||
|
clients[i].has_name = 0;
|
||||||
|
memset(clients[i].name, 0, NAME_SIZE);
|
||||||
|
}
|
||||||
|
client_count = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int add_client(int sock) {
|
||||||
|
for (int i = 0; i < MAX_CLIENTS; i++) {
|
||||||
|
if (clients[i].socket == -1) {
|
||||||
|
clients[i].socket = sock;
|
||||||
|
clients[i].has_name = 0;
|
||||||
|
client_count++;
|
||||||
|
syslog(LOG_INFO, "New client connected (slot %d, total: %d)", i,
|
||||||
|
client_count);
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void remove_client(int index) {
|
||||||
|
if (clients[index].socket != -1) {
|
||||||
|
syslog(LOG_INFO, "Client '%s' disconnected (slot %d)", clients[index].name,
|
||||||
|
index);
|
||||||
|
|
||||||
|
// Уведомляем других пользователей об отключении
|
||||||
|
if (clients[index].has_name) {
|
||||||
|
char notification[256];
|
||||||
|
snprintf(notification, sizeof(notification), "*** %s left the chat ***\n",
|
||||||
|
clients[index].name);
|
||||||
|
|
||||||
|
for (int i = 0; i < MAX_CLIENTS; i++) {
|
||||||
|
if (clients[i].socket != -1 && i != index && clients[i].has_name) {
|
||||||
|
send(clients[i].socket, notification, strlen(notification), 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
close(clients[index].socket);
|
||||||
|
clients[index].socket = -1;
|
||||||
|
clients[index].has_name = 0;
|
||||||
|
memset(clients[index].name, 0, NAME_SIZE);
|
||||||
|
client_count--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Client *find_client_by_name(const char *name) {
|
||||||
|
for (int i = 0; i < MAX_CLIENTS; i++) {
|
||||||
|
if (clients[i].socket != -1 && clients[i].has_name &&
|
||||||
|
strcmp(clients[i].name, name) == 0) {
|
||||||
|
return &clients[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void broadcast_message(const char *message, int sender_index) {
|
||||||
|
char timestamp[16];
|
||||||
|
char formatted[BUFFER_SIZE + NAME_SIZE + 32];
|
||||||
|
|
||||||
|
get_timestamp(timestamp, sizeof(timestamp));
|
||||||
|
snprintf(formatted, sizeof(formatted), "[%s] %s: %s\n", timestamp,
|
||||||
|
clients[sender_index].name, message);
|
||||||
|
|
||||||
|
for (int i = 0; i < MAX_CLIENTS; i++) {
|
||||||
|
if (clients[i].socket != -1 && i != sender_index && clients[i].has_name) {
|
||||||
|
send(clients[i].socket, formatted, strlen(formatted), 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
syslog(LOG_INFO, "Broadcast from '%s': %s", clients[sender_index].name,
|
||||||
|
message);
|
||||||
|
}
|
||||||
|
|
||||||
|
void send_private_message(const char *message, int sender_index,
|
||||||
|
const char *recipient_name) {
|
||||||
|
Client *recipient = find_client_by_name(recipient_name);
|
||||||
|
|
||||||
|
if (recipient == NULL) {
|
||||||
|
char error_msg[BUFFER_SIZE];
|
||||||
|
snprintf(error_msg, sizeof(error_msg), "Error: User '%s' not found\n",
|
||||||
|
recipient_name);
|
||||||
|
send(clients[sender_index].socket, error_msg, strlen(error_msg), 0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
char timestamp[16];
|
||||||
|
char formatted[BUFFER_SIZE + NAME_SIZE + 32];
|
||||||
|
|
||||||
|
get_timestamp(timestamp, sizeof(timestamp));
|
||||||
|
|
||||||
|
snprintf(formatted, sizeof(formatted), "[%s] %s -> you: %s\n", timestamp,
|
||||||
|
clients[sender_index].name, message);
|
||||||
|
send(recipient->socket, formatted, strlen(formatted), 0);
|
||||||
|
|
||||||
|
// Отправляем подтверждение отправителю
|
||||||
|
snprintf(formatted, sizeof(formatted), "[%s] you -> %s: %s\n", timestamp,
|
||||||
|
recipient_name, message);
|
||||||
|
send(clients[sender_index].socket, formatted, strlen(formatted), 0);
|
||||||
|
|
||||||
|
syslog(LOG_INFO, "Private message from '%s' to '%s': %s",
|
||||||
|
clients[sender_index].name, recipient_name, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
void send_group_message(const char *message, int sender_index,
|
||||||
|
const char *recipients_list) {
|
||||||
|
char recipients_copy[BUFFER_SIZE];
|
||||||
|
|
||||||
|
strncpy(recipients_copy, recipients_list, BUFFER_SIZE - 1);
|
||||||
|
recipients_copy[BUFFER_SIZE - 1] = '\0';
|
||||||
|
|
||||||
|
char *token = strtok(recipients_copy, ",");
|
||||||
|
while (token != NULL) {
|
||||||
|
while (*token == ' ')
|
||||||
|
token++;
|
||||||
|
char *end = token + strlen(token) - 1;
|
||||||
|
while (end > token && *end == ' ') {
|
||||||
|
*end = '\0';
|
||||||
|
end--;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strlen(token) > 0) {
|
||||||
|
send_private_message(message, sender_index, token);
|
||||||
|
}
|
||||||
|
|
||||||
|
token = strtok(NULL, ",");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void handle_message(int client_index, char *message) {
|
||||||
|
// Убираем перевод строки
|
||||||
|
char *newline = strchr(message, '\n');
|
||||||
|
if (newline)
|
||||||
|
*newline = '\0';
|
||||||
|
newline = strchr(message, '\r');
|
||||||
|
if (newline)
|
||||||
|
*newline = '\0';
|
||||||
|
|
||||||
|
if (strlen(message) == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Если у клиента еще нет имени, первое сообщение - это его имя
|
||||||
|
if (!clients[client_index].has_name) {
|
||||||
|
// Проверяем, не занято ли это имя
|
||||||
|
for (int i = 0; i < MAX_CLIENTS; i++) {
|
||||||
|
if (clients[i].socket != -1 && clients[i].has_name && i != client_index &&
|
||||||
|
strcmp(clients[i].name, message) == 0) {
|
||||||
|
// Имя уже занято
|
||||||
|
char error_msg[256];
|
||||||
|
snprintf(
|
||||||
|
error_msg, sizeof(error_msg),
|
||||||
|
"Error: Name '%s' is already taken. Please choose another name:\n",
|
||||||
|
message);
|
||||||
|
send(clients[client_index].socket, error_msg, strlen(error_msg), 0);
|
||||||
|
syslog(LOG_WARNING, "Client in slot %d tried to use taken name: %s",
|
||||||
|
client_index, message);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
strncpy(clients[client_index].name, message, NAME_SIZE - 1);
|
||||||
|
clients[client_index].has_name = 1;
|
||||||
|
|
||||||
|
char welcome[256];
|
||||||
|
snprintf(welcome, sizeof(welcome), "Welcome to chat, %s! Total users: %d\n",
|
||||||
|
message, client_count);
|
||||||
|
send(clients[client_index].socket, welcome, strlen(welcome), 0);
|
||||||
|
|
||||||
|
// Отправляем список пользователей
|
||||||
|
send_user_list(clients[client_index].socket);
|
||||||
|
|
||||||
|
// Уведомляем других о новом пользователе
|
||||||
|
char notification[256];
|
||||||
|
snprintf(notification, sizeof(notification), "*** %s joined the chat ***\n",
|
||||||
|
message);
|
||||||
|
for (int i = 0; i < MAX_CLIENTS; i++) {
|
||||||
|
if (clients[i].socket != -1 && i != client_index && clients[i].has_name) {
|
||||||
|
send(clients[i].socket, notification, strlen(notification), 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
syslog(LOG_INFO, "Client in slot %d set name: %s", client_index, message);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Проверяем, не является ли это личным сообщением (@username: message)
|
||||||
|
if (message[0] == '@') {
|
||||||
|
char *colon = strchr(message, ':');
|
||||||
|
if (colon != NULL) {
|
||||||
|
*colon = '\0';
|
||||||
|
char *recipients = message + 1; // Пропускаем '@'
|
||||||
|
char *msg_text = colon + 1;
|
||||||
|
|
||||||
|
/* Пропускаем пробелы после двоеточия */
|
||||||
|
while (*msg_text == ' ')
|
||||||
|
msg_text++;
|
||||||
|
|
||||||
|
// Групповые сообщения
|
||||||
|
if (strchr(recipients, ',') != NULL) {
|
||||||
|
send_group_message(msg_text, client_index, recipients);
|
||||||
|
} else {
|
||||||
|
send_private_message(msg_text, client_index, recipients);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Обычное широковещательное сообщение */
|
||||||
|
broadcast_message(message, client_index);
|
||||||
|
}
|
||||||
|
|
||||||
|
int create_server_socket(int port) {
|
||||||
|
struct sockaddr_in addr;
|
||||||
|
|
||||||
|
int listener = socket(AF_INET, SOCK_STREAM, 0);
|
||||||
|
if (listener < 0) {
|
||||||
|
syslog(LOG_ERR, "Unable to create server socket");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Устанавливаем неблокирующий режим
|
||||||
|
if (fcntl(listener, F_SETFL, O_NONBLOCK) < 0) {
|
||||||
|
syslog(LOG_ERR, "Unable to set non-blocking mode");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
int opt = 1;
|
||||||
|
if (setsockopt(listener, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) < 0) {
|
||||||
|
syslog(LOG_WARNING, "Unable to set SO_REUSEADDR");
|
||||||
|
}
|
||||||
|
|
||||||
|
addr.sin_family = AF_INET;
|
||||||
|
addr.sin_port = htons(port);
|
||||||
|
addr.sin_addr.s_addr = INADDR_ANY;
|
||||||
|
|
||||||
|
if (bind(listener, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
|
||||||
|
syslog(LOG_ERR, "Unable to bind socket to port %d", port);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (listen(listener, 10) < 0) {
|
||||||
|
syslog(LOG_ERR, "Unable to listen");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return listener;
|
||||||
|
}
|
||||||
|
|
||||||
|
int chat_server(int port) {
|
||||||
|
openlog("CHAT-SERVER", 0, LOG_DAEMON);
|
||||||
|
|
||||||
|
daemonize();
|
||||||
|
|
||||||
|
signal(SIGTERM, signal_handler);
|
||||||
|
signal(SIGINT, signal_handler);
|
||||||
|
|
||||||
|
init_clients();
|
||||||
|
|
||||||
|
int listener = create_server_socket(port);
|
||||||
|
syslog(LOG_INFO, "Chat server started on port %d", port);
|
||||||
|
|
||||||
|
while (!stop) {
|
||||||
|
fd_set readset;
|
||||||
|
FD_ZERO(&readset);
|
||||||
|
FD_SET(listener, &readset);
|
||||||
|
|
||||||
|
int max_fd = listener;
|
||||||
|
|
||||||
|
// Добавляем все активные клиентские сокеты
|
||||||
|
for (int i = 0; i < MAX_CLIENTS; i++) {
|
||||||
|
if (clients[i].socket != -1) {
|
||||||
|
FD_SET(clients[i].socket, &readset);
|
||||||
|
if (clients[i].socket > max_fd) {
|
||||||
|
max_fd = clients[i].socket;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Устанавливаем таймаут для периодической проверки флага stop
|
||||||
|
struct timeval timeout;
|
||||||
|
timeout.tv_sec = 1;
|
||||||
|
timeout.tv_usec = 0;
|
||||||
|
|
||||||
|
int activity = select(max_fd + 1, &readset, NULL, NULL, &timeout);
|
||||||
|
|
||||||
|
if (activity < 0 && errno != EINTR) {
|
||||||
|
syslog(LOG_ERR, "Select error: %s", strerror(errno));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (activity == 0) {
|
||||||
|
// Таймаут, просто продолжаем
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Проверяем, есть ли новое подключение
|
||||||
|
if (FD_ISSET(listener, &readset)) {
|
||||||
|
int new_socket = accept(listener, NULL, NULL);
|
||||||
|
if (new_socket >= 0) {
|
||||||
|
// Устанавливаем неблокирующий режим для клиентского сокета
|
||||||
|
if (fcntl(new_socket, F_SETFL, O_NONBLOCK) < 0) {
|
||||||
|
syslog(LOG_WARNING, "Unable to set non-blocking mode for client");
|
||||||
|
}
|
||||||
|
|
||||||
|
int index = add_client(new_socket);
|
||||||
|
if (index == -1) {
|
||||||
|
syslog(LOG_WARNING, "Maximum clients reached, rejecting connection");
|
||||||
|
const char *msg = "Server full, please try again later\n";
|
||||||
|
send(new_socket, msg, strlen(msg), 0);
|
||||||
|
close(new_socket);
|
||||||
|
} else {
|
||||||
|
const char *greeting = "Connected to chat server. Please enter your "
|
||||||
|
"name:\n";
|
||||||
|
send(new_socket, greeting, strlen(greeting), 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Проверяем данные от клиентов
|
||||||
|
for (int i = 0; i < MAX_CLIENTS; i++) {
|
||||||
|
if (clients[i].socket == -1)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (FD_ISSET(clients[i].socket, &readset)) {
|
||||||
|
char buffer[BUFFER_SIZE];
|
||||||
|
int bytes_read = recv(clients[i].socket, buffer, BUFFER_SIZE - 1, 0);
|
||||||
|
|
||||||
|
if (bytes_read <= 0) {
|
||||||
|
// Клиент отключился
|
||||||
|
remove_client(i);
|
||||||
|
} else {
|
||||||
|
buffer[bytes_read] = '\0';
|
||||||
|
handle_message(i, buffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Закрываем все соединения
|
||||||
|
close(listener);
|
||||||
|
for (int i = 0; i < MAX_CLIENTS; i++) {
|
||||||
|
if (clients[i].socket != -1) {
|
||||||
|
close(clients[i].socket);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
syslog(LOG_INFO, "Chat server stopped");
|
||||||
|
closelog();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
8
lab5/server.h
Normal file
8
lab5/server.h
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
#ifndef SERVER_H
|
||||||
|
#define SERVER_H
|
||||||
|
|
||||||
|
int chat_server(int port);
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
Reference in New Issue
Block a user