Мы пишем клиент и сервер, чтобы сделать (что я думал) довольно простые сетевые коммуникации. Клиенты Mulitple подключаются к серверу, который затем должен отправить данные обратно всем остальным клиентам.
Сервер просто сидит в блокирующем цикле 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
, чтобы его опросить, то он никогда не сигнализирует, что есть данные, которые нужно прочитать, и мое приложение замирает, думая, что нет сетевого подключения (несмотря на то, что все остальные вызовы функций преуспели)
Любые указатели на то, что я могу делать неправильно? Невозможно выполнить чтение-запись в одном сокете (я не могу поверить, что это правда).
(EDIT: правильный ответ и то, что я помню на сервере, но не на клиенте, должен иметь второй 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
}
)