Сокеты по протоколу ТСР/IP служат для реализации надежных двунаправленных, постоянных, двухточечных, потоковых соединений между хостами в Интернете.
Сокет может служить для подключения системы ввода-вывода в Java к другим программам, которые могут находиться как на локальной машине, таки на любой другой машине в Интернете.
В Java поддерживаются две разновидности сокетов по протоколу ТСР /IP: один — для серверов, другой — для клиентов.
Класс ServerSocket служит «приемником», ожидая подключения клиентов прежде, чем предпринять какие-нибудь действия. Иными словами, класс ServerSocket предназначен для серверов, тогда как класс Socket — для клиентов.
Он служит для подключения к серверным сокетам и инициирования обмена данными по сетевому протоколу. Клиентские сокеты чаще всего применяются в прикладных программах на Java.
При создании объекта типа Socket неявно устанавливается соединение клиента с сервером.
Выявить это соединение нельзя никакими методами или конструкторами. Ниже перечислены два конструктора класса Socket, предназначенные для создания клиентских сокетов.
1 2 3 4 5 |
// Создает сокет, подключаемый к указанному имени_хоста и порту Socket(String имя_хоста, int порт) throws UnknownHostException, IOException // Создает сокет, используя уже существующий объект типа InetAddress и указанный порт Socket(InetAddress IP-адрес, int порт) throws IOException |
В классе Socket определяется ряд методов экземпляра. Например, объект типа Socket может быть просмотрен в любой момент для извлечения сведений о связанных с ним адресе и порте. Для этого применяются методы, перечисленные ниже.
- InetAddress getInetAddress() — возвращает объект типа InetAddress, связанный с объектом типа Socket. Если же сокет не подключен, возвращается значение null
- int getPort() — возвращает удаленный порт, к которому привязан вызывающий объект типа Socket. Если же сокет не привязан, возвращается нулевое значение
- int getLocalPort() — возвращает локальный порт, к которому привязан вызывающий объект типа Socket. Если же сокет не привязан, возвращается значение -1
Для доступа к потокам ввода-вывода, связанным с классом Socket, можно воспользоваться методами getInputStream() и getOuptutStream(), перечисленными ниже.
Каждый из этих методов может сгенерировать исключение типа IOException, если сокет оказался недействительным из-за потери соединения.
Эти потоки ввода-вывода используются для передачи и приема данных таким же образом, как и потоки ввода-вывода.
- InputStream getInputStream() throws IOException — возвращает объект типа InetAddress, связанный с вызывающим сокетом
- OutputStream getOutputStream() throws IOException — возвращает объект типа OutputStream, связанный с вызывающим сокетом
Имеется и ряд других методов, в том числе метод connect(), позволяющий указать новое соединение; метод isConnected(), возвращающий логическое значение true, если сокет подключен к серверу; метод isBound(), возвращающий логическое значение true, если сокет привязан к адресу; а также метод isClosed(), возвращающий логическое значение true, если сокет закрыт.
Чтобы закрыть сокет, достаточно вызвать метод close(). Закрытие сокета приводит также к закрытию связанных с ним потоков ввода-вывода.
Начиная с версии JDК 7 класс Socket реализует также интерфейс AutoCloseabe. Это означает, что управление соке том можно организовать в блоке оператора try с ресурсами.
Приведенная ниже программа служит простым примером применения класса Socket. В этой программе устанавливается соединение с портом «whois» (номер 43) на Tcinet-cepвepe, посылается аргумент командной строки через сокет, а затем выводятся возвращаемые данные.
Tcinet-cepвep пытается интерпретировать аргумент как зарегистрированное доменное имя Интернета, а затем возвращает IР-адрес и контактную информацию из веб-сайта, найденного по этому доменному имени.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
// Продемонстрировать обращение с сокетами в Java import java.net.*; import java.io.*; class Whois { public static void main(String args[]) throws Exception { int c; // Создать сокетное соединение с веб-сайтом tcinet.ru // через порт 43 Socket s = new Socket("whois.tcinet.ru", 43); // получить потоки ввода-вывода InputStream in = s.getInputStream(); OutputStream out = s.getOutputStream(); // сформировать строку запроса String str = (args.length == 0 ? "pro-java.ru" : args[0]) + "\n"; // преобразовать строку в байты byte buf[] = str.getBytes(); // послать запрос out.write(buf); // прочитать ответ и вывести его на экран while((c = in.read()) != -1) { System.out.print((char) c); } s.close(); } } |
Если запросить, например, сведения об адресе pro-java.ru, то будет получен результат, аналогичный следующему:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
% By submitting a query to RIPN's Whois Service % you agree to abide by the following terms of use: % http://www.ripn.net/about/servpol.html#3.2 (in Russian) % http://www.ripn.net/about/en/servpol.html#3.2 (in English). domain: PRO-JAVA.RU nserver: dns2.fastdns24.org. nserver: dns3.fastdns24.eu. nserver: dns4.fastdns24.link. nserver: dns.fastdns24.com. state: REGISTERED, DELEGATED, VERIFIED person: Private Person registrar: REGRU-RU admin-contact: http://www.reg.ru/whois/admin_contact created: 2013-04-30T10:36:21Z paid-till: 2018-04-30T11:36:21Z free-date: 2018-05-31 source: TCI Last updated on 2017-04-20T11:56:32Z |
Эта программа на Java действует следующим образом. Сначала в ней создается объект типа Socket, обозначающий сокет и задающий имя хоста «whois.tcinet.ru» и номер порта 43.
Затем в сокете открываются потоки ввода-вывода. Далее формируется символьная строка, содержащая имя веб-сайта, сведения о котором требуется получить.
Если веб-сайт не указан в командной строке, то выбирается имя хоста «pro-java.ru». Эта символьная строка преобразуется в массив байтов и направляется в сеть через сокет.
После этого ответ читается из сокета, а результат выводится на экран. И наконец, сокет закрывается, а вместе с ним и потоки ввода-вывода. В данном примере сокет закрывается вручную в результате вызова метода close().
Если же используется комплект версии JDК 7, то для автоматического закрытия сокета можно организовать блок оператора try с ресурсами. В качестве примера ниже приведен другой способ написать метод main() из рассматриваемой здесь программы, чтобы закрывать сокет автоматически.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
// Использовать блое оператора try с ресурсами для закрытия сокета import java.net.*; import java.io.*; class Whois { public static void main(String args[]) throws Exception { int c; // Создать сокетное соединение с веб-сайтом tcinet.ru // через порт 43 try ( Socket s = new Socket("whois.tcinet.ru", 43) ) { // получить потоки ввода-вывода InputStream in = s.getInputStream(); OutputStream out = s.getOutputStream(); // сформировать строку запроса String str = (args.length == 0 ? "pro-java.ru" : args[0]) + "\n"; // преобразовать строку в байты byte buf[] = str.getBytes(); // послать запрос out.write(buf); // прочитать ответ и вывести его на экран while((c = in.read()) != -1) { System.out.print((char) c); } } // Теперь сокет закрыт } } |
В своем прикладном коде следует уделить внимание автоматическому управлению сетевыми ресурсами, поскольку это более рациональный и гибкий подход.
Следует также иметь в виду,что в данной версии программы исключения все еще генерируются в теле метода main(), но их можно было бы перехватывать и обрабатывать, добавив операторы catch в конце блока оператора try с ресурсами.