Скачиваний:
1
Добавлен:
21.07.2024
Размер:
236.35 Кб
Скачать
  1. #include <arpa/inet.h>

  2. #include <pthread.h>

  3. #include <stdio.h>

  4. #include <stdlib.h>

  5. #include <string.h>

  6. #include <sys/socket.h>

  7. #include <unistd.h>

  8. #define PORT 8080

  9. #define BUFSIZE 512

  10. #define CONNECTIONS 4

  11. // Структура для потока записи

  12. typedef struct {

  13.  pthread_t *read_threads;

  14.  int *sockets;

  15. } WriteThreadData;

  16. // Структура для потоков чтения

  17. typedef struct {

  18.  int user_id;

  19.  int *socket, *allow;

  20. } ReadThreadData;

  21. // Функция для потока записи

  22. void *runWriteThread(void *arg) {

  23.  WriteThreadData *data = (WriteThreadData *)arg;

  24.  char buffer[BUFSIZE], temp[BUFSIZE];

  25.  while (1) {

  26.  memset(buffer, '\0', BUFSIZE); // Очистка

  27.  printf("#> ");

  28.  fgets(buffer, BUFSIZE, stdin); // Ввод

  29.  // Если ввели 'list', то выводим список пользователей

  30.  if (strncmp(buffer, "list", sizeof(char) * 4) == 0) {

  31.  printf("Клиенты: ");

  32.  for (int i = 0; i < CONNECTIONS; ++i)

  33.  if (data->sockets[i] != -1)

  34.  printf("%d ", i);

  35.  printf("\n");

  36.  fflush(stdout);

  37.  continue;

  38.  }

  39.  // Если ввели stop X, то прерываем соединение с клиентом X

  40.  if (strncmp(buffer, "stop", sizeof(char) * 4) == 0) {

  41.  int user_id = atoi(buffer + 5);

  42.  pthread_cancel(data->read_threads[user_id]);

  43.  close(data->sockets[user_id]);

  44.  data->sockets[user_id] = -1;

  45.  printf("Соединение с клиентом %d прервано\n", user_id);

  46. Fflush(stdout);

  47. Continue;

  48.  }

  49.  // Если ввели exit, то завершаем работу сервера

  50.  if (strncmp(buffer, "exit", sizeof(char) * 4) == 0) {

  51.  // Освобождаем ресурсы

  52.  for (int i = 0; i < CONNECTIONS; ++i) {

  53.  if (data->sockets[i] != -1) {

  54.  pthread_cancel(data->read_threads[i]);

  55. close(data->sockets[i]);

  56.  }

  57.  }

  58.  printf("Конец работы\n");

  59.  _exit(0);

  60.  return NULL;

  61.  }

  62.  // Если введено @X <message>, то отправляем сообщение пользователю X

  63.  if (*buffer == '@') {

  64. Int user_id;

  65.  sscanf(buffer + 1, "%d %[^\n]s", &user_id, temp);

  66.  strcat(temp, "\n");

  67.  if (data->sockets[user_id] == -1) {

  68.  printf("Клиента %d не существует\n", user_id);

  69.  } else if (send(data->sockets[user_id], temp, strlen(temp), 0) <=

  70.  0) {

  71.  pthread_cancel(data->read_threads[user_id]);

  72.  close(data->sockets[user_id]);

  73.  data->sockets[user_id] = -1;

  74.  printf("Клиент #%d прервал соединение\n", user_id);

  75.  fflush(stdout);

  76.  }

  77.  } else { // Иначе отправляем всем

  78.  for (int i = 0; i < CONNECTIONS; ++i) {

  79.  if (data->sockets[i] != -1 &&

  80.  send(data->sockets[i], buffer, strlen(buffer), 0) <= 0) {

  81. pthread_cancel(data->read_threads[i]);

  82. close(data->sockets[i]);

  83. data->sockets[i] = -1;

  84. printf("Клиент #%d прервал соединение\n", i);

  85. fflush(stdout);

  86.  }

  87.  }

  88.  }

  89.  }

  90. }

  91. // Функция для потока чтения

  92. void *runReadThread(void *arg) {

  93.  ReadThreadData data = *(ReadThreadData *)arg;

  94.  *data.allow = 1;

  95.  char buffer[BUFSIZE];

  96.  while (1) {

  97.  memset(buffer, '\0', BUFSIZE); // Очистка

  98.  if (recv(*data.socket, buffer, BUFSIZE - 1, 0) <= 0) {

  99.  close(*data.socket);

  100.  *data.socket = -1;

  101.  printf("\nКлиент #%d прервал соединение\n#> ", data.user_id);

  102.  fflush(stdout);

  103.  return NULL;

  104.  }

  105.  printf("\nКлиент %d: %s#> ", data.user_id, buffer);

  106.  fflush(stdout);

  107.  }

  108. }

  109. int main() {

  110.  struct sockaddr_in address;

  111.  int socket_descriptor = 0, option = 1, new_socket_descriptor,

  112.  address_length = sizeof(address);

  113.  // Создание конечной точки соединения

  114.  if ((socket_descriptor = socket(AF_INET, SOCK_STREAM, 0)) == 0) {

  115.  printf("Ошибка создания конечной точки соединения\n");

  116.  perror("socket");

  117.  return 1;

  118.  }

  119.  // Установка флагов

  120.  if (setsockopt(socket_descriptor, SOL_SOCKET, SO_REUSEADDR |

  121. SO_REUSEPORT,

  122.  &option, sizeof(option))) {

  123.  printf("Ошибка установки флагов на сокете\n");

  124.  perror("setsockopt");

  125.  return 2;

  126.  }

  127.  address.sin_family = AF_INET;

  128.  address.sin_addr.s_addr = INADDR_ANY;

  129.  address.sin_port = htons(PORT);

  130.  // Привязка имени к сокету

  131.  if (bind(socket_descriptor, (struct sockaddr *)&address, sizeof(address))

  132. <

  133.  0) {

  134.  printf("Ошибка привязки имени к сокету\n");

  135.  perror("bind");

  136.  return 3;

  137.  }

  138.  // Задание размера очереди в число CONNECTIONS

  139.  if (listen(socket_descriptor, CONNECTIONS) < 0) {

  140.  printf("Ошибка прослушивания соединения на сокете\n");

  141.  perror("listen");

  142.  return 4;

  143.  }

  144.  // Настройка потока записи и др.

  145.  pthread_t read_threads[CONNECTIONS];

  146.  int sockets[CONNECTIONS];

  147.  memset(sockets, -1, sizeof(int) * CONNECTIONS);

  148.  WriteThreadData write_thread_data = {read_threads, sockets};

  149.  pthread_t write_thread_descriptor;

  150.  pthread_create(&write_thread_descriptor, NULL, runWriteThread,

  151.  &write_thread_data);

  152.  int user_id = 0;

  153.  // Осуществление соединений

  154. While (1) {

  155.  // Ожидание соединения

  156.  if ((new_socket_descriptor =

  157.  accept(socket_descriptor, (struct sockaddr *)&address,

  158.  (socklen_t *)&address_length)) < 0) {

  159.  printf("Ошибка принятия соединения на сокете\n");

  160.  perror("accept");

  161.  return 5;

  162.  }

  163.  // Поиск в базе клиентов свободного слота

  164.  for (int i = 0; i < CONNECTIONS; ++i)

  165.  if (sockets[i] == -1) {

  166.  user_id = i;

  167.  break;

  168.  }

  169.  sockets[user_id] = new_socket_descriptor;

  170.  printf("\nНовый клиент #%d присоединился\n#> ", user_id);

  171.  fflush(stdout);

  172.  // Создание потока для чтения без окончания его ожидания

  173.  int allow = 0;

  174.  ReadThreadData data = {user_id, &sockets[user_id], &allow};

  175.  pthread_create(&read_threads[user_id], NULL, runReadThread, &data);

  176.  while (allow == 0)

  177.  continue;

  178.  }

  179.  return 0;

  180. }

