Сокеты, порты, процессы и потоки

Оглавление

1. Как доставить пакет программе

Данные между компьютерами пересылаются с помощью пакетов: последовательностей байт, которые содержат информацию об отправителе, цели, а также могут содержать пользовательские данные.

Заметьте, я называю пакет именно последовательностью байт. Это важно, многие современные библиотеки скрывают от пользователя тот факт, что он отправляет байты, утверждая, что он отправляет строку или число, а иногда и вовсе экземпляр класса. Часто вам придётся отправлять данные системе, написанной с помощью другой библиотеки, в таких ситуациях вам нужно точно знать, как именно интерпретировать полученные пакеты и как их составлять. Подробнее в следующей статье.

Из прошлой статьи вы узнали, что для того, чтобы доставить пакет конкретной машине требуется либо находится в одной подсети и знать её серый IP адрес, либо она должна иметь белый IP адрес.

В реальности этого недостаточно, на одном компьютере чаще всего запущено более одного процесса: система, различные фоновые процессы и, конечно же, нужный нам процесс. Для того, чтобы доставить нашу информацию конкретному процессу, были придуманы порты.

Порт - это число, которое должно однозначно определить какому процессу на машине предназначается полученная информация. Программа, которая ожидает данные, слушает порт, когда адаптер обнаруживает новые данные, система оповещает слушающий процесс и тот их обрабатывает. Также отправляя данные, программа использует какой-то порт.

В итоге, в общем случае схема передачи данных между двумя компьютерами выглядит примерно так:

Теперь поясню этот рисунок. Допустим, нужный процесс на компьютере 1 хочет отправить информацию процессу на компьютере 2. Для этого он отправит пакет через порт 4, указав целью IP адрес компьютера 2 и порт номер 2.

Аналогично, если процесс на 2 компьютере хочет отправить пакет процессу на 1 компьютере, то он отправит его с порта 3, указав целью IP адрес компьютера 1 и порт 2.

2. Сокеты

Сейчас, когда описана вся теория по пересылке данных, возникает вопрос: "Как с этим работать, если я программист?". Для удобной работы с портами и пересылкой данных используют сокеты.

С точки зрения программиста, сокет - это некий объект, который может отправлять данные и/или принимать их.

Сокет можно привязать к какому-то порту и адаптеру, тогда процесс заберёт у системы этот порт, после привязки только этот процесс и, как правило, только этот сокет сможет использовать указанный порт. Процесс в забравший себе порт получает возможность принимать сообщения, приходящие на него.

Обычно, одновременно только один сокет может быть привязан к одному порту.

Как правило, сокеты поддерживают следующие операции:

  1. send(данные, адрес) #Отправка данных
  2. данные = recv(размер хранилища) #Получение данных
  3. bind(адрес) #Привязка сокета к указанному порту
  4. close() #Закрывает сокет для подключений и отвязывает, если он был привязан

При этом существуют 2 режима работы сокетов:

В блокирующем режиме, при вызове recv или другой операции ожидающий события, сокет будет бесконечно ожидать подключения. Если подключение так и не произойдёт, то программа просто зависнет. Но можно установить время ожидания, после которого сокет прервёт операцию и сообщит от ошибке. Иногда это определяют как отдельный режим работы сокета. В неблокирующем режиме сокет вернёт информацию об отсутствии пакета или сам пакет сразу при вызове recv.

3. Работа с множеством сокетов.

Из схемы выше видно, что даже для взаимодействия двух процессов может потребоваться более одного порта и, соответственно, более одного сокета. Использование неблокирующего режима может привести к потере пакетов. Для правильной работы вашего сервера требуются параллельные прослушивания, обработка и отправка пакетов.

Чаще всего эффективно работают схемы один поток на один сокет или один поток на два сокета, где первый сокет прослушивает порт, а другой используется для отправки данных.

Если более одного потока используют один сокет, могут возникать ошибки, а некоторые системы не различают потоки и процессы. Поэтому рекомендую создавать сокеты в том потоке, в котором планируете их использовать и пользоваться только в нём.

Если входящий трафик накатывает "волнами" есть смысл организовать "почтовый ящик". см. ddos.