Java работа с сетью

Учимся программировать на Java с нуля

Сокеты по протоколу ТСР/IP служат для реализации надежных двунаправленных, постоянных, двухточечных, потоковых соединений между хостами в Интернете.

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

В Java поддерживаются две разновидности сокетов по протоколу ТСР /IP: один — для серверов, другой — для клиентов.

Класс ServerSocket служит "приемником", ожидая подключения клиентов прежде, чем предпринять какие-нибудь действия. Иными словами, класс ServerSocket предназначен для серверов, тогда как класс Socket — для клиентов.

Он служит для подключения к серверным сокетам и инициирования обмена данными по сетевому протоколу. Клиентские сокеты чаще всего применяются в прикладных программах на Java.

При создании объекта типа Socket неявно устанавливается соединение клиента с сервером.

Выявить это соединение нельзя никакими методами или конструкторами. Ниже перечислены два конструктора класса Socket, предназначенные для создания клиентских сокетов.

В классе 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Р-адрес и контактную информацию из веб-сайта, найденного по этому доменному имени.

Если запросить, например, сведения об адресе pro-java.ru, то будет получен результат, аналогичный следующему:

Эта программа на Java действует следующим образом. Сначала в ней создается объект типа Socket, обозначающий сокет и задающий имя хоста "whois.tcinet.ru" и номер порта 43.

Затем в сокете открываются потоки ввода-вывода. Далее формируется символьная строка, содержащая имя веб-сайта, сведения о котором требуется получить.

Если веб-сайт не указан в командной строке, то выбирается имя хоста "pro-java.ru". Эта символьная строка преобразуется в массив байтов и направляется в сеть через сокет.

После этого ответ читается из сокета, а резуль­тат выводится на экран. И наконец, сокет закрывается, а вместе с ним и потоки ввода-вывода. В данном примере сокет закрывается вручную в результате вызова метода close().

Если же используется комплект версии JDК 7, то для автомати­ческого закрытия сокета можно организовать блок оператора try с ресурсами. В качестве примера ниже приведен другой способ написать метод main() из рас­сматриваемой здесь программы, чтобы закрывать сокет автоматически.

В своем прикладном коде следует уделить внимание автоматическому управлению сетевыми ресурсами, по­скольку это более рациональный и гибкий подход.

Следует также иметь в виду,что в данной версии программы исключения все еще генерируются в теле метода main(), но их можно было бы перехватывать и обрабатывать, добавив операторы catch в конце блока оператора try с ресурсами.

Интересное видео на сегодня:

категория
Java
дата 25.12.2013
автор Vlad_Lastname
голосов 29

Теория — IP, порт, сокет

Если вы начали читать эту статью, то, скорее всего, имеете какое-то отношение к IT-и понимаете что такое IP-адрес – уникальный адрес, который определяет компьютер в сети.

Но достаточно ли такой адресации для полноценной работы? Предположим, что на некотором компьютере запущены одновременно две программы, которые взаимодействуют с интернетом – получают и/или отсылают какие-то данные. Программы никак между собой не связаны и общаются с разными интернет-сервисами. Но они расположены на одном компьютере, следовательно, имеют один IP-адрес. Если они одновременно должны получить данные от двух разных серверов, как же они определят, кому какие данные предназначались?

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

В нашем случае каждая из программ (точнее, программисты, написавшие их) должна определить, по какому порту она хочет взаимодействовать с сетью. Сервер, в свою очередь, должен тоже знать этот порт и посылать данные именно на него.

Что же собой представляет этот загадочный порт? Вы можете взять отвёртку и перебрать весь компьютер, но портов так и не найдёте. Это просто число, которое передаётся вместе с данными. Теоретически, оно может находиться в диапазоне от 1 до 65535, но порты 1..1024 используются системными программами и занимать их не стоит. Поэтому порт следует выбирать из диапазона 1025..65535.

