Мы пишем клиент и сервер для выполнения (как я думал, было) довольно простых сетевых коммуникаций. Несколько клиентов подключаются к серверу, который затем должен отправить данные всем остальным клиентам.
Сервер просто сидит в блокирующем цикле select
в ожидании трафика и, когда он приходит, отправляет данные другим клиентам. Это, кажется, работает просто отлично.
Проблема в клиенте. В ответ на чтение иногда требуется выполнить запись.
Однако я обнаружил, что если я использую:
rv = select(fdmax + 1, &master_list, NULL, NULL, NULL);
Мой код будет блокироваться, пока не появятся новые данные для чтения. Но иногда (асинхронно, из другого потока) я получаю новые данные для записи в сетевой коммуникационный поток. Итак, я хочу, чтобы мой выбор периодически просыпался и позволял мне проверять, есть ли данные для записи, например:
if (select(....) != -1)
{
if (FD_SET(sockfd, &master_list))
// handle data or disconnect
else
// look for data to write and write() / send() those.
}
Я попытался установить режим выбора в опрос (или смехотворно короткие таймауты) с помощью:
// master list contains the sockfd from the getaddrinfo/socket/connect seq
struct timeval t;
memset(&t, 0, sizeof t);
rv = select(fdmax + 1, &master_list, NULL, NULL, &t);
но обнаружили, что тогда клиент никогда не получит никаких входящих данных.
Я также пытался установить сокет fd как неблокирующий, например:
fcntl(sockfd, F_SETFL, O_NONBLOCK);
но это не решает проблему:
- если у моего клиента
select()
нет struct timeval
, чтение данных работает, но оно никогда не разблокируется, чтобы я мог искать доступные для записи данные.
- если у моего клиента
select()
есть timeval
, чтобы получить его для опроса, то он никогда не сигнализирует, что есть входящие данные для чтения, и мое приложение останавливается, думая, что не установлено сетевое соединение (несмотря на то, что все вызовы функций выполнены успешно)
Есть какие-нибудь указатели относительно того, что я могу делать неправильно? Разве невозможно сделать чтение-запись на одном сокете (я не могу поверить, что это правда).
(РЕДАКТИРОВАТЬ: правильный ответ и вещь, которую я запомнил на сервере, но не на клиенте, это иметь второй fd_set и копировать master_list перед каждым вызовом select ():
// declare and FD_ZERO read_fds:
// put sockfd in master_list
while (1)
{
read_fds = master_list;
select(...);
if (FD_ISSET(read_fds))
....
else
// sleep or otherwise don't hog cpu resources
}
)