Логин:   Пароль:




Новости
Рассылки
Форум
Поиск


Java
- Апплеты
- Вопрос-ответ
- Классы
- Примеры
- Руководства
- Статьи
- IDE
- Словарь терминов
- Скачать

Мобильная Java
- Игры
- Примеры
- Статьи
- WAP, WML и пр.

JavaScript
- Вопрос-ответ
- Примеры
- Статьи

Веб-мастеринг
- HTML
- CSS
- SSI

Разминка для ума
Проекты
Книги
Ссылки
Программы
Юмор :)




Rambler's Top100

Java: СтатьиJava. HTTP протокол и работа с WEB

Java. HTTP протокол и работа с WEB


оглавление | далее

Работа с TCP/IP в Java. Сокеты

Итак, для начала немного теории. HTTP (Hyper Text Transfert Protocol) был изначально создан для пересылки HTML документов, отсюда и "заточка" этого протокола под работу с отдельными документами, преимущественно текстовыми. HTTP в своей работе использует возможности TCP/IP, поэтому рассмотрим возможности, предоставляемые java для работы с последним.

В джаве для этого существует специальный пакет "java.net", содержащий класс java.net.Socket. Socket в переводе означает "гнездо", название это было дано по аналогии с гнёздами на аппаратуре, теми самыми, куда подключают штепсели. Соответственно этой аналогии, можно связать два "гнезда", и передавать между ними данные. Каждое гнездо принадлежит определённому хосту (Host - хозяин, держатель). Каждый хост имеет уникальный IP (Internet Packet) адрес. На данный момент интернет работает по протоколу IPv4, где IP адрес записывается 4 числами от 0 до 255 - например, 127.0.0.1 (подробнее о распределении IP адресов тут - RFC 790, RFC 1918, RFC 2365, о версии IPv6 читайте тут - RFC 2373)

Гнёзда монтируются на порт хоста (port). Порт обозначается числом от 0 до 65535 и логически обозначает место, куда можно пристыковать (bind) сокет. Если порт на этом хосте уже занят каким-то сокетом, то ещё один сокет туда пристыковать уже не получится. Таким образом, после того, как сокет установлен, он имеет вполне определённый адрес, символически записывающийся так [host]:[port], к примеру - 127.0.0.1:8888 (означает, что сокет занимает порт 8888 на хосте 127.0.0.1)

TCP/IP: логическая структура соединений через сокеты
TCP/IP: логическая структура соединений через сокеты

Для того, чтобы облегчить жизнь, чтобы не использовать неудобозапоминаемый IP адрес, была придумана система DNS (DNS - Domain Name Service). Цель этой системы - сопоставлять IP адресам символьные имена. К примеру, адресу "127.0.0.1" в большинстве компьютеров сопоставленно имя "localhost" (в просторечье - "локалхост").

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

Клиентский сокет

Итак, вернёмся к классу java.net.Socket Наиболее удобно инициализировать его следующим образом:

public Socket(String host, int port) throws UnknownHostException, IOException
В строковой константе host можно указать как IP адрес сервера, так и его DNS имя. При этом программа автоматически выберет свободный порт на локальном компьютере и "привинтит" туда ваш сокет, после чего будет предпринята попытка связаться с другим сокетом, адрес которого указан в параметрах инициализации. При этом могут возникнуть два вида исключений: неизвестный адрес хоста - когда в сети нет компьютера с таким именем или ошибка отсутствия связи с этим сокетом.

Так же полезно знать функцию

public void setSoTimeout(int timeout) throws SocketException
Эта функция устанавливает время ожидания (timeout) для работы с сокетом. Если в течение этого времени никаких действий с сокетом не произведено (имеется ввиду получение и отправка данных), то он самоликвидируется. Время задаётся в секундах, при установке timeout равным 0 сокет становится "вечным".

Для некоторых сетей изменение timeout невозможно или установлено в определённых интервалах (к примеру от 20 до 100 секунд). При попытке установить недопустимый timeout, будет выдано соответственное исключение.

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

Сокет сервера

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

public ServerSocket(int port, int backlog, InetAddress bindAddr) throws IOException
Как видно, в качестве третьего параметра используется объект ещё одного класса - java.net.InetAddress Этот класс обеспечивает работу с DNS и IP именами, по этому вышеприведённый инициализатор в программах можно использовать так:
ServerSocket(port, 0, InetAddress.getByName(host)) throws IOException
Для этого типа сокета порт установки указывается прямо, поэтому, при инициализации, может возникнуть исключение, говорящее о том, что данный порт уже используется либо запрещён к использованию политикой безопасности компьютера.

После установки сокета, вызывается функция

public Socket accept() throws IOException
Эта функция погружает программу в ожидание того момента, когда клиент будет присоединяться к сокету сервера. Как только соединение установлено, функция возвратит объект класса Socket для общения с клиентом.

