Что такое сокет Java

Обновлено: 06.07.2024

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

Программирование сокетов Java состоит из двух разделов.

Программа сокетов сервера Java

Программа Server Socket здесь представляет собой приложение на основе консоли Java. Эта программа действует как сервер и прослушивает запросы клиентов через порт № 8888.

Следующий шаг — создать бесконечный цикл для отслеживания запроса со стороны клиента и ответа на него со стороны сервера. Когда сокет сервера принимает запрос со стороны клиента, он считывает данные из DataInputStream, а также записывает ответ в DataOutputStream.

Пример сокета сервера

Программа клиентских сокетов Java

Клиент подключается к порту 8888 программы Java Server Socket и IP-адресу (имя компьютера) сервера. Здесь мы указываем 127.0.0.1, потому что сервер и клиент работают на одной машине. Если клиентская программа работает на другом компьютере, вы можете указать IP-адрес этого компьютера.

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

Пример сокет-клиента

Как запустить эту программу?

Когда вы закончите кодирование и скомпилируете серверную и клиентскую программу, сначала вам нужно запустить Java Server Socket Program из командной строки DOS (консоли), затем вы получите сообщение «Сервер запущен» на экране DOS, где сервер программа запущена.

Следующий шаг — запустить Java Client Socket Program на том же компьютере или на других компьютерах в той же сети. Когда вы запускаете клиентскую программу, она устанавливает соединение с сервером и ожидает ввода со стороны клиента. Когда вы вводите сообщение и нажимаете кнопку ENTER, вы можете увидеть такое же сообщение на стороне сервера. Получив сообщение со стороны клиента, вы можете отправить сообщение клиенту со стороны сервера. Когда клиент отправляет «до свидания» со стороны клиента, сервер закрывает соединение с клиентом.

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

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

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

На стороне клиента, если соединение принято, сокет успешно создается, и клиент может использовать сокет для связи с сервером.

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

Сокет — это конечная точка двустороннего канала связи между двумя программами, работающими в сети. Сокет привязан к номеру порта, чтобы уровень TCP мог идентифицировать приложение, которому предназначены данные для отправки.

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

Если вы пытаетесь подключиться к Интернету, класс URL и связанные с ним классы ( URLConnection , URLEncoder ), вероятно, более подходят, чем классы сокетов. На самом деле URL-адреса представляют собой относительно высокоуровневое соединение с Интернетом и используют сокеты как часть базовой реализации. Информацию о подключении к Интернету через URL-адреса см. в разделе Работа с URL-адресами.

Термин сетевое программирование относится к написанию программ, которые выполняются на нескольких устройствах (компьютерах), в которых все устройства подключены друг к другу с помощью сети.

TCP — TCP означает протокол управления передачей, который обеспечивает надежную связь между двумя приложениями. TCP обычно используется по протоколу Интернета, который называется TCP/IP.

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

Эта глава дает хорошее представление о следующих двух темах —

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

Обработка URL-адресов — это будет рассмотрено отдельно. Нажмите здесь, чтобы узнать об обработке URL-адресов на языке Java.

Программирование сокетов

Сокеты обеспечивают механизм связи между двумя компьютерами с использованием TCP. Клиентская программа создает сокет на своем конце соединения и пытается соединить этот сокет с сервером.

Когда соединение установлено, сервер создает объект сокета на своем конце соединения. Теперь клиент и сервер могут обмениваться данными, записывая и читая из сокета.

Следующие шаги выполняются при установлении TCP-соединения между двумя компьютерами с использованием сокетов —

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

Сервер вызывает метод accept() класса ServerSocket. Этот метод ожидает, пока клиент не подключится к серверу через заданный порт.

После ожидания сервера клиент создает экземпляр объекта Socket, указывая имя сервера и номер порта для подключения.

Конструктор класса Socket пытается подключить клиента к указанному серверу и номеру порта. Если связь установлена, у клиента теперь есть объект Socket, способный обмениваться данными с сервером.

На стороне сервера метод accept() возвращает ссылку на новый сокет на сервере, который подключен к сокету клиента.

