Открыть сокет на localhost

Обновлено: 07.07.2024

Ты уже знаешь, что у каждого компьютера в сети есть его уникальный IP-адрес.

И эти программы хотят общаться между собой.

Надо сделать так, чтобы они друг другу «не мешали». Чтобы Skype связывался со Skype’ом, ICQ с ICQ и т.д.

Помнишь, как это проблему решили с URL и веб-серверами?

Это все равно, что в доме сделать много маленьких комнат и объявить его многоквартирным. Каждый порт – это как отдельная квартира.

Так вот IP-адрес – это уникальный номер компьютера, а IP-адрес+порт – это уникальный номер некой «квартиры» (ячейки) в компьютере, которую может занять программа.

Вот такая уникальная ячейка и называется сокет.

У сокета есть его уникальный номер, и с остоит он из IP-адреса и номера порта.

Сокеты – это фактически самый базовый и самый примитивный способ межсетевого взаимодействия программ.

В Java для работы с сокетами есть два класса. Это классы Socket и ServerSocket.

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

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

Вот тут можешь почитать дополнительную информацию.

Теперь мы разберемся, как работает серверный сокет.

Он работает чуток посложнее.

Хочу обратить твое внимание на несколько моментов.

Момент 1: Для создания сокета нужно указывать IP-адрес (или домен) и порт. Для создания серверного сокета – только порт. Серверный сокет появляется только на том компьютере, где его создали.

Момент 2: У класса ServerSocket есть метод accept(), который, если его вызвать, будет ждать входящее соединение. Т.е. метод будет выполняться бесконечно долго, пока какой-то клиентский сокет не попробует обратиться к нему. Тогда метод accept() примет соединение, создаст объект сокет для коммуникации и после этого вернет этот объект.

Когда ты создаешь серверный сокет, фактически появляется порт, к которому могут подключаться сокеты с других компьютеров. Но для этого им надо правильно указать номер порта нашего сокета и IP-адрес нашего компьютера. Ну, или его доменное имя.

Вот тебе интересный пример, можешь попробовать разобрать его и выполнить:

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

Программы для работы с WEB.

Если всё пройдёт нормально, сервер devresource.org доступен и работает сеть, через некоторое время вы получите в той же директории файл "testreply.txt", содержащий ответ сервера, включая HTTP заголовок и содержимое документа. Файл "testreply.txt" вы сможете посмотреть в любом текстовом редакторе.

Как создать мэнеджер докачки.

вначале методом HEAD получаем всю доступную информацию о файле:

из полученного ответа выделяем значение параметра

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

если полученный ответ содержит код "206 Partial Content", то всё, что содержится под заголовком, и будет запрашиваемым куском файла.

Простой WEB сервер.

  • сервер будет принимать заголовки длиною не более 64 кб
  • сервер понимает только методы GET и POST, в противном случае выдаётся "400 Bad Request"
  • сервер не сможет выдавать документ по-частям (для мэнеджеров докачек)
  • допустимые коды возвратов для сервера ограничиваются "200 OK", если он удачно обработал запрос, "400 Bad Request", если запрос не понят сервером и "404 Not Found", если запрос понят, но файл не найден
  • результатом работы сервера, будет выдача файла, указанного в запросе. При этом всё, что находится после символа "?" в URI документа и сам этот символ отсекаются.
  • MIME типы, выдаваемые сервером ограничены 5 значениями: text/html для файлов с расширениями htm и html, image/jpeg, image/gif, image/x-xbitmap для файлов с расширениями jpg, gif, bmp соответственно и text/plain для всех остальных файлов

Программа SimpleWEBServer была создана как модификация SampleServer из первой части статьи:

Компилируем программу и получаем SimpleWEBServer.class. Так как данная программа была написана с использованием Java 2 API (в части получения даты в формате GMT), то для её компиляции и выполнения нужен JDK версии не ниже 1.2. Данный сервер будет ставиться на localhost:80, но, в принципе, можно использовать любой другой свободный порт и адрес хоста.

Запускаем сервер: Если программа написала "server is started", то сервер запущен и готов к работе. В противном случае, будет выдана ошибка (скорее всего, что даный порт занят другой программой или запрещён политикой безопасности).

Проверьте сервер: положите в директорию, где находится программа файл "index.html". Файл может быть, допустим, таким: Теперь откройте браузер и наберите адрес "http://localhost" или "http://localhost/index.html". Страничка должна отобразиться.