Итак, IP-адрес это адрес компьютера в сети; порт – «адрес» программы на этом компьютере. А сокет – это их объединение, т.е. адрес программы в сети. Именно сокеты мы и будем использовать, чтобы указать программе, куда же стоит отправлять сообщения.

Планируем

Даже в таких простых программах, как чат, не нужно сразу лезть в IDEи писать что-то невнятное. Для начала стоит осмыслить теорию, с которой мы ознакомились (вы же её не пропустили, правда?) и понять, что она значит в контексте нашей программы.

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

  • Необходимо два режима работы программы – серверный и клиентский

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

Дальше самое время подумать об архитектуре. С двумя пользователями всё легко – они просто пересылают друг другу свои сообщения. Но как мы помним, чат у нас планируется многопользовательский. Хранить у каждого клиента список всех остальных пользователей бессмысленно (следует понимать, что в данном случае клиент и пользователь – одно и то же). Поэтому сделаем так:

  • Сервер будет один, а пользователей – любое количество. Каждый пользователь подключается к этому серверу и посылает ему свои сообщения. Сервер хранит список подключенных пользователей и рассылает им все полученные сообщения.

И последнее, с чем нужно разобраться это протокол – алгоритм взаимодействия программ в сети. Очевидно, что и сервер и клиент должны слать друг другу сообщения по одному алгоритму. Ведь если клиент отправит строку, в то время как сервер будет ждать число, они друг друга не поймут, и будут работать неправильно.

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

Написание программы

Теперь, когда все подготовительные момент ясны, можно преступить к самому интересному – написанию программы.

Файлы и структура пакетов

Вы, конечно, знаете, что любая программа на Javaначинается с метода main(String[] args). Для большей наглядности не будем добавлять его к другим классам, а создадим отдельный класс Mainи пакет для него – main. В любой программе наверняка будут какие-то константы. Я предпочитаю выносить их в отдельный файл в виде publicstaticполей, поэтому создам класс Constи также добавлю его в пакет main.

Как мы помним, программа должна работать в режиме клиента или сервера. Создадим два соответствующих класса Clientи Server.

В итоге дерево пакетов выглядит так:

Выбор режима работы

Для начала нужно выбрать, в каком режиме запускать программу – сервер или клиент. Это нам и нужно первым делом узнать у пользователя, поэтому в метод main(…) пишем следующее:

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

Режим клиента

Пойдём от простого к сложному и сначала реализует клиентский режим работы.

Если сервер просто запускается и ждём пользователей, то клиентам приходится проявлять некоторую активность, а именно – подключиться к серверу. Для этого нужно знать его IPи порт подключения. Порт является константой, поэтому зададим его в Const.java:

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

IPдолжен ввести пользователь, поэтому в конструкторе Client пишем:

Теперь у нас есть все необходимые данные – ip, порт, режим работы программы. Можно подключаться к серверу. Сначала создадим сокет:

При этом сразу же производится подключение и можно передавать и считывать данные. Но как это сделать, если данные передаются только через потоки? Каждый Socketсодержит входной и выходной потоки класса InputStreamи OutputStream. Можно работать прямо с ними, но лучше для удобства «завернуть» их во что-то более функциональное:

Любые операции с потоками и сокетами должны выполняться внутри блока try..catch, для того, чтобы обрабатывать ошибки.

Вот теперь можно начать обмен сообщениями с сервером. Как мы помним, по протоколу, нужно сначала передать имя пользователя а затем каждое введённое (в консоль) сообщение отправлять серверу. Так и сделаем:

Метод println() объекта outотправляет данные на сервер, а метод readLine() объекта in– считывает полученные данные. Но как нам печатать в консоль полученные от сервера сообщения? Ведь нам нужно одновременно ожидать сообщений из консоли (от пользователя) и сообщений из потока (от сервера). Придётся для этого создать дополнительную нить.

Thread– класс Java, который реализует такую незаменимую вещь, как многопоточность. Это возможность программы одновременно выполнять разные наборы действий. Как раз то, что нам сейчас нужно.