После того, как соединения установлены, обмен данными может осуществляться с использованием потоков ввода-вывода. Каждый сокет имеет как OutputStream, так и InputStream. Клиентский OutputStream подключается к серверному InputStream, а клиентский InputStream подключается к серверному OutputStream.

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

Методы класса ServerSocket

Класс ServerSocket имеет четыре конструктора —

public ServerSocket(int port) генерирует исключение IOException

Попытка создать серверный сокет, привязанный к указанному порту. Исключение возникает, если порт уже привязан к другому приложению.

public ServerSocket(int port, int backlog) генерирует IOException

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

public ServerSocket(int port, int backlog, InetAddress address) выдает исключение IOException

Как и в предыдущем конструкторе, параметр InetAddress указывает локальный IP-адрес для привязки. InetAddress используется для серверов, которые могут иметь несколько IP-адресов, что позволяет серверу указывать, на каком из его IP-адресов принимать клиентские запросы.

public ServerSocket() генерирует исключение IOException

Создает несвязанный серверный сокет. При использовании этого конструктора используйте метод bind(), когда вы будете готовы привязать серверный сокет.

Если конструктор ServerSocket не генерирует исключение, это означает, что ваше приложение успешно выполнило привязку к указанному порту и готово к клиентским запросам.

Ниже приведены некоторые из распространенных методов класса ServerSocket —

public int getLocalPort()

Возвращает порт, который прослушивает серверный сокет. Этот метод полезен, если вы передали 0 в качестве номера порта в конструкторе и позволили серверу найти порт для вас.

public Socket accept() выдает исключение IOException

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

public void setSoTimeout(int timeout)

Устанавливает время ожидания, в течение которого сокет сервера ожидает клиента во время accept().

public void bind(хост SocketAddress, int backlog)

Связывает сокет с указанным сервером и портом в объекте SocketAddress. Используйте этот метод, если вы создали экземпляр ServerSocket с помощью конструктора без аргументов.

Когда ServerSocket вызывает accept(), метод не возвращается до тех пор, пока не подключится клиент. После того, как клиент подключается, ServerSocket создает новый сокет на неуказанном порту и возвращает ссылку на этот новый сокет. Теперь между клиентом и сервером установлено TCP-соединение, и можно начинать обмен данными.

Методы классов сокетов

Класс Socket имеет пять конструкторов, которые клиент использует для подключения к серверу —

public Socket(String host, int port) генерирует UnknownHostException, IOException.

Этот метод пытается подключиться к указанному серверу через указанный порт. Если этот конструктор не выдает исключение, соединение установлено успешно, и клиент подключается к серверу.

общедоступный сокет (хост InetAddress, порт int) генерирует исключение IOException

Этот метод идентичен предыдущему конструктору, за исключением того, что хост обозначается объектом InetAddress.

public Socket(String host, int port, InetAddress localAddress, int localPort) генерирует IOException.

Подключается к указанному хосту и порту, создавая сокет на локальном хосте с указанным адресом и портом.

общедоступный сокет (хост InetAddress, int port, InetAddress localAddress, int localPort) вызывает исключение IOException.

Этот метод идентичен предыдущему конструктору, за исключением того, что хост обозначается объектом InetAddress вместо строки.

общедоступный сокет()

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

Когда конструктор Socket возвращается, он не просто создает экземпляр объекта Socket, но фактически пытается подключиться к указанному серверу и порту.

Здесь перечислены некоторые интересные методы класса Socket. Обратите внимание, что и клиент, и сервер имеют объект Socket, поэтому эти методы могут вызываться как клиентом, так и сервером.

public void connect (хост SocketAddress, тайм-аут int) вызывает исключение IOException

Этот метод подключает сокет к указанному хосту. Этот метод необходим только при создании экземпляра Socket с помощью конструктора без аргументов.

публичный InetAddress getInetAddress()

Этот метод возвращает адрес другого компьютера, к которому подключен этот сокет.

общедоступный порт getPort()

Возвращает порт, к которому привязан сокет на удаленной машине.

public int getLocalPort()

Возвращает порт, к которому привязан сокет на локальном компьютере.

общедоступный адрес сокета getRemoteSocketAddress()

Возвращает адрес удаленного сокета.

public InputStream getInputStream() вызывает исключение IOException