Proxy серверы.

Proxy (proxy - заместитель, посредник) серверы (в просторечье - просто "прокси" или "прокси сервер") - это узловые станции интернета. Они отвечают за соединение различных сегментов интернета меджду собою, а так же могут выполнять несколько других полезных действий. Хотя функционально различные варианты прокси перекрывают друг друга, всё же можно выделить несколько их основных типов.

Первый тип прокси - это так называемый "шлюз". Как было сказанно в первой части статьи, IP адрес для каждой TCP/IP сети должен быть уникальным. Каждый IP в сети интернет тоже уникален, по этому возникает закономерный вопрос подключения частной локальной сети к сети интернет. Этот вопрос как раз и решают шлюзы (они же proxy-gate). Программа такого прокси устанавливается на одном из серверов внутренней сети, имеющий выход в Internet. Разберём принцип работы такого прокси.

  1. клиент открывает сокет локальной сети до прокси сервера ("127.0.0.2:3128")
  2. в открытый сокет клиент пишет HTTP запрос примерно следующего содержания:
  3. прокси сервер получает этот запрос, из параметра "Host" узнаёт хост ресурса, его порт и открывает сокет сети Internet до сервера "www.devresource.org:80"
  4. в открытый сокет, прокси-сервер пишет полученый от клиента HTTP запрос; фактически он перенаправляет запрос от клиента к серверу, не изменяя его.
  5. сервер запрашиваемого ресурса получает HTTP запрос от шлюза, обрабатывает его и высылает ответ обратно, к прокси серверу
  6. шлюз получает ответ от сервера "www.devresource.org:80" и, не изменяя его, отправляет к клиенту
  7. клиент получает ответ от сокета прокси сервера и побрабатывает его

Для лучшего понимания того, что пошагово описано выше, прилагаю схему, иллюстрирующую процесс шлюзования запроса:


Схема работы шлюза.

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

Анонимный прокси всё так же получает запрос от клиента, обрабатывает поле "Host", передаёт запрос серверу и возвращает его ответ. Единственное отличие в том, что и ServerSocket, и Socket до указанного хоста лежат в одной сети. Собственно, анонимный прокси является самым простым типом прокси-серверов.

Ещё одним типом прокси являются так называемые "Firewall" (firewall - огненная стена, в просторечье - файрвол). Это модули системы защиты компьютеров и локальных сетей. Для HTTP суть этих модулей сводится к тому, что они фильтруют нежелательный контент. Например рассмотрим принцип работы простейшего HTTP Firewall, отсекаюего загрузку любых не-текстовых документов и запрещающий запрос страничек, URL которых содержит в себе ключевые слова "sex", "chat" и т.д. У многих на работе стоят подобные файрволы (обычно они функционально совмещены с шлюзами). Зная, как они работают, можно попытаться обойти их.

  1. клиент открывает сокет локальной сети до прокси сервера и отправляет ему заголовок
  2. файрвол обрабатывает HTTP заголовок запроса: выделяет URL ресурса и сканирует его на наличие "запретных" слов.
    • если слова найдены - возвращает клиенту ошибку типа "403 Forbidden" и завершает с ним соединение
    • если URL ресурса "в порядке", то соединяется с указанным хостом и передаёт ему запрос, в котором подменяет метод запроса на "HEAD". Например так:
  3. прокси получает часть ответа запрашиваемого сервера и обрабатывает HTTP заголовок ответа: выделяет поле "Content-Type", читает MIME тип документа (по умолчанию, если заголовок "Content-Type" опущен, то считается, что MIME тип - "text/html").
    • если заголовок содержит код возврата отличный от "200 OK", прокси создаёт страничку с информацией об ошибке и отправляет её к клиенту.
    • Если заголовок содержит MIME типа "image/gif" - то есть класса "image/", то в ответ выдаётся заранее заготовленная GIF картинка, содержащая прозрачный пиксел: Впрочем, прокси может просто выдать ошибку типа "403 Forbidden" - всё зависит от качества программы.
    • если заголовок содержит MIME типа "text/html" - то есть класса "text/", то прокси перенаправляет запрос клиента серверу, после чего перенаправляет ответ сервера к клиенту (работает как простой посредник)
    • во всех остальных случаях к клиенту возвращается страничка с ошибкой "403 Forbidden"

Рассмотрим ещё один тип прокси, называемый кэширующим (cache-proxy).