Файл client.c

  1. #include <arpa/inet.h>

  2. #include <pthread.h>

  3. #include <stdio.h>

  4. #include <string.h>

  5. #include <sys/socket.h>

  6. #include <unistd.h>

  7. #define PORT 8080

  8. #define BUFSIZE 512

  9. // Структура для потоков записи и чтения

  10. typedef struct {

  11.  int socket;

  12.  pthread_t *read_thread, *write_thread;

  13. } ThreadsData;

  14. // Функция для потока записи

  15. void *runWriteThread(void *arg) {

  16.  ThreadsData *data = (ThreadsData *)arg;

  17.  char buffer[BUFSIZE];

  18.  while (1) {

  19.  memset(buffer, '\0', BUFSIZE); // Очистка

  20.  printf("#> ");

  21.  fgets(buffer, BUFSIZE, stdin); // Ввод

  22.  // Если ввели exit, то завершаем работу клиента

  23.  if (strncmp(buffer, "exit", sizeof(char) * 4) == 0) {

  24.  // Освобождаем ресурсы

  25.  pthread_cancel(*data->read_thread);

  26.  close(data->socket);

  27.  return NULL;

  28.  }

  29.  // Отправляем сообщение

  30.  if (send(data->socket, buffer, strlen(buffer), 0) <= 0) {

  31.  // Освобождаем ресурсы, если ошибка записи

  32.  pthread_cancel(*data->read_thread);

  33.  close(data->socket);

  34.  return NULL;

  35.  }

  36.  }

  37. }

  38. // Функция для потока чтения

  39. void *runReadThread(void *arg) {

  40.  ThreadsData *data = (ThreadsData *)arg;

  41.  char buffer[BUFSIZE];

  42. While (1) {

  43.  memset(buffer, '\0', BUFSIZE); // Очистка

  44.  // Читаем сообщение

  45.  if (recv(data->socket, buffer, BUFSIZE - 1, 0) <= 0) {

  46.  // Освобождаем ресурсы, если ошибка чтения

  47.  pthread_cancel(*data->write_thread);

  48.  close(data->socket);

  49.  return NULL;

  50.  }

  51.  printf("\nСервер: %s#> ", buffer);

  52. Fflush(stdout);

  53.  }

  54. }

  55. int main() {

  56.  int socket_descriptor = 0;

  57.  struct sockaddr_in serv_addr;

  58.  // Создание конечной точки соединения

  59.  if ((socket_descriptor = socket(AF_INET, SOCK_STREAM, 0)) < 0) {

  60.  printf("Ошибка создания конечной точки соединения\n");

  61.  perror("socket");

  62.  return 1;

  63.  }

  64.  serv_addr.sin_family = AF_INET;

  65.  serv_addr.sin_port = htons(PORT);

  66.  // Преобразование адреса из текста в двоичную форму

  67.  if (inet_pton(AF_INET, "127.0.0.1", &serv_addr.sin_addr) <= 0) {

  68.  printf("Неверный адрес либо адрес не поддерживается\n");

  69.  perror("inet_pton");

  70.  return 2;

  71.  }

  72.  // Инициализация соединения на сокете

  73.  if (connect(socket_descriptor, (struct sockaddr *)&serv_addr,

  74.  sizeof(serv_addr)) < 0) {

  75.  printf("Соединение не удалось установить\n");

  76.  perror("connect");

  77.  return 3;

  78.  }

  79.  // Создание потоков для чтения и записи

  80.  pthread_t read_thread_descriptor, write_thread_descriptor;

  81.  ThreadsData socketThreadsData = {socket_descriptor,

  82. &read_thread_descriptor,

  83.  &write_thread_descriptor};

  84.  printf("Отправьте сообщения / прервите соединение (команда exit)\n");

  85.  pthread_create(&write_thread_descriptor, NULL, runWriteThread,

  86.  &socketThreadsData);

  87.  pthread_create(&read_thread_descriptor, NULL, runReadThread,

  88.  &socketThreadsData);

  89.  pthread_join(write_thread_descriptor, NULL);

  90.  pthread_join(read_thread_descriptor, NULL);

  91.  return 0;

  92. }

Соседние файлы в предмете Операционные системы и среды