IP адреса не достаточно для уникальной идентификации сервера, так как многие сервера могут существовать на одной машине. Каждая IP машина также содержит порты, и когда вы устанавливаете клиента или сервер, вы должны выбрать порт, через который и клиент, и сервер согласны соединиться.
Порт - это не физическое расположение в машине, а программная абстракция (в основном для целей учета). Клиентская программа знает, как соединится к машине через ее IP адрес, но как она может присоединится к определенной службе (потенциально, к одной из многих на этой машине)? Таким образом номер порта стал вторым уровнем адресации. Идея состоит в том, что при запросе определенного порта вы запрашиваете службу, ассоциированную с этим номером порта. Служба времени - простейший пример службы. Обычно каждая служба ассоциируется с уникальным номером порта на определенной серверной машине. Клиент должен предварительно знать, на каком порту запущена нужная ему служба.
Системные службы зарезервировали использование портов с номерам от 1 до 1024, так что вы не можете использовать этот или любой другой порт, про который вы знаете, что он задействован. Первым выбором, например, в этой книге будет порт 8080 (в память многоуважаемого 8-битного процессора 8080 от Intel в моем первом компьютере, CP/M машине).
Сокеты
Сокет - это программная абстракция, используемая для представления "терминалов" соединения между двумя машинами. Для данного соединения есть сокет на каждой машине, и вы можете представить гипотетический "кабель", включенный в сокет. Конечно, физическое оборудование и каблирование между машинами полностью неизвестно. Главное назначение абстракции состоит в том, что мы не должны знать более того, что нам необходимо.
В Java вы создаете сокет, чтобы создать соединение с другой машиной, затем вы получаете InputStream и OutputStream (или, с соответствующими конверторами, Reader и Writer) из сокета, чтобы получить возможность трактовать соединение, как объект потока ввода/вывода. Существует два класса сокетов, основанных на потоках: ServerSocket, который использует сервер для "прослушивания" входящих соединения, и Socket, который использует клиент для инициализации соединения. Как только клиент создаст сокетное соединение, ServerSocket возвратит (посредством метода accept( )) соответствующий Socket, через который может происходить коммуникация на стороне сервера. После этого вы общаетесь в соединении через Socket с Socket'ом и вы трактуете оба конца одинаково, посколько они и являются одним и тем же. На этой стадии вы используете методы getInputStream( ) и getOutputStream( ) для получения соответствующих объектов InputStream'а и outputStream'а для каждого сокета. Они должны быть обернуты внутрь буферных и форматирующих классов точно так же, как и другие объекты потоков, описанные в Главе 11.
Использование термина ServerSocket может показаться другим примером сбивающей с толку схемы именования в библиотеках Java. Вы можете подумать, что ServerSocket лучше было бы назвать "ServerConnector" или как-то подругому, без слова "Socket" внутри. Вы также можете подумать, что ServerSocket и Socket должны оба наследоваться от какого-то общего базового класса. На самом деле, два калсса имеют некоторые общие методы, но не настолько, чтобы дать им общий базовый класс. Вместо этого, работа ServerSocket'а состоит в том, чтобы ждать, пока некоторая машина не присоединится к нему, а затем он возвращает реальный Socket. Вот почему кажется, что ServerSocket назван немножко неправильно, так как его работа состоит не в том, чтобы быть реальным сокетом, а в том, чтобы создавать объект Socket'а, когда кто-то присоединяется к нему.
Однако, ServerSocket создает физический "сервер" или слушающий сокет на хост-машине. Этот сокет слушает входящие соединения, а затем возвращает "связанный" сокет (с определенными локальной и удаленной конечными точками) посредством метода accept( ). Сбивающая часть состоит в том, что оба эти сокета (слушающий и связанный) ассоциированы с одним и тем же серверным сокетом. Слушающий сокет может принять только новый запрос на соединение, а не пакет данных. Так что, не смотря на то, что ServerSocket имеет мало смыла с точки зрения программирования, в нем много смысла "физически".
Когда вы создаете ServerSocket, вы даете ему только номер порта. Вы не даете ему IP адрес, поскольку он уже есть на той машине, на которой он представлен. Однако когда вы создаете Socket, вы должны передать ему и IP адрес, и номер порта, к которому вы хотите присоединиться. (Однако Socket, который возвращается из метода ServerSocket.accept( ) уже содержит всю эту информацию.)
← | Тестирование программ без сети | Простейший сервер и клиент | → |