Что это значит кэширующий прокси? Это значит, что такой прокси сохраняет некоторые странички к себе в память, а потом, если от клиента придёт запрос на эту страничку, файл будет загружён не из сети, а из кэша - области памяти, куда прокси сохранил страничку.

Зачем это нужно? Данный принцип значительно сокращает траффик, ведь стоит одному клиенту обратиться, скажем, к "http://www.devresource.org/", как страничка окажется в кэше и для всех следующих клиентов, работающих через данный прокси и запрашивающих "http://www.devresource.org/", серверу не нужно будет снова загружать эту страничку из сети: достаточно будет просто достать её из кэша.

  • "public" - документ является публичным, его может брать любой клиент из кэша
  • "private" - документ является приватным, из кэша его может брать только клиент, пославший этот запрос
  • "no-store" - не сохранять документ в кэш
  • "no-transform" - если в кэшэ уже находится документ по данному запросу, то его не нужно обновлять ответом на этот запрос сервера
  • "must-revalidate" - в любом случае, лежит этот документ в кэшэ или нет, прокси обязан обновить его на тот, что выдаст сервер
  • "proxy-revalidate" - относительно прокси-сервера означает то же самое
  • "max-age=[seconds]" - количество секунд, которое должен храниться этот документ в кэшэ, начиная от данного момента

И, как вы помните, значения полей можно совмещать, к примеру: будет обозначать, что прокси обязан сохранить этот документ в кэш, причём только для этого клиента. И что по истечении суток от данного момента (через 86400 секунд) документ из кэша должен быть удалён.

Внимание! Для прокси-сервера приоритетным является поле "Cache-Control". В случае противоречивых данных в полях "Cache-Control" и "Pragma", прокси сервер будет выполнять команды первого поля, а браузер будет конфигурировать свой кэш в соответствии с полем "Pragma".

Простой кэширующий прокси.

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

  • длина HTTP запроса от клиента не должна превышать 64 кб (чтобы не обрабатывать поле "Content-Length" при POST запросах)
  • обрабатываются только поле "Pragma" из HTTP заголовка запроса; обработка эта заключается в поиске параметра "no-cache"
  • кэш прокси хранится в форме файлов-директорий. К примеру страничка с URL "http://www.devresource.ru/javalinks/catalog.php3?val=this a test!" в системе Windows будет сохранена как ".\cache\www.devresource.org\!javalinks\catalog.php3\val=this%20a%20test%21" - как видно, символы "/" и "?" заменяются, соответственно, на "\!" и "\" (вместо "\" может быть любой символ разделителя пути - берётся java.io.File.separatorChar), а служебный символ "!" обозначающий директорию в кэше и непечатные символы - на %NN, в соответствие с кодом символа.

Программа CacheProxy была тоже создана как модификация SampleServer из первой части статьи:

После компиляции программы, получаем CacheProxy.class и запускаем его (перед запуском убедитесь, что порт 3128 на вашем localhost свободен): Если выдана строка "proxy is started", то прокси был успешно запущен.

Теперь можно проверить его в действии: в настройках своего браузера найдите секцию, где прописываются Proxy сервера и укажите следующий HTTP прокси - имя localhost, порт - 3128. Теперь откройте браузер и немного поползайте по WEB ресурсам. Из-за того, что программа имеет ограниченную функциональность, некоторые ресурсы могут не открыться или выдать ошибку. После завершения работы, откройте в той же директории, где лежит программа, папку "cache" и посмотрите, как прокси сохранил просмотренные вами странички.

Java

Клиент-серверная архитектура — наиболее распространенная структура приложений в Интернете. В этой архитектуре клиенты (т.е. персональные компьютеры, устройства Интернета вещей и т. д.) сначала запрашивают ресурсы с сервера. Затем сервер отправляет обратно соответствующие ответы на запросы клиентов. Чтобы это произошло, должен быть какой-то механизм, реализованный как на стороне клиента, так и на стороне сервера, который поддерживает эту сетевую транзакцию. Этот механизм называется коммуникацией посредством сокетов.

Почти каждое приложение, которое полагается на сетевые операции, такие как извлечение данных с удаленных серверов и загрузка файлов на сервер, широко использует сокеты “под капотом”. Несколько примеров таких приложений — браузеры, чат-приложения и одноранговые сетевые приложения.