Создадим внутренний класс, который будет получать сообщения от сервера и выводить их в консоль.

До тех пор, пока поток не будет остановлен, он просто считывает все сообщения сервера и выводит их в консоль.

В конструкторе создадим объект этого класса и запустим поток:

Итоговый файл Client.java вместе с остальными приведён в конце статьи.

Режим сервера

Сервер, в отличие от клиента, работает не с классом Socket, а с ServerSocket. При создании его объекта программа никуда не подключается, а просто создаётся сервер на порту, переданном в конструктор.

Вся логика работы с конкретным пользователем будем находиться во внутреннем класса Connection, а Serverбудет только принимать новые подключения и оперировать существующими. Начнём «снизу» и создадим класс Connection, который должен в отдельной нити принимать от пользователя сообщения и рассылать их остальным клиентам:

connection это массив со всеми соединениями пользователей. Когда необходимо отправить какое-то сообщение всем, мы перебираем этот массив и обращаемся к каждому клиенту.

Конструктор, как и в классе Client, преобразовывает потоки, связанные с сокетом. Метод run() запускается в отдельной нити и выполняется параллельно с остальной частью программы. В нём, согласно протоколу, сначала считывается имя пользователя, а потом все остальные его сообщения рассылаются всем клиентам чата. Когда приходит сообщение “exit”, пользователь отключается от чата, а все связанные с ним потоки закрываются методом close().

Теперь осталось только создать сервер, который будет принимать подключения, создавать объекты Connectionи добавлять их в массив. В конструкторе класса Server пишем:

Метод server.accept() указывает серверу ожидать подключения. Как только какой-то клиент подключится к серверу, метод вернёт объект Socket, связанный с этим подключением. Дальше создаётся объект Connection, инициализированный этим сокетом и добавляется в массив. Не забываем про try..catchи в конце закрываем все сокеты вместе с потоками методом closeAll();

Исходники

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

Если Вам понравилась статья, проголосуйте за нее

Голосов: 29 Голосовать

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

Для решения этой задачи Java предоставляет различные механизмы, среди которых особое место занимают сокеты.

Сокет (англ. socket — разъём) — название программного интерфейса для обеспечения обмена данными между процессами. Процессы при таком обмене могут исполняться как на одной ЭВМ, так и на различных ЭВМ, связанных между собой сетью. Сокет — абстрактный объект, представляющий конечную точку соединения (Википедия).

Ключевое отличие сокетов от других сетевых инструментов Java (таких как HttpRequest, SMTPTransport и др.) состоит в том, что:

  • Сокеты представляют собой достаточно низкоуровневый интерфейс.
    Это позволяет работать напрямую через протокол TCP/IP и тем самым обеспечивает универсальность.
  • Сокеты позволяют обеспечить обмен данными в режиме достаточно приближенном к реальному времени.
    При отсутствии задержек при обработке и передачи данных обмен происходит с очень высокой скоростью.

Недостаток сокетов, по сути является продолжением их достоинств. Универсальность и работа на низком уровне неизбежно порождает неудобство при работе с распространёнными протоколами (того же HTTP) . Поэтому для них лучше использовать высокоуровневые средства. Но, подобные протоколы, к сожалению, не покрывают весь спектр задач сетевого программирования. Поэтому программирование на сокетах по-прежнему остаётся актуальным.

Ниже мы рассмотрим примеры создания и работы серверных и клиентских сокетов на примере несложного клиент-серверного приложения.

Серверная часть

Существует два вида сокетов. Серверные и клиентские. В этой части мы рассмотрим серверные сокеты.

Серверные сокеты реализуются на основе класса ServerSocket. Они прослушивают определённый порт и по получении данных от клиента могут сформировать и передать ответ.

Ниже приведён пример создания серверного сокета для 5000 порта.


[an error occurred while processing the directive]
Карта сайта