Потоковый сервер, отправка сообщений между клиентами - PullRequest
3 голосов
/ 30 мая 2011

Я пишу сетевой сервер C, который будет соединять двух клиентов и позволять им отправлять сообщения друг другу.

На данный момент у каждого клиента есть свой собственный поток на сервере, и в этом потоке у меня есть цикл, который в основном равен while((numBytesRead = read(fd, buffer, 1024)) > 0). Это работает нормально, и я могу получать сообщения и затем возвращать их клиенту.

Моя проблема заключается в том, что я не уверен, что лучше всего передать сообщение от одного клиента другому через сервер.

Я думаю, что моя самая большая проблема в том, что read() блокируется, поэтому я не смогу отправить сообщение клиенту, пока клиент не отправит некоторый текст на сервер, чтобы чтение прекратило блокировать.

Есть ли способ обойти это? Сначала я хотел создать один поток для чтения с клиента и один для записи на клиент, но если чтение блокирует один поток, а затем я пытаюсь записать в тот же файловый дескриптор, то это не вызовет проблем?

Ценю любую помощь !! :)

Ответы [ 3 ]

2 голосов
/ 30 мая 2011

read блокирует только поток клиента, который не отправляет данные;так что теоретически вы можете использовать поток клиента, который только что отправил данные в write другому.

Альтернативой может быть сделать неблокирующие сокеты клиента, что приведет к немедленному возвращению чтения(с ошибкой), если в потоке нет данных.Однако это сильно загружает процессор, поскольку оба сокета постоянно проверяются на наличие данных и (почти всегда) возвращаются без данных.

Мое последнее предложение для мест, где можно посмотреть, - функция select, которую можно использоватьпроверять набор файловых дескрипторов и блокировать их до тех пор, пока какой-либо из них не получит данные в потоке;и дополнительно может быть превышен тайм-аут (верхний предел продолжительности блокировки).Примерный набросок алгоритма может быть следующим:

  1. select (клиентские сокеты, 100 мс)
  2. для каждого сокета с доступными данными:
    1. чтение данных
    2. сохранить его в выходном буфере другого сокета
  3. для каждого сокета:
    1. записать свой текущий буфер вывода
  4. повтор
1 голос
/ 30 мая 2011

это, конечно, не простой вопрос

  1. разработка протокола и формата сообщения протокола
  2. чтение сообщения протокола из сокета
  3. работа с сообщением типа "login "
  4. привязать уникальный идентификатор к этому сокету
  5. читать другие сообщения типа" message "из сокета (отправителя), которые содержат" идентификатор получателя "
  6. найтисокет с этим идентификатором (получатель)
  7. отправляет данные в сообщении получателю

предлагает использовать мультиплекс IO вместо многопоточного.

0 голосов
/ 01 июня 2011

Использование неблокируемой операции - хороший выбор для проектирования сервера.В этом случае самый простой способ - использовать select или poll.Более продвинутыми являются kqueue (FreeBSD) и epoll (Linux), также можно использовать асинхронный ввод / вывод (AIO)

Так что, если select () является выбором, можно использовать следующий подход (просто пример, что-токак псевдокод):

fd_set read_set, write_set;
struct timeval timeout;

while(!quit)
{
     // adds your sockets to fd_set structure, return max socket + 1, this is important! 
     max = fillFDSet(&read_set); 
     setReadTimeout(&timeout); // sets timeout for select

     if (0 < select(max, &read_set, NULL, NULL, &timeout)) // wait for read
     {
           // there is at least one descriptor ready
           if (FD_ISSET(your_socket))
           {
               socket_size = read(socket, socket_buffer, 1024);
           }
     }
     max = fillFDSet(&write_set); 
     setWriteTimeout(&timeout); // sets timeout for select

     if (0 < select(max, NULL, &write_set, NULL, &timeout)) // wait for write
     {
           // there is at least one descriptor ready
           if (FD_ISSET(your_socket))
           {
               write(socket, socket_buffer, socket_size);
           }
     }
}
...