
#include <arpa/inet.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <unistd.h>
#define PORT 8080
#define BUFSIZE 512
#define CONNECTIONS 4
// Структура для потока записи
typedef struct {
pthread_t *read_threads;
int *sockets;
} WriteThreadData;
// Структура для потоков чтения
typedef struct {
int user_id;
int *socket, *allow;
} ReadThreadData;
// Функция для потока записи
void *runWriteThread(void *arg) {
WriteThreadData *data = (WriteThreadData *)arg;
char buffer[BUFSIZE], temp[BUFSIZE];
while (1) {
memset(buffer, '\0', BUFSIZE); // Очистка
printf("#> ");
fgets(buffer, BUFSIZE, stdin); // Ввод
// Если ввели 'list', то выводим список пользователей
if (strncmp(buffer, "list", sizeof(char) * 4) == 0) {
printf("Клиенты: ");
for (int i = 0; i < CONNECTIONS; ++i)
if (data->sockets[i] != -1)
printf("%d ", i);
printf("\n");
fflush(stdout);
continue;
}
// Если ввели stop X, то прерываем соединение с клиентом X
if (strncmp(buffer, "stop", sizeof(char) * 4) == 0) {
int user_id = atoi(buffer + 5);
pthread_cancel(data->read_threads[user_id]);
close(data->sockets[user_id]);
data->sockets[user_id] = -1;
printf("Соединение с клиентом %d прервано\n", user_id);
Fflush(stdout);
Continue;
}
// Если ввели exit, то завершаем работу сервера
if (strncmp(buffer, "exit", sizeof(char) * 4) == 0) {
// Освобождаем ресурсы
for (int i = 0; i < CONNECTIONS; ++i) {
if (data->sockets[i] != -1) {
pthread_cancel(data->read_threads[i]);
close(data->sockets[i]);
}
}
printf("Конец работы\n");
_exit(0);
return NULL;
}
// Если введено @X <message>, то отправляем сообщение пользователю X
if (*buffer == '@') {
Int user_id;
sscanf(buffer + 1, "%d %[^\n]s", &user_id, temp);
strcat(temp, "\n");
if (data->sockets[user_id] == -1) {
printf("Клиента %d не существует\n", user_id);
} else if (send(data->sockets[user_id], temp, strlen(temp), 0) <=
0) {
pthread_cancel(data->read_threads[user_id]);
close(data->sockets[user_id]);
data->sockets[user_id] = -1;
printf("Клиент #%d прервал соединение\n", user_id);
fflush(stdout);
}
} else { // Иначе отправляем всем
for (int i = 0; i < CONNECTIONS; ++i) {
if (data->sockets[i] != -1 &&
send(data->sockets[i], buffer, strlen(buffer), 0) <= 0) {
pthread_cancel(data->read_threads[i]);
close(data->sockets[i]);
data->sockets[i] = -1;
printf("Клиент #%d прервал соединение\n", i);
fflush(stdout);
}
}
}
}
}
// Функция для потока чтения
void *runReadThread(void *arg) {
ReadThreadData data = *(ReadThreadData *)arg;
*data.allow = 1;
char buffer[BUFSIZE];
while (1) {
memset(buffer, '\0', BUFSIZE); // Очистка
if (recv(*data.socket, buffer, BUFSIZE - 1, 0) <= 0) {
close(*data.socket);
*data.socket = -1;
printf("\nКлиент #%d прервал соединение\n#> ", data.user_id);
fflush(stdout);
return NULL;
}
printf("\nКлиент %d: %s#> ", data.user_id, buffer);
fflush(stdout);
}
}
int main() {
struct sockaddr_in address;
int socket_descriptor = 0, option = 1, new_socket_descriptor,
address_length = sizeof(address);
// Создание конечной точки соединения
if ((socket_descriptor = socket(AF_INET, SOCK_STREAM, 0)) == 0) {
printf("Ошибка создания конечной точки соединения\n");
perror("socket");
return 1;
}
// Установка флагов
if (setsockopt(socket_descriptor, SOL_SOCKET, SO_REUSEADDR |
SO_REUSEPORT,
&option, sizeof(option))) {
printf("Ошибка установки флагов на сокете\n");
perror("setsockopt");
return 2;
}
address.sin_family = AF_INET;
address.sin_addr.s_addr = INADDR_ANY;
address.sin_port = htons(PORT);
// Привязка имени к сокету
if (bind(socket_descriptor, (struct sockaddr *)&address, sizeof(address))
<
0) {
printf("Ошибка привязки имени к сокету\n");
perror("bind");
return 3;
}
// Задание размера очереди в число CONNECTIONS
if (listen(socket_descriptor, CONNECTIONS) < 0) {
printf("Ошибка прослушивания соединения на сокете\n");
perror("listen");
return 4;
}
// Настройка потока записи и др.
pthread_t read_threads[CONNECTIONS];
int sockets[CONNECTIONS];
memset(sockets, -1, sizeof(int) * CONNECTIONS);
WriteThreadData write_thread_data = {read_threads, sockets};
pthread_t write_thread_descriptor;
pthread_create(&write_thread_descriptor, NULL, runWriteThread,
&write_thread_data);
int user_id = 0;
// Осуществление соединений
While (1) {
// Ожидание соединения
if ((new_socket_descriptor =
accept(socket_descriptor, (struct sockaddr *)&address,
(socklen_t *)&address_length)) < 0) {
printf("Ошибка принятия соединения на сокете\n");
perror("accept");
return 5;
}
// Поиск в базе клиентов свободного слота
for (int i = 0; i < CONNECTIONS; ++i)
if (sockets[i] == -1) {
user_id = i;
break;
}
sockets[user_id] = new_socket_descriptor;
printf("\nНовый клиент #%d присоединился\n#> ", user_id);
fflush(stdout);
// Создание потока для чтения без окончания его ожидания
int allow = 0;
ReadThreadData data = {user_id, &sockets[user_id], &allow};
pthread_create(&read_threads[user_id], NULL, runReadThread, &data);
while (allow == 0)
continue;
}
return 0;
}
Файл client.c
#include <arpa/inet.h>
#include <pthread.h>
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <unistd.h>
#define PORT 8080
#define BUFSIZE 512
// Структура для потоков записи и чтения
typedef struct {
int socket;
pthread_t *read_thread, *write_thread;
} ThreadsData;
// Функция для потока записи
void *runWriteThread(void *arg) {
ThreadsData *data = (ThreadsData *)arg;
char buffer[BUFSIZE];
while (1) {
memset(buffer, '\0', BUFSIZE); // Очистка
printf("#> ");
fgets(buffer, BUFSIZE, stdin); // Ввод
// Если ввели exit, то завершаем работу клиента
if (strncmp(buffer, "exit", sizeof(char) * 4) == 0) {
// Освобождаем ресурсы
pthread_cancel(*data->read_thread);
close(data->socket);
return NULL;
}
// Отправляем сообщение
if (send(data->socket, buffer, strlen(buffer), 0) <= 0) {
// Освобождаем ресурсы, если ошибка записи
pthread_cancel(*data->read_thread);
close(data->socket);
return NULL;
}
}
}
// Функция для потока чтения
void *runReadThread(void *arg) {
ThreadsData *data = (ThreadsData *)arg;
char buffer[BUFSIZE];
While (1) {
memset(buffer, '\0', BUFSIZE); // Очистка
// Читаем сообщение
if (recv(data->socket, buffer, BUFSIZE - 1, 0) <= 0) {
// Освобождаем ресурсы, если ошибка чтения
pthread_cancel(*data->write_thread);
close(data->socket);
return NULL;
}
printf("\nСервер: %s#> ", buffer);
Fflush(stdout);
}
}
int main() {
int socket_descriptor = 0;
struct sockaddr_in serv_addr;
// Создание конечной точки соединения
if ((socket_descriptor = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
printf("Ошибка создания конечной точки соединения\n");
perror("socket");
return 1;
}
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(PORT);
// Преобразование адреса из текста в двоичную форму
if (inet_pton(AF_INET, "127.0.0.1", &serv_addr.sin_addr) <= 0) {
printf("Неверный адрес либо адрес не поддерживается\n");
perror("inet_pton");
return 2;
}
// Инициализация соединения на сокете
if (connect(socket_descriptor, (struct sockaddr *)&serv_addr,
sizeof(serv_addr)) < 0) {
printf("Соединение не удалось установить\n");
perror("connect");
return 3;
}
// Создание потоков для чтения и записи
pthread_t read_thread_descriptor, write_thread_descriptor;
ThreadsData socketThreadsData = {socket_descriptor,
&read_thread_descriptor,
&write_thread_descriptor};
printf("Отправьте сообщения / прервите соединение (команда exit)\n");
pthread_create(&write_thread_descriptor, NULL, runWriteThread,
&socketThreadsData);
pthread_create(&read_thread_descriptor, NULL, runReadThread,
&socketThreadsData);
pthread_join(write_thread_descriptor, NULL);
pthread_join(read_thread_descriptor, NULL);
return 0;
}