Неблокирующие сокеты и опросы poll () в прокси - новичок - PullRequest
0 голосов
/ 31 августа 2011

Я новичок в Си, и мой маленький проект - написать простой прокси SOCKS4.Благодаря помощи здесь я до сих пор использовал неблокирующие сокеты и poll () для своей рутины.Однако на данный момент у меня, кажется, есть 2 проблемы:

  1. Исходящий Socket dstSocket не закрывается, если входящий Socket rcvSocket закрывается, и наоборот.Я не проверяю это в цикле, но я не знаю как.Я попробовал POLLHUP как реванш, но, похоже, это не сработало.Нормальная проверка, кажется, возвращает ли recv () 0, но это также верно для неблокирующих сокетов?И если да, то как это работает с реваншами, я не могу понять, где бы это поставить, так как если бы POLLIN |POLLPRI установлены мне кажется recv () никогда не должен возвращать 0?Кроме того, я не понимаю, в чем точная разница между POLLIN и POLLPRI, мне кажется, просто проверка "данные доступны для чтения" в любом случае?

  2. Прокси, кажется, работаетдля соединений я тестировал с Netcat.Однако, если я использую браузер, он говорит (когда я нацеливаюсь на веб-сайт), хочу ли я сохранить «двоичные данные».Я проверил данные в wireshark, и то, что получено с сервера, корректно пересылается клиенту побайтно.Если у кого-то может быть идея, почему это может произойти с этой программой, было бы неплохо:)

Прикреплен соответствующий код (будьте осторожны, программист, новичок):

 fds[1].fd = dstSocket;
 fds[0].fd = rcvSocket;
 fds[1].events = POLLIN | POLLPRI | POLLHUP;
 fds[0].events = POLLIN | POLLPRI | POLLHUP;

 timer = poll(fds, 2, timeout_msecs); /* i dont use this yet */

 fcntl(rcvSocket, F_SETFL, O_NONBLOCK);
 fcntl(dstSocket, F_SETFL, O_NONBLOCK);

 while (1 == 1)
 {
     if (fds[0].revents & POLLIN | POLLPRI)
     {
        recvMsgSize = recv(rcvSocket, rcvBuffer, RCVBUFSIZE, 0);
        if (recvMsgSize > 0) {send(dstSocket, rcvBuffer, recvMsgSize, 0);}
     }
     if (fds[1].revents & POLLIN | POLLPRI)
     { 
        sndMsgSize = recv(dstSocket, sndBuffer, RCVBUFSIZE, 0);
        if (sndMsgSize > 0) { send(rcvSocket, sndBuffer, sndMsgSize, 0);}
     }         

     if ((fds[0].revents & POLLHUP) || (fds[1].revents & POLLHUP))
     {
        close(rcvSocket);
        close(dstSocket);
     }
} 

1 Ответ

1 голос
/ 31 августа 2011

recv() возвращает 0 при чистом удаленном отключении - это верно даже для неблокирующих сокетов. В этом случае будет возвращено POLLIN - уведомление о том, что удаленная сторона закрыла сокет, считается «читаемым» событием.

Вам не нужно использовать POLLPRI для соединений SOCKS / HTTP - это указывает на TCP «срочные данные», которые не используются этими протоколами (или даже вообще используются).


Помимо ваших прямых вопросов, вам нужно сделать больше для реализации надежного прокси:

  1. Вам нужно вызывать poll() в каждом цикле, а не только один раз. То, как вы это написали, это циклы занятости, что обычно не считается приемлемой практикой.
  2. Вы должны установить расположение SIGPIPE на игнорируемое с помощью signal(SIGPIPE, SIG_IGN);. Это позволяет корректно обрабатывать сбои записи.
  3. Вы должны проверить результат send(). Обратите внимание, что он может записать меньше запрошенной вами суммы - в этом случае вам придется хранить неотправленные буферизованные данные, вернуться к poll() и попытаться отправить оставшиеся данные еще раз, если на сокете было поднято POLLOUT. Вы хотите запросить POLLOUT, только если - это оставшихся неотправленных данных, поэтому вам нужно убедиться, что .events установлен правильно перед каждым poll() вызовом.
  4. Вы должны проверять errno, если recv() или send() возвращает значение меньше 0. EINTR и EWOULDBLOCK следует игнорировать; любая другая ошибка должна рассматриваться как сбой соединения.
  5. Вы не должны закрывать оба направления сразу после закрытия одного сокета - вы должны поддерживать асимметричные выключения. Это означает, что когда вы видите, что fds[0] был закрыт удаленным концом, вы должны позвонить shutdown(fds[1], SHUT_WR);, и наоборот; только когда оба были закрыты (или произошел сбой соединения), вы должны вызвать close() для обоих файловых дескрипторов и завершить работу.
...