Возвращает входной поток сокета. Входной поток подключается к выходному потоку удаленного сокета.

public OutputStream getOutputStream() вызывает исключение IOException

Возвращает выходной поток сокета. Выходной поток подключается к входному потоку удаленного сокета.

public void close() вызывает исключение IOException

Закрывает сокет, из-за чего этот объект Socket больше не может снова подключаться к какому-либо серверу.

Методы класса InetAddress

Этот класс представляет адрес интернет-протокола (IP). Вот следующие полезные методы, которые вам понадобятся при программировании сокетов —

статический InetAddress getByAddress(byte[] addr)

Возвращает объект InetAddress с необработанным IP-адресом.

статический InetAddress getByAddress(String host, byte[] addr)

Создает InetAddress на основе предоставленного имени хоста и IP-адреса.

статический InetAddress getByName(String host)

Определяет IP-адрес хоста по имени хоста.

Строка getHostAddress()

Возвращает строку IP-адреса в текстовом виде.

Строка getHostName()

Получает имя хоста для этого IP-адреса.

статический InetAddress InetAddress getLocalHost()

Возвращает локальный хост.

Строка toString()

Преобразует этот IP-адрес в строку.

Пример сокет-клиента

Приведенный ниже GreetingClient — это клиентская программа, которая подключается к серверу с помощью сокета и отправляет приветствие, а затем ожидает ответа.

Пример

Пример сокет-сервера

Следующая программа GreetingServer является примером серверного приложения, использующего класс Socket для прослушивания клиентов через номер порта, указанный аргументом командной строки —

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

Существует два протокола связи, которые мы можем использовать для программирования сокетов: протокол пользовательских дейтаграмм (UDP) и протокол управления передачей (TCP).

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

Это руководство представляет собой введение в программирование сокетов в сетях TCP/IP и демонстрирует, как писать клиент-серверные приложения на Java. UDP не является основным протоколом, поэтому его можно встретить нечасто.

2. Настройка проекта

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

Нам также нужен пакет java.io, который предоставляет входные и выходные потоки для записи и чтения во время связи:

Для простоты мы будем запускать клиентскую и серверную программы на одном компьютере. Если бы мы запускали их на разных сетевых компьютерах, единственное, что изменилось бы, — это IP-адрес. В этом случае мы будем использовать localhost на 127.0.0.1.

3. Простой пример

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

Мы создадим серверное приложение в классе GreetServer.java со следующим кодом.

Мы включим метод main и глобальные переменные, чтобы привлечь внимание к тому, как мы будем запускать все серверы в этой статье. В остальных примерах этой статьи мы не будем использовать повторяющийся код:

Мы также создадим клиент с именем GreetClient.java с помощью следующего кода:

Теперь давайте запустим сервер. В нашей среде IDE мы делаем это, просто запуская ее как приложение Java.

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

Этот пример дает нам представление о том, чего ожидать далее в статье. Таким образом, мы можем еще не до конца понять, что здесь происходит.

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

4. Как работают сокеты

Мы будем использовать приведенный выше пример, чтобы просмотреть различные части этого раздела.

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

4.1. Сервер

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

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

Когда код сервера встречает метод accept, он блокируется до тех пор, пока клиент не отправит ему запрос на подключение.

Если все в порядке, сервер принимает соединение. После принятия сервер получает новый сокет clientSocket, привязанный к тому же локальному порту 6666, а также устанавливает удаленную конечную точку на адрес и порт клиента. .

На этом этапе новый объект Socket устанавливает прямое соединение сервера с клиентом. Затем мы можем получить доступ к выходному и входному потокам для записи и получения сообщений от клиента соответственно:

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

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

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

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

4.2. Клиент

Клиент должен знать имя хоста или IP-адрес компьютера, на котором работает сервер, и номер порта, на котором сервер прослушивает.

Чтобы сделать запрос на подключение, клиент пытается связаться с сервером на машине сервера и порте:

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

Приведенный выше конструктор создает новый сокет только тогда, когда сервер принял соединение; в противном случае мы получим исключение отказа в соединении. После успешного создания мы можем получить от него потоки ввода и вывода для связи с сервером:

Входной поток клиента подключается к выходному потоку сервера точно так же, как входной поток сервера подключается к выходному потоку клиента.

5. Непрерывная связь

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

Таким образом, это полезно только для ping-запросов. Но представьте, что мы хотим реализовать чат-сервер; определенно потребуется постоянная связь между сервером и клиентом.

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

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

Обратите внимание, что мы добавили условие завершения, при котором цикл while завершается, когда мы получаем символ точки.

Мы запустим EchoServer с помощью основного метода, как мы это делали для GreetServer. На этот раз мы запускаем его на другом порту, например 4444, чтобы избежать путаницы.

EchoClient похож на GreetClient, поэтому мы можем продублировать код. Мы разделяем их для ясности.

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

Работа с несколькими клиентами — это другой случай, который мы рассмотрим в следующем разделе.

Теперь давайте создадим метод setup для установления соединения с сервером:

Мы также создадим метод tearDown, чтобы освободить все наши ресурсы. Это лучшая практика для каждого случая, когда мы используем сетевые ресурсы:

Затем мы протестируем наш эхо-сервер с помощью нескольких запросов:

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

6. Сервер с несколькими клиентами

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

В этом разделе мы рассмотрим работу с несколькими клиентами.

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

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

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

Основной поток будет запускать цикл while, ожидая новых подключений.

Теперь давайте посмотрим на это в действии. Мы создадим еще один сервер с именем EchoMultiServer.java. Внутри него мы создадим класс потока-обработчика для управления связью каждого клиента через его сокет:

Обратите внимание, что теперь мы вызываем accept внутри цикла while. Каждый раз, когда выполняется цикл while, он блокируется вызовом accept до тех пор, пока не подключится новый клиент. Затем для этого клиента создается поток обработчика EchoClientHandler.

В потоке происходит то же самое, что и в EchoServer, где мы обрабатывали только одного клиента. EchoMultiServer делегирует эту работу EchoClientHandler, чтобы он мог продолжать прослушивать больше клиентов в цикле while.

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

Давайте запустим наш сервер, используя его основной метод на порту 5555.

Для ясности мы по-прежнему помещаем тесты в новый набор:

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

7. Заключение

В этой статье мы сосредоточились на введении в программирование сокетов через TCP/IP и написали простое клиент-серверное приложение на Java.

Полный исходный код этой статьи можно найти в проекте GitHub.

Программирование на стороне клиента

Установите сокетное соединение

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

Закрытие соединения

Соединение сокета явно закрывается после отправки сообщения на сервер.

В программе клиент продолжает считывать ввод пользователя и отправляет его на сервер до тех пор, пока не будет введено «Over».

Реализация Java

Программирование сервера

Установить сокетное соединение

  • ServerSocket, ожидающий клиентских запросов (когда клиент создает новый Socket())
  • Старый простой сокет Socket для связи с клиентом.

Связь
Метод getOutputStream() используется для отправки вывода через сокет.

Закройте соединение.
После завершения важно закрыть соединение, закрыв сокет, а также потоки ввода/вывода.

  • Серверное приложение создает ServerSocket на определенном порту 5000. Это запускает наш сервер для прослушивания клиентских запросов, поступающих на порт 5000.
  • Затем сервер создает новый сокет для связи с клиентом.
  • Метод accept() блокируется (просто находится там) до тех пор, пока клиент не подключится к серверу.
  • Затем мы получаем входные данные из сокета, используя метод getInputStream(). Наш Сервер продолжает получать сообщения, пока Клиент не отправит «Over».
  • После завершения мы закрываем соединение, закрывая сокет и входной поток.
  • Чтобы запустить клиентское и серверное приложения на своем компьютере, скомпилируйте их оба. Затем сначала запустите серверное приложение, а затем клиентское приложение.

Для запуска в терминале или командной строке

Откройте два окна, одно для сервера, другое для клиента

<р>1. Сначала запустите серверное приложение как,

Сервер запущен
Ожидание клиента…

<р>2. Затем запустите клиентское приложение на другом терминале как,

Появится сообщение «Подключено», и сервер принимает клиента и показывает,
Клиент принят

<р>3. Затем вы можете начать вводить сообщения в окне клиента. Вот пример ввода для клиента

Который Сервер одновременно получает и показывает,

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

Читайте также: