powered by simpleCommunicator - 2.0.61     © 2026 Programmizd 02
Целевая тема:
Создать новую тему:
Автор:
Закрыть
Цитировать
Форумы / Java [игнор отключен] [закрыт для гостей] / IO/NIO WTF
25 сообщений из 85, страница 1 из 4
IO/NIO WTF
    #38415908
Фотография Blazkowicz
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Понадобилось написать TCP "прокси", чем-то аналогичный вот этой тулзе
http://ws.apache.org/tcpmon/tcpmontutorial.html
С сокетами знаком исключительно по форумам, поэтому решил заполнить этот пробел ограничившись для начала Java API.
В качестве тестового варианта написал простейший насос данных в одну и другую сторону на plain IO.
WTF#1 - метод InputStream.available() бесполезен и даже сломан для сокетов.
Читаем JavaDoc
Returns:an estimate of the number of bytes that can be read (or skipped over) from this input stream without blocking or 0 when it reaches the end of the input stream.

На сокетах available будет изначально возвращать 0, если узел с другой стороны ничего не отправляет. Но это ещё не значит что поток завершен.
WTF#2 Дальше хуже. read(), который должен вернуть -1 по окончании чтения тоже никогда -1 не возвращает. В общем случае никакого начала и конца у InputStream для сокетов нет. А использовать available для хоть какой-то эмуляции неблокируемости тоже не выйдет.
Кое как заборол. Работает на браузере.

Начал тестировать на боевом приложении, WTF#3
Клиент отправляет пакет данных. Он прочитан, перенаправлен на другой сервер. После этого мой серверный сокет снова пытается сделать read(). В теории он должен либо заблокироваться, либо вернуть -1.
На практике чтение выкидывает исключение Connection reset! Но это пол беды. Помимо этого клиентская сторона закрывает свой сокет и на запись. Т.е. отклик сервера записать проигнорировав reset уже нельзя.
WTF#4 - не смотря на connection reset и невозможность дальнейшей работы с клиентом со стороны сервера, никакие флаги у сокета не меняются. Всякие connected, closed и пр. находятся в том же состоянии что и в начале работы. Т.е. спросит клиента с сервера живо ли тот ещё никакой возможности не видно.

Психанул. Переписал всё на NIO без селекторов. Т.е. тот же многопоточный подход что и в IO, только через NIO API. И, о чудо!
Работает. Причем независимо от параметра configureBlocking().
Никаких проблем с чтением после первого пакета почему-то нет. Всё до окончания сессии обмена пакетами работает более менее.

WTF#5 - я вообще смотрю на разные проекты и вижу что постоянные исключения в TCP на Java это вообще штатные ситуации. Имеет ли смысл избегать логирования stacktrace для повышения производительности? Ведь если разворачивать stacktrace на каждый пук при многочисленых соединениях и отсоединениях клиентов, то провал в производительности обеспечен.

И напоследок вопрос про NIO - как лучше всего избегать холостых циклов чтения, когда данные ещё не отправленый второй стороной. Они почему-то возникают даже при configureBlocking(true). Перейти исключительно на селекторы? Варианты с Thread.sleep(n) как-то не улыбаются.

Комментарии и нравоучения приветствуются.
...
Рейтинг: 0 / 0
IO/NIO WTF
    #38415965
Лагман
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Можно селектор использовать http://tutorials.jenkov.com/java-nio/selectors.html
...
Рейтинг: 0 / 0
IO/NIO WTF
    #38415970
Фотография Blazkowicz
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
ЛагманМожно селектор использовать http://tutorials.jenkov.com/java-nio/selectors.html
Прочитано несколько раз. Спасибо.
...
Рейтинг: 0 / 0
IO/NIO WTF
    #38415974
Андрей Панфилов
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Blazkowicz,

относительно 1-3. На транспортном вы не можете знать сколько байт должен в итоге передать клиент, потому что это определяется протоколом приложения, поэтому для сокета у вас фактически только три состояния: в буфере есть данные, их нужно считать и отправить дальше, в буфере нет данных - нужно ждать когда появятся, сокет закрыт.
...
Рейтинг: 0 / 0
IO/NIO WTF
    #38415983
Фотография Blazkowicz
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Андрей ПанфиловНа транспортном вы не можете знать сколько байт должен в итоге передать клиент, потому что это определяется протоколом приложения
Правильно. Если можно привязаться к протоколу, то всё становится просто.

Андрей ПанфиловПоэтому для сокета у вас фактически только три состояния: в буфере есть данные, их нужно считать и отправить дальше, в буфере нет данных - нужно ждать когда появятся, сокет закрыт.
Именно. Проблема в том что нет какого-то однозначно способа определить состояния и поступить правильно в зависимости от этого в IO.
...
Рейтинг: 0 / 0
IO/NIO WTF
    #38415990
Андрей Панфилов
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
BlazkowiczПроблема в том что нет какого-то однозначно способа определить состояния и поступить правильно в зависимости от этого в IO.Не понял проблемы.. д.б. что-то в духе:

Код: java
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
while (true) {
    while (clientInput.available() > 0) {
        считать/послать
    }
    while (serverInput.available() > 0) {
        считать/послать
    }
    if (client.isClosed() || server.isClosed()) {
        break;
    }
}


по вкусу вставить sleep, т.е. на одно проксируемое соединение уходит один поток
...
Рейтинг: 0 / 0
IO/NIO WTF
    #38415993
Фотография Blazkowicz
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Спасибо за пример! Попробую на досуге в таком ключе. На первый взгляд, действительно, можно избежать вышеуказаных проблем таким способом.
Я просто сходу в разных потоках это делал. Для NIO понял что лучше объединить. Для IO, вижу, вообще без объединения не обойтись.
...
Рейтинг: 0 / 0
IO/NIO WTF
    #38416064
Фотография mayton
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Для файлового ввода-вывода имплементация IO должна импортировать в себя пакеты NIO.
Как там с сокетами - ХЗ но странно всё это. Насчёт переписывания.
...
Рейтинг: 0 / 0
IO/NIO WTF
    #38416086
cdtyjv
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Не очень понял, что у вас за проблемы с available(). Вот пример, в котором все абсолютно четко работает:

Код: java
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.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
public class SocketExample {
    public static void main(String[] args) throws Exception {
        final byte[] chunk1 = new byte[] {1, 2, 3, 4};
        final byte[] chunk2 = new byte[] {5, 6, 7, 8};

        final CountDownLatch latch1 = new CountDownLatch(1);
        final CountDownLatch latch2 = new CountDownLatch(1);
        final CountDownLatch latch3 = new CountDownLatch(1);

        new Thread(new Runnable() {
            @Override public void run() {
                try {
                    ServerSocket srvSock = new ServerSocket(8844);

                    Socket cliSock = srvSock.accept();

                    OutputStream os = cliSock.getOutputStream();

                    os.write(chunk1);

                    latch1.await();

                    os.write(chunk2);

                    latch2.await();

                    os.close();

                    latch3.await();

                    cliSock.close();
                    srvSock.close();
                }
                catch (Exception ignore) {
                    // Ignore.
                }
            }
        }).start();

        Thread.sleep(1000);

        Socket sock = new Socket("localhost", 8844);

        InputStream is = sock.getInputStream();

        assert is.available() == 4;

        byte[] buf = new byte[4];

        int read = is.read(buf);

        assert read == 4;
        assert Arrays.equals(buf, chunk1);
        assert is.available() == 0;

        latch1.countDown();
        Thread.sleep(50);

        assert is.available() == 4;

        read = is.read(buf);

        assert read == 4;
        assert Arrays.equals(buf, chunk2);
        assert is.available() == 0;

        latch2.countDown();
        Thread.sleep(50);

        assert is.available() == 0;
        assert is.read() == -1;

        latch3.countDown();
        Thread.sleep(50);

        assert is.read() == -1;
    }
}



Но это "Java из Java". Я легко допускаю, что клиенты/сервера, с которыми вы взаимодействуете могут рвать соединения некорректно. В любом случае, для вычитки никаких проблем быть не должно.
Метод available() имеет лишь ограниченную применимость, и скедулить потоки, полагаясь на него, разумеется, нельзя.
...
Рейтинг: 0 / 0
IO/NIO WTF
    #38416092
cdtyjv
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
По NIO - да, лучше всего сидеть на селекторах. Один тред слушает slector.select(), потом диспатчит это в другие треды - это типичный паттерн для этого дела.
Вот очень хороший туториал по NIO: http://rox-xmlrpc.sourceforge.net/niotut/

P.S.: Если често, не очень понял, как NIO может быть без селекторов
...
Рейтинг: 0 / 0
IO/NIO WTF
    #38416098