Клиент-сервер через сокеты. Пример

Как пример - простейшая программа, реализующая работу с сокетами.

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

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

логическая структура работы программ-примеров
Логическая структура работы программ-примеров

Программа простого TCP/IP клиента

(SampleClient.java)
import java.io.*;
import java.net.*;

class SampleClient extends Thread
{
    public static void main(String args[])
    {
        try
        {
            // открываем сокет и коннектимся к localhost:3128
            // получаем сокет сервера
            Socket s = new Socket("localhost", 3128);

            // берём поток вывода и выводим туда первый аргумент
            // заданный при вызове, адрес открытого сокета и его порт
            args[0] = args[0]+"\n"+s.getInetAddress().getHostAddress()
                            +":"+s.getLocalPort();
            s.getOutputStream().write(args[0].getBytes());

            // читаем ответ
            byte buf[] = new byte[64*1024];
            int r = s.getInputStream().read(buf);
            String data = new String(buf, 0, r);

            // выводим ответ в консоль
            System.out.println(data);
        }
        catch(Exception e)
        {System.out.println("init error: "+e);} // вывод исключений
    }
}

Программа простого TCP/IP сервера

(SampleServer.java)
import java.io.*;
import java.net.*;

class SampleServer extends Thread
{
    Socket s;
    int num;

    public static void main(String args[])
    {
        try
        {
            int i = 0; // счётчик подключений

            // привинтить сокет на локалхост, порт 3128
            ServerSocket server = new ServerSocket(3128, 0,
                    InetAddress.getByName("localhost"));

            System.out.println("server is started");

            // слушаем порт
            while(true)
            {
                // ждём нового подключения, после чего запускаем обработку клиента
                // в новый вычислительный поток и увеличиваем счётчик на единичку
                new SampleServer(i, server.accept());
                i++;
            }
        }
        catch(Exception e)
        {System.out.println("init error: "+e);} // вывод исключений
    }

    public SampleServer(int num, Socket s)
    {
        // копируем данные
        this.num = num;
        this.s = s;

        // и запускаем новый вычислительный поток (см. ф-ю run())
        setDaemon(true);
        setPriority(NORM_PRIORITY);
        start();
    }

    public void run()
    {
        try
        {
            // из сокета клиента берём поток входящих данных
            InputStream is = s.getInputStream();
            // и оттуда же - поток данных от сервера к клиенту
            OutputStream os = s.getOutputStream();

            // буффер данных в 64 килобайта
            byte buf[] = new byte[64*1024];
            // читаем 64кб от клиента, результат - кол-во реально принятых данных
            int r = is.read(buf);

            // создаём строку, содержащую полученную от клиента информацию
            String data = new String(buf, 0, r);

            // добавляем данные об адресе сокета:
            data = ""+num+": "+"\n"+data;

            // выводим данные:
            os.write(data.getBytes());

            // завершаем соединение
            s.close();
        }
        catch(Exception e)
        {System.out.println("init error: "+e);} // вывод исключений
    }
}

После компиляции, получаем файлы SampleServer.class и SampleClient.class (все программы здесь и далее откомпилированы с помощью JDK v1.4) и запускаем вначале сервер:

java SampleServer
а потом, дождавшись надписи "server is started", и любое количество клиентов:
java SampleClient test1
java SampleClient test2
...
java SampleClient testN

Если во время запуска программы-сервера, вместо строки "server is started" выдало строку типа

init error: java.net.BindException: Address already in use: JVM_Bind
то это будет обозначать, что порт 3128 на вашем компьютере уже занят какой-либо программой или запрещён к применению политикой безопасности.

Заметки

Отметим немаловажную особенность сокета сервера: он может принимать подключения сразу от нескольких клиентов одновременно. Теоретически, количество одновременных подключений неограниченно, но практически всё упирается в мощность компьютеров. Кстати, эта проблема конечной мощности компьютеров используется в DOS атаках на серверы: их просто закидывают таким количеством подключений, что компьютеры не справляются с нагрузкой и "падают".

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

Стоит упомянуть, что абстракцию Socket - ServerSocket и работу с потоками данных используют C/C++, Perl, Python, многие другие языки программирования и API операционных систем, так что многое из сказанного подходит к применению не только для платформы Java.


оглавление | далее


Х. М. Дейтел, П. Дж. Дейтел, С. И. Сантри
"Технологии программирования на Java 2. Книга 2. Распределенные приложения"
Подробнее>>
Заказать>>


Питер Вейнер
"Java и JavaScript"
Подробнее>>
Заказать>>

Узнай о чем ты на самом деле сейчас думаешь тут.


[an error occurred while processing this directive]



Apache Struts 2.0.11
Apache MyFaces Trinidad Core 1.2.3.
Sun переводит мобильные устройства с Java ME на Java SE
Хакерская атака!