2.1.6. Протокол TCP
2.1.6. Протокол TCP
Протокол TCP (Transmission Control Protocol — протокол управления передачей) является надежным потоковым протоколом с соединением, т. е. полной противоположностью UDP. Единственное, что у этих протоколов общее, — это способ адресации: в TCР каждому сокету также назначается уникальный номер порта. Уникальность номера порта требуется только в пределах протокола: два сокета могут иметь одинаковые номера портов, если один из них работает через TCP, а другой через UDP.
В TCP предусмотрены так называемые хорошо известные (well-known) порты, которые зарезервированы для нужд системы и не должны использоваться программами. Стандарт TCP определяет диапазон хорошо известных портов от 0 до 255, в Windows и в некоторых других системах этот диапазон расширен до 0–1023. Часть портов UDP тоже выделена для системных нужд, но зарезервированного диапазона в UDP нет. Кроме того, некоторые системные утилиты используют порты за пределами диапазона 0–1023. Полный список системных портов для TCP и UDP содержится в MSDN, в разделе Resource Kits/Windows 2000 Server Resource Kit/TCP/IP Core Networking Appendixes/TCP and UDP Port Assignment.
Для отправки пакета с помощью TCP отправителю необходимо сначала установить соединение с получателем. После выполнения этого действия соединенные таким образом сокеты могут использоваться только для отправки сообщений друг другу. Если соединение разрывается (самой программой из-за проблем в сети), эти сокеты уже непригодны для установления нового соединения: они должны быть уничтожены, а вместо них созданы новые сокеты.
Механизм соединения, принятый в TCP, подразумевает разделение ролей соединяемых сторон: одна из них пассивно ждет, когда кто-то установит с ней соединение, и называется сервером, другая самостоятельно устанавливает соединение и называется клиентом. Действия клиента по установке связи заключаются в следующем: создать сокет, привязать его к адресу и порту вызвать функцию для установки соединения, передав ей адрес сервера. Если все эти операции выполнены успешно, то связь установлена, и можно начинать обмен данными. Действия сервера выглядят следующим образом: создать сокет, привязать его к адресу и порту, перевести в режим ожидания соединения и дождаться соединения. При соединении система создаст на стороне сервера специальный сокет, который будет связан с соединившимся клиентом, и обмениваться данными с подключившимся клиентом сервер будет через этот новый сокет. Старый сокет останется в режиме ожидания соединения. и другой клиент сможет к нему подключиться. Для каждого нового подключения будет создаваться новый сокет, обслуживающий только данное соединение, а исходный сокет будет по-прежнему ожидать соединения. Это позволяет нескольким клиентам одновременно соединяться с одним сервером, а серверу — не путаться в своих клиентах. Точное число клиентов, которые могут одновременно работать с сервером, в документации не приводится, но оно достаточно велико.
Установление такого соединения позволяет осуществлять дополнительный контроль прохождения пакетов. В рамках протокола TCP выполняется проверка доставки пакета, соблюдения очередности и отсутствия дублей. Механизмы обеспечения надежности достаточно сложны, и мы их здесь рассматривать не будем. Программисту для начала достаточно знать, что данные, переданные с помощью TCP, не теряются, не дублируются и доставляются в том порядке, в каком были отправлены. В противном случае отправитель получает сообщение об ошибке. Соединенные сокеты время от времени обмениваются между собой специальными пакетами, чтобы убедиться в наличии соединения.
Если из-за неполадок в сети произошел разрыв связи, при попытке отправить данные или прочитать их клиент получит отказ, а соединение будет разорвано. После этого клиент должен уничтожить сокет, создать новый и повторить подключение. Сервер также получает ошибку на сокете, обслуживающем данное соединение, но существенно позже (эта задержка может достигать часа). При обнаружении ошибки сервер просто уничтожает сокет и ждет нового подключения от клиента. Возможна ситуация, когда клиент уже подключился заново и для него создан новый сокет, а старый сокет еще не закрыт. Это не является существенной проблемой — на старом сокете рано или поздно будет получена ошибка, и он будет закрыт. Тем не менее сервер может учитывать такую ситуацию и уничтожать старый сокет, не дожидаясь, пока на нем будет получена ошибка, если новое соединение с тем же клиентом уже установлено. На исходный сокет, находящийся в режиме ожидания подключения, физические разрывы связи никак не влияют, после восстановления связи никаких действий с ним проводить не нужно. Если на клиентской стороне не удалось для новою сокета установить соединение с сервером с первого раза (из-за отсутствия связи или неработоспособности сервера), этот сокет не обязательно уничтожать: он может использоваться при последующих попытках установления связи неограниченное число раз, пока связь не будет установлена.
Протокол TCP называется потоковым потому, что он собирает входящие пакеты в один поток. В частности, если в буфере сокета лежат 30 байтов, принятые по сети, не существует возможности определить, были ли эти 30 байтов отправлены одним пакетом, 30 пакетами по 1 байт, или еще как-либо. Гарантируется только то. что порядок байтов в буфере совпадает с тем порядком, в котором они были отправлены. Принимающая сторона также не ограничена в том, как она будет читать информацию из буфера: все сразу или по частям. Это существенно отличает TCP от UDP, в котором дейтаграммы не объединяются и не разбиваются на части.
Склеивание пакетов осуществляется не только принимающей, но и отправляющей стороной. Библиотека сокетов может придержать в выходном буфере то, что кладет программа, и потом отправить одним пакетом данные, которые программа складывала в буфер постепенно. И наоборот, данные большого объема могут быть отправлены по частям, поэтому возможна ситуация, когда принимающая сторона находит в своем буфере только часть сообщения, посланного своим визави. Это значит, что оставшаяся часть сообщения придет позже. Будут ли пакеты сливаться или разбиваться на части, зависит от пропускной способности и текущей загрузке сети, определяется это алгоритмом, который называется алгоритмом Нагла.
TCP применяется там, где программа не хочет заботиться о проверке целостности данных. За отсутствие этой проверки приходится растачиваться более сложной процедурой установления и восстановления связи. Если при использовании UDP сообщение не будет отправлено из-за проблем в сети или на удаленной стороне, никаких действий перед отправкой следующего сообщения выполнять не нужно и можно использовать тот же сокет. В случае же TCP, как уже было сказано, необходимо сначала уничтожать старый сокет, затем создать новый и подключить его к серверу, и только потом можно будет снова отправлять сообщения. Другим недостатком TCP по сравнению с UDP является то, что через один TCP-сокет все пакеты отправляются только по одному адресу, в то время как через UDP-сокет разные пакеты могут быть отправлены по разным адресам. И наконец, TCP не позволяет рассылать широковещательные сообщения. Но, несмотря на эти неудобства, TCP применяется существенно чаще UDP, потому что автоматическая проверка целостности данных и гарантия их доставки является очень важным преимуществом. Кроме того, TCP гарантирует более высокую безопасность в Интернете (это связано с тем, что обеспечить безопасность при передаче данных легче при наличии соединения, а не в ситуации, когда пакеты могут передаваться от кого угодно кому угодно).
То, что TCP склеивает данные в один поток, не всегда удобно. Во многих случаях пакеты, приходящие по сети, обрабатываются отдельно, поэтому читать их из буфера желательно тоже по одному. Это просто сделать, если все пакеты имеют одинаковую длину. Но при различной длине пакетов принимающая сторона заранее не знает, сколько байтов нужно прочитать из буфера, чтобы получить ровно один пакет и ни байта больше. Чтобы обойти эту ситуацию, в пакете можно предусмотреть обязательный заголовок фиксированной длины, одно из полей которого хранит длину пакета. В этом случае принимающая сторона может читать пакет по частям: сначала заголовок известной длины, а потом тело пакета, размер которого стал известен благодаря заголовку. Другой способ разделения пакетов — вставка между ними заранее оговоренной последовательности байтов, которая не может появиться внутри пакета.
Но самое неудобное то, что пакеты не только склеиваются, но и разбиваются на части. Принимающая сторона может получить пакет меньшего размера, чем отправленный, если этот пакет был послан по частям, и на момент его чтения принимающей стороной еще не все части были получены. Тогда приходится повторять операцию чтения данных, пока не будет получено все, что нужно.
В отличие от UDP, в TCP данные, которые программа отправляет одной командой, могут разбиваться на части и отправляться несколькими IP-пакетами. Поэтому ограничение на длину данных, отправляемых за один раз, в TCP отсутствует (точнее, определяется доступными ресурсами системы). Количество данных, получаемое отправителем за одну операцию чтения, ограничено размером низкоуровневого буфера сокета и может быть разным в различных реализациях. Следует иметь в виду, что при переполнении буфера принимающей стороны протокол TCP предусматривает передачу отправляющей стороне сигнала, по которому она приостанавливает отправку, причём этот сигнал прерывает всю передачу данных между этими двумя компьютерами с помощью TCP, т. е. это может повлиять и на другие программы. Поэтому желательно не допускать таких ситуаций, когда у принимающей стороны в буфере накапливается много данных.
Данный текст является ознакомительным фрагментом.