Фотография Blazkowicz
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
cdtyjvP.S.: Если често, не очень понял, как NIO может быть без селекторов
Тот же IO, только проще, и без блокировки.
http://tutorials.jenkov.com/java-nio/socket-channel.html
...
Рейтинг: 0 / 0
IO/NIO WTF
    #38416106
Фотография Blazkowicz
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
cdtyjvНе очень понял, что у вас за проблемы с available(). Вот пример, в котором все абсолютно четко работает:
Я не знаю размеров пакетов. И не могу на них полагаться. Я полагался на то что метод read заблокируется если что или вернет -1.
А ещё я к тому что SocketInputStream нарушает контракт InputStream.

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

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

Вот чтение меня больше всего и озадачило. Как только я пытаюсь в IO читать больше чем длина пакета, то получаю исключение Connection reset, вместо ожидаемой блокировки или -1. И что самое удивительно что в блокирующем режиме через NIO такого не происходит. Я понимаю что частично это и косяк стороннего клиентского сокета. Но как-то слишком много сюрпризов по сравнению с ожиданиями из JavaDoc.


cdtyjvМетод available() имеет лишь ограниченную применимость, и скедулить потоки, полагаясь на него, разумеется, нельзя.
Скедулить потоки я на нем не собирался. Я думал как бы его вообще применить к моей задаче. Но вот до такого 14920507 не додумался, так как всё делал в 2х потоках а не в одном.
...
Рейтинг: 0 / 0
IO/NIO WTF
    #38416112
DoSOfRedRiver
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Blazkowicz,

Приведите пример Вашего кода работы с IO.
...
Рейтинг: 0 / 0
IO/NIO WTF
    #38416117
Фотография Blazkowicz
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
DoSOfRedRiverBlazkowicz,
Приведите пример Вашего кода работы с IO.

Две такие асинхронные таски запускаются с каждой стороны.
Код: java
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
            int read;
            int c = 0;
            byte[] buff = new byte[inSocket.getReceiveBufferSize()];
            try {
                InputStream in = inSocket.getInputStream();
                OutputStream out = outSocket.getOutputStream();
                while (!inSocket.isInputShutdown() && (read = in.read(buff)) >= 0) {
                    if (!outSocket.isOutputShutdown() && read > 0) {
                        out.write(buff, 0, read);
                        c += read;
                        System.out.println(name + " bytes copied " + c);
                    }
                }
            } catch (Exception e) {
                e.printStackTrace(System.out);
            }
...
Рейтинг: 0 / 0
IO/NIO WTF
    #38416137
cdtyjv
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
BlazkowiczСкедулить потоки я на нем не собирался. Я думал как бы его вообще применить к моей задаче. Но вот до такого 14920507 не додумался, так как всё делал в 2х потоках а не в одном.Настоятельно НЕ рекомендую использовать этот код, ибо это что-то сродни busy loop в многопоточном программировании. Если вы не хотите блокироваться, то смело берите селектор, и вперед. Там есть нормальный Selector.select(), с которым никаких Thread.sleep() и прочего *** не надо делать. Тупо один поток сидит на селекторе. Как только с сокетом происходит какое-то событие - он просыпается, и говорит вам, что это было. А вы в зависимости от этого переадресуете работу в другой поток.
В общем, по ссылке, что я привел выше это очень хорошо расписано. Мне в свое время именно этот материал поставил голову на место по части NIO.
...
Рейтинг: 0 / 0
IO/NIO WTF
    #38416141
Фотография schwa
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
авторДальше хуже. read(), который должен вернуть -1 по окончании чтения тоже никогда -1 не возвращает.
-1 вернется только в случае, если другая сторона закрыла соединение пока мы висели на read (и все оповещения об этом успешно дошли).
...
Рейтинг: 0 / 0
IO/NIO WTF
    #38416143
Фотография Blazkowicz
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Андрей Панфилов,
Спасибо огромное! Работает в лучшем виде! Код на много проще и ещё другуя бага выловилась после рефакторинга.
...
Рейтинг: 0 / 0
IO/NIO WTF
    #38416146
Фотография Blazkowicz
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
cdtyjvНастоятельно НЕ рекомендую использовать этот код, ибо это что-то сродни busy loop в многопоточном программировании.
Какой именно?

