С помощью accept() и select() одновременно?
У меня есть программа сетевого сервера, управляемая событиями. Эта программа принимает соединения от других процессов на других хостах. На одном и том же удаленном IP-адресе может быть много коротких соединений из разных портов.
В настоящее время у меня есть цикл while(1)
, который вызывает accept()
, а затем порождает поток для обработки нового соединения. Каждое сообщение закрывается после чтения сообщения. На удаленном конце соединение закрывается после отправки сообщения.
Я хочу устранить накладные расходы на настройку и разрывание соединений путем кэширования открытых FD-фреймов. На стороне отправителя это легко - я просто не закрываю соединения и не поддерживаю их.
На стороне приемника это немного сложнее. Я знаю, что я могу сохранить FD, возвращенный accept()
в структуре, и прослушивать сообщения во всех таких сокетах с помощью poll()
или select()
, но я хочу одновременно одновременно слушать новые соединения через accept()
и прослушивать все кэшированные подключения.
Если я использую два потока, один на poll()
и один на accept()
, тогда, когда вызов accept()
возвращается (новое соединение открывается), я должен просыпать другой поток, ожидающий старого набора соединений. Я знаю, что могу сделать это с сигналом и pselect()
, но весь этот беспорядок кажется слишком сложным для чего-то такого простого.
Есть ли вызов или превосходная методология, которая позволит мне одновременно обрабатывать открытые соединения и отправлять данные по старым соединениям?
Ответы
Ответ 1
В прошлый раз, когда я проверил, вы могли бы просто listen
в сокете, а затем select
или poll
, чтобы узнать, произошло ли соединение. Если это так, accept
it; он не будет блокировать (но вы можете захотеть действительно должны установить O_NONBLOCK, чтобы быть уверенным)
Ответ 2
вы можете использовать прослушивание, затем использовать select или poll, а затем принять
if (listen (socket_fd, Number_connection) <0 )
{
perror("listen");
return 1;
}
fd_set set;
struct timeval timeout;
int rv;
FD_ZERO(&set); /* clear the set */
FD_SET(socket_fd, &set); /* add our file descriptor to the set */
timeout.tv_sec = 20;
timeout.tv_usec = 0;
rv = select(socket_fd + 1, &set, NULL, NULL, &timeout);
if(rv == -1)
{
perror("select"); /* an error accured */
return 1;
}
else if(rv == 0)
{
printf("timeout occurred (20 second) \n"); /* a timeout occured */
return 1;
}
else
client_socket_fd = accept (socket_fd,(struct sockaddr *) &client_name, &client_name_len);
Ответ 3
Я бы поставил слушателя в отдельный процесс (поток), чтобы не испортить вещи. И запустите рабочий процесс на другом, чтобы обрабатывать существующие сокеты. На самом деле нет необходимости в неблокирующем слушателе. И нить потоком не работает с двумя потоками.
Он должен работать следующим образом: вы принимаете поток слушателя до тех пор, пока он не представит дескриптор клиентского сокета и не передаст его работнику, который выполняет все грязные операции чтения/записи.
Если вы хотите прослушивать несколько портов и не хотите проводить один процесс на слушателя, я предлагаю вам установить ваш сокет в O_NONBLOCK и сделать что-то вроде:
// loop through listeners here and poll'em for read, when read is successful call accept, get descriptor, pass it to worker and continue listen
while(1){
foreach( serverSocket in ServerSockets ){
if( serverSocket.Poll( 10, SelectRead ) ){
clientSocket = serverSocket.Accept();
// pass to worker here and release
}
}
}