В этой статье мы более подробно рассмотрим сокеты и простую клиент-серверную реализацию с использованием сокетов в Java.

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

Что такое сокет?

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

Как работают сокеты?

TCP-сокет устанавливает связь между клиентом и сервером в несколько этапов.


Блок-схема коммуникации TCP-сокета
  • Socket() — на сервере создается конечная точка для коммуникации.
  • Bind() — сокету присваивается уникальный номер и для него резервируется уникальная комбинации IP-адреса и порта.
  • Listen() — после создания сокета сервер ожидает подключения клиента.
  • Accept() — сервер получает запрос на подключение от клиентского сокета.
  • Connect() — клиент и сервер соединены друг с другом.
  • Send()/Recieve() — обмен данными между клиентом и сервером.
  • Close() — после обмена данными сервер и клиент разрывают соединение.

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

Реализация коммуникации посредством TCP-сокетов в Java

Давайте посмотрим, как мы можем реализовать коммуникацию сокетов в Java. Мы сейчас напишем две Java-программы. Одной будет программа, запущенная на сервере, а другой — клиентская программа, которая будет взаимодействовать с сервером.

Реализация серверного сокета

В приведенной выше программе сервер открывает сокет с порта 50001 на серверной машине и ожидает клиента на server.accept() . После подключения клиента создается экземпляр выходного потока. Это может быть использовано для отправки данных с сервера на подключенный клиент. Именно это и делает serverOutput.writeBytes() . После отправки данных соединение с клиентом завершается.

Теперь давайте создадим клиент для взаимодействия с серверным сокетом, созданным выше.

Реализация клиентского сокета

Запуск программ

Сначала запустите серверную Java-программу, а затем клиентскую Java-программу (потому что сервер уже должен работать для подключения клиента). Вы увидите Received data: Java Revisited в терминале, где работает клиентская программа. Вот что здесь произошло: серверная программа отправила данные клиенту по запросу, а клиентская программа вывела их на терминал.

В этой статье мы обсудили, что такое сокеты и Java-реализация связи TCP-сокетов.

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


Что это

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

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


Рассмотрим это на простом примере. Представим себе большой зал с множеством небольших окошек, за которыми стоят девушки. Есть и пустые окна, за которыми никого нет. Те самые окна — это порты. Там, где стоит девушка — это открытый порт, за которым стоит какое-то приложение, которое его прослушивает. То есть, если, вы подойдете к окошку с номером 9090, то вас поприветствуют и спросят, чем могут помочь. Так же и с сокетами. Создается приложение, которое прослушивает свой порт. Когда клиент устанавливает соединение с сервером на этом порту именно данное приложение будет ответственно за работу этим клиентом. Вы же не подойдете к одному окошку, а кричать вам будут из соседнего :)

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

Сервер

Сейчас создайте два файла — один для сервера, а другой для клиента.

В Python для работы с сокетами используется модуль socket:

Прежде всего нам необходимо создать сокет:

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

Теперь нам нужно определиться с хостом и портом для нашего сервера. Насчет хоста — мы оставим строку пустой, чтобы наш сервер был доступен для всех интерфейсов. А порт возьмем любой от нуля до 65535. Следует отметить, что в большинстве операционных систем прослушивание портов с номерами 0 — 1023 требует особых привилегий. Я выбрал порт 9090. Теперь свяжем наш сокет с данными хостом и портом с помощью метода bind, которому передается кортеж, первый элемент (или нулевой, если считать от нуля) которого — хост, а второй — порт:

Теперь у нас все готово, чтобы принимать соединения. С помощью метода listen мы запустим для данного сокета режим прослушивания. Метод принимает один аргумент — максимальное количество подключений в очереди. Напряжем нашу бурную фантазию и вспомним про зал с окошками. Так вот этот параметр определяет размер очереди. Если он установлен в единицу, а кто-то, явно лишний, пытается еще подстроится сзади, то его пошлют :) Установим его в единицу:

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

Вот и все. Теперь мы установили с клиентом связь и можем с ним «общаться». Т.к. мы не можем точно знать, что и в каких объемах клиент нам пошлет, то мы будем получать данные от него небольшими порциями. Чтобы получить данные нужно воспользоваться методом recv, который в качестве аргумента принимает количество байт для чтения. Мы будем читать порциями по 1024 байт (или 1 кб):

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

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

Теперь можно и закрыть соединение:

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

Клиент

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

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

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