cdtyjvЕсли вы не хотите блокироваться, то смело берите селектор, и вперед.
Это я знаю. Я планировал блокироваться в первой итерации.

cdtyjvТам есть нормальный Selector.select(), с которым никаких Thread.sleep() и прочего *** не надо делать.
Я в курсе. Мануал прочитан. Примеры прочитаны. Thread.sleep() не планировал использовать в продакшне. Собственно это всё никак не отвечает на мои вопросы выше. Было интересно как сделать через IO. И почему именно так.


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

На словах я это сам могу рассказывать кому угодно. :)
Так и сделаю когда до нагрузки дойдёт.

cdtyjvВ общем, по ссылке, что я привел выше это очень хорошо расписано. Мне в свое время именно этот материал поставил голову на место по части NIO.
Спасибо прочту.
...
Рейтинг: 0 / 0
IO/NIO WTF
    #38416148
Фотография Blazkowicz
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
schwa-1 вернется только в случае, если другая сторона закрыла соединение пока мы висели на read (и все оповещения об этом успешно дошли).
Ок. А может мне кто на пальцах объяснить что такое Connection reset и как на это принято реагировать в TCP?
...
Рейтинг: 0 / 0
IO/NIO WTF
    #38416150
Фотография Blazkowicz
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Андрей Панфилов
Код: java
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
while (true) {
    while (clientInput.available() > 0) {
        считать/послать
    }
    while (serverInput.available() > 0) {
        считать/послать
    }
    if (client.isClosed() || server.isClosed()) {
        break;
    }
}



Блин. Рано радовался. Сессия обмена данных протекла успешно. Это супер. Но! Вылез косяк. После сессии оба сокета открыты. Соединение активно. Я не могу выйти из этого цикла! Ведь я не делаю блокирующего read(), который бы мог вернуть -1 или выкинуть исключение.
Так что IO вариант, похоже, в моём случае окончательно сломан.
...
Рейтинг: 0 / 0
IO/NIO WTF
    #38416155
Андрей Панфилов
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Blazkowicz,

тут беда с isClosed() ровно как и с вашим inSocket.isInputShutdown() - чтобы получить на них true, нужно закрытие сокета инициировать в JVM. По факту жавская реализация сокета предполагает, что чтобы убедиться что сокет закрыт, нужно либо в него что-то записать (тут мы протокол ломаем), либо прочитать (тут зависнем на таймауте), как вариант можно вместо isClosed() ловить эксепшн на sendUrgentData()
...
Рейтинг: 0 / 0
IO/NIO WTF
    #38416158
Фотография Blazkowicz
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Андрей Панфиловлибо прочитать (тут зависнем на таймауте)
Или отхватим исключение ещё до окончания записи.

Андрей Панфиловкак вариант можно вместо isClosed() ловить эксепшн на sendUrgentData()
Не вижу разницы с write.
...
Рейтинг: 0 / 0
IO/NIO WTF
    #38416159
DoSOfRedRiver
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Blazkowicz,

Сразу оговорюсь, что могу в чём-то ошибаться. Ключевые моменты лучше проверить.

Итак,

WTF#1
Действительно, available возвращает количество байт, доступных для чтения. То-есть, если данные на вашу машину ещё не пришли, либо пришли не полностью, то available() вернёт 0.

WTF#2
А какой, собсна, read() вы используете? У меня всё нормально работало, для read() . Советую вам использовать более удобное и эффективное чтение в массив read(byte [] arg), которое возвращает количество считанных байт.
И да,
авториспользовать available для хоть какой-то эмуляции неблокируемости тоже не выйдет

WTF#3
По поводу Connetion reset вот что пишут. Сдаётся мне, проблемы конкретно у вас.

WTF#4
Думается, вытекает из третьего. Всё должно нормально работать.

WTF#5
Избегайте логгирования в местах, где ошибки предсказуемы и некритичны. Можете глянуть на то, как обрабатываются ошибки в Netty, там даже лисенеры специальные есть.

По поводу NIO:
Селекторы достаточно эффективная и удобная вещь, почему вы их избегаете? Вообще, классический IO давно устарел, он неудобен и непрактичен, потому писать лучше сразу с использованием NIO\NIO.2

Приведу здесь пример. Как по мне, в нём отражена вся суть работы IO, кроме, разве что, многопоточного взаимодействия. Пример не мой, кстати.

