Расскажите в деталях как пользоваться неблокирующими сокетами
Собственно, сабж. Хочу повысить собственный навык network programming до уровня, достаточно для написания веб сервера, принимающего множество подключений.
Что удалось понять самостоятельно:
- Использование блокирующих сокетов и запись/чтение через классические read()/write() в потоке для каждого подключения имеет смысл только если сервер не рассчитан на большое кол-во подключения (ибо большое кол-во потоков будет жрать память) в пользу простоты кода.
- Использование неблокирующих сокетов имеет смысл если нужно принимать много подключений, и данных оттуда могут поступать не сразу/медленно/с задержкой. Посему можно набрать пачку подключений и потом poll()-ить их.
- Создание потока (posix threads) или процесса (fork()) - довольно затратная операция, и потому для отзывчивого и/или производительного сервера есть смысл создавать потоки/процессы заранее, а потом передавать им дескрипторы.
- Использование блокирующих вызовов ввода-вывода (т.е. write() и read()) не позволяет добиться информации о ошибках типа «потеря соединения» или «network is unreachable», и потому приходится использовать неблокирующие сокеты и соответствующие вызовы.
Что не удалось понять и требуются пояснения:
- Как передать дескриптор уже существующему потоку? Я так понял, что никаких спец. техник не нужно, поток просто должен знать номер и всё.
- Для передачи дескриптора уже существующему процессу (который создали при помощи fork()) нужно что-то шаманить с sendmsg(). Дальше я не копал. Судя по всему, этим не особо активно пользуются в современном софте, хотя хз. К слову, я так и не понял чем именно sendmsg() заставляет получить дескриптор в процессе, а не просто передаёт информацию.
- Если send() и другие функции из той же семьи возвращают кол-во переданных/полученных байт, то как они могут что-то вернуть, если они сразу возвращают управление? Что они возвращают в таком случае?
- Можно ли использовать буфер сразу после того, как send() вернул управление? Если да, то он, получается, сначала копирует буфер в пространство ядра, а потом возвращает управление (т.е. send() всё-таки не мгновенно возвращает, лол). Если нет, то когда можно снова использовать буфер? Нужно poll()-ить и ждать пока poll() вернёт результат по соответствующему дескриптору?
- Если poll()-ить менее пары десятков дескрипторов, можно ли забить болт на epoll() или kqueue()?
- Есть ли какой-то годный туториал (от простого к сложному, можно и на английском), который бы посвятил бы во всякие тонкости типа тех, которые будут в следующем пункте:
- Если poll() сообщает, что есть входящие данные по дескриптору, но чтение их возвращает 0, означает ли это, что удалённый хост просто сделал close()?
- Зачем нужен shutdown(), если есть обычный close()?