Код: java
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.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
79.
80.
81.
82.
83.
84.
85.
86.
87.
88.
89.
90.
91.
92.
93.
94.
95.
96.
97.
98.
99.
100.
101.
102.
103.
104.
105.
106.
107.
108.
109.
110.
111.
112.
113.
114.
115.
116.
117.
118.
119.
120.
121.
122.
123.
124.
125.
126.
127.
128.
129.
130.
131.
132.
133.
134.
135.
136.
137.
138.
139.
140.
141.
142.
143.
144.
145.
146.
147.
148.
149.
150.
151.
152.
153.
154.
155.
156.
157.
158.
159.
160.
161.
162.
163.
164.
165.
166.
167.
168.
169.
170.
171.
172.
173.
174.
175.
176.
177.
178.
179.
180.
181.
182.
183.
184.
185.
186.
187.
188.
189.
190.
191.
192.
193.
194.
195.
196.
197.
198.
199.
200.
201.
202.
203.
204.
205.
206.
207.
208.
209.
210.
211.
212.
213.
214.
215.
216.
217.
218.
219.
220.
221.
222.
223.
224.
225.
226.
227.
228.
229.
230.
231.
232.
233.
234.
235.
236.
237.
238.
239.
240.
241.
242.
243.
244.
245.
246.
247.
248.
249.
250.
251.
252.
253.
254.
255.
256.
257.
258.
259.
260.
261.
262.
263.
264.
265.
import java.io.*;
import java.net.*;
import java.text.DateFormat;
import java.util.Date;
import java.util.TimeZone;

class SimpleWEBServer extends Thread
{
    Socket s;

    public static void main(String args[])
    {
        try
        {
            // привинтить сокет на локалхост, порт 80
            ServerSocket server = new ServerSocket(80, 0,
                    InetAddress.getByName("localhost"));

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

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

    public SimpleWEBServer(Socket s)
    {
        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 request = new String(buf, 0, r);

            // получаем путь до документа (см. ниже ф-ю "getPath")
            String path = getPath(request);

            // если из запроса не удалось выделить путь, то 
            // возвращаем "400 Bad Request"
            if(path == null)
            {
                // первая строка ответа
                String response = "HTTP/1.1 400 Bad Request\n";

                // дата в GMT
                DateFormat df = DateFormat.getTimeInstance();
                df.setTimeZone(TimeZone.getTimeZone("GMT"));
                response = response + "Date: " + df.format(new Date()) + "\n";

                // остальные заголовки
                response = response
                + "Connection: close\n"
                + "Server: SimpleWEBServer\n"
                + "Pragma: no-cache\n\n";

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

                // завершаем соединение
                s.close();

                // выход
                return;
            }


            // если файл существует и является директорией,
            // то ищем индексный файл index.html
            File f = new File(path);
            boolean flag = !f.exists();
            if(!flag) if(f.isDirectory())
            {
                if(path.lastIndexOf(""+File.separator) == path.length()-1)
                    path = path + "index.html";
                else
                    path = path + File.separator + "index.html";
                f = new File(path);
                flag = !f.exists();
            }

            // если по указанному пути файл не найден
            // то выводим ошибку "404 Not Found"
            if(flag)
            {
                // первая строка ответа
                String response = "HTTP/1.1 404 Not Found\n";

                // дата в GMT
                DateFormat df = DateFormat.getTimeInstance();
                df.setTimeZone(TimeZone.getTimeZone("GMT"));
                response = response + "Date: " + df.format(new Date()) + "\n";

                // остальные заголовки
                response = response
                + "Content-Type: text/html\n"
                + "Connection: close\n"
                + "Server: SimpleWEBServer\n"
                + "Pragma: no-cache\n\n";

                // и гневное сообщение
                response = response + "File " + path + " not found!";

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

                // завершаем соединение
                s.close();

                // выход
                return;
            }

            // определяем MIME файла по расширению
            // MIME по умолчанию - "text/plain"
            String mime = "text/plain";
            
            // выделяем у файла расширение (по точке)
            r = path.lastIndexOf(".");
            if(r > 0)
            {
                String ext = path.substring(r);
                if(ext.equalsIgnoreCase("html"))
                    mime = "text/html";
                else if(ext.equalsIgnoreCase("htm"))
                    mime = "text/html";
                else if(ext.equalsIgnoreCase("gif"))
                    mime = "image/gif";
                else if(ext.equalsIgnoreCase("jpg"))
                    mime = "image/jpeg";
                else if(ext.equalsIgnoreCase("jpeg"))
                    mime = "image/jpeg";
                else if(ext.equalsIgnoreCase("bmp"))
                    mime = "image/x-xbitmap";
            }

            // создаём ответ

            // первая строка ответа
            String response = "HTTP/1.1 200 OK\n";

            // дата создания в GMT
            DateFormat df = DateFormat.getTimeInstance();
            df.setTimeZone(TimeZone.getTimeZone("GMT"));

            // время последней модификации файла в GMT
            response = response + "Last-Modified: " + df.format(new Date(f.lastModified())) + "\n";

            // длина файла
            response = response + "Content-Length: " + f.length() + "\n";

            // строка с MIME кодировкой
            response = response + "Content-Type: " + mime + "\n";

            // остальные заголовки
            response = response
            + "Connection: close\n"
            + "Server: SimpleWEBServer\n\n";

            // выводим заголовок:
            os.write(response.getBytes());

            // и сам файл:
            FileInputStream fis = new FileInputStream(path);
            r = 1;
            while(r > 0)
            {
                r = fis.read(buf);
                if(r > 0) os.write(buf, 0, r);
            }
            fis.close();

            // завершаем соединение
            s.close();
        }
        catch(Exception e)
        {e.printStackTrace();} // вывод исключений
    }


    // "вырезает" из HTTP заголовка URI ресурса и конвертирует его в filepath
    // URI берётся только для GET и POST запросов, иначе возвращается null
    protected String getPath(String header)
    {
        // ищем URI, указанный в HTTP запросе
        // URI ищется только для методов POST и GET, иначе возвращается null
        String URI = extract(header, "GET ", " "), path;
        if(URI == null) URI = extract(header, "POST ", " ");
        if(URI == null) return null;

        // если URI записан вместе с именем протокола
        // то удаляем протокол и имя хоста
        path = URI.toLowerCase();
        if(path.indexOf("http://", 0) == 0)
        {
            URI = URI.substring(7);
            URI = URI.substring(URI.indexOf("/", 0));
        }
        else if(path.indexOf("/", 0) == 0)
            URI = URI.substring(1); // если URI начинается с символа /, удаляем его

        // отсекаем из URI часть запроса, идущего после символов ? и #
        int i = URI.indexOf("?");
        if(i > 0) URI = URI.substring(0, i);
        i = URI.indexOf("#");
        if(i > 0) URI = URI.substring(0, i);

        // конвертируем URI в путь до документов
        // предполагается, что документы лежат там же, где и сервер
        // иначе ниже нужно переопределить path
        path = "." + File.separator;
        char a;
        for(i = 0; i < URI.length(); i++)
        {
            a = URI.charAt(i);
            if(a == '/')
                path = path + File.separator;
            else
                path = path + a;
        }

        return path;
    }


    // "вырезает" из строки str часть, находящуюся между строками start и end
    // если строки end нет, то берётся строка после start
    // если кусок не найден, возвращается null
    // для поиска берётся строка до "\n\n" или "\r\n\r\n", если таковые присутствуют
    protected String extract(String str, String start, String end)
    {
        int s = str.indexOf("\n\n", 0), e;
        if(s < 0) s = str.indexOf("\r\n\r\n", 0);
        if(s > 0) str = str.substring(0, s);
        s = str.indexOf(start, 0)+start.length();
        if(s < start.length()) return null;
        e = str.indexOf(end, s);
        if(e < 0) e = str.length();
        return (str.substring(s, e)).trim();
    }
}

...
Рейтинг: 0 / 0
IO/NIO WTF
    #38416164
Андрей Панфилов
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Blazkowicz,

sendUrgentData данные не передает а шлет флаг, правда минут что поведение зависит от ОС.
...
Рейтинг: 0 / 0
IO/NIO WTF
    #38416165
DoSOfRedRiver
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
DoSOfRedRiver,

Алсо для многопоточной обработки внешних соединений рекомендую асинхронное неблокироющее IO с NIO.2 и шаблон Proactor. Ну это если вам надо высокоэффективное приложение, по типу вэб-сервер.
...
Рейтинг: 0 / 0
25 сообщений из 85, страница 1 из 4
Форумы / Java [игнор отключен] [закрыт для гостей] / IO/NIO WTF
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


Просмотр
0 / 0
Close
Debug Console [Select Text]