Как работает js: websocket и http/2+sse. что выбрать?

Свойства

Возвращает причину, по которой подтверждение закрытия было инициировано для экземпляра ClientWebSocket.Gets the reason why the close handshake was initiated on ClientWebSocket instance.

Возвращает описание причины, по которой экземпляр ClientWebSocket был закрыт.Gets a description of the reason why the ClientWebSocket instance was closed.

Возвращает параметры WebSocket для экземпляра ClientWebSocket.Gets the WebSocket options for the ClientWebSocket instance.

Получает состояние WebSocket экземпляра ClientWebSocket.Gets the WebSocket state of the ClientWebSocket instance.

Получает поддерживаемый подпротокол WebSocket для экземпляра ClientWebSocket.Gets the supported WebSocket sub-protocol for the ClientWebSocket instance.

Простой клиент веб-сокетов

С точки зрения веб-страницы функциональность веб-сокетов легко понять и использовать. Первый шаг — это создать объект WebSocket и передать ему URL. Код для этого подобен следующему:

Строка URL начинается с текста ws://, который идентифицирует подключение типа веб-сокет. Этот URL указывает файл веб-приложения на сервере (в данном случае это сценарий socketServer.php).

Стандарт веб-сокетов также поддерживает URL, которые начинаются с текста wss://, что указывает на требование использовать безопасное, зашифрованное подключение (точно так же, как и при запросе веб-страницы указывается URL, начинающийся с https:// вместо http://).

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

Само обстоятельство создания объекта WebSocket понуждает страницу пытаться подключиться к серверу. Дальше надо использовать одно из четырех событий объекта WebSocket: onOpen (при установлении подключения), onError (когда возникает ошибка), onClose (при закрытии подключения) и onMessage (когда страница получает сообщение от сервера):

Например, в случае успешного подключения неплохо бы отправить соответствующее подтверждающее сообщение. Такое сообщение доставляется с помощью метода send() объекта WebSocket, которому в качестве параметра передается обычный текст. Далее приведена функция, которая обрабатывает событие onopen и отправляет сообщение:

Предположительно, веб-сервер получит это сообщение и даст на него ответ.

События onError и onClose можно использовать для отправки извещений посетителю веб-страницы. Но безоговорочно самым важным является событие onMessage, которое срабатывает при получении новых данных от сервера. Опять же, код JavaScript для обработки этого события не представляет никаких сложностей — мы просто извлекаем текст сообщения из свойства data:

Если веб-страница решит, что вся ее работа выполнена, она может закрыть подключение, используя метод disconnect():

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

Чтобы заставить подключение веб-сокетов работать, выполняется большой объем работы за кулисами. Прежде всего, веб-страница устанавливает связь по обычному стандарту HTTP. Потом это подключение нужно повысить до подключения веб-сокетов, позволяющего свободную двустороннюю связь. На этом этапе возможны проблемы, если между компьютером клиента и веб-сервером находится прокси-сервер (как, например, в типичной корпоративной сети). Прокси-сервер может отказаться сотрудничать и разорвет подключение. Эту проблему можно решить, обнаруживая неудачное подключение (посредством события onError объекта WebSocket) и применяя один из заполнителей (polyfills) для сокетов, описанных на веб-сайте GitHub. Эти заполнители применяют метод опроса, чтобы эмулировать подключение веб-сокетов.

Реализация сервера на PHP

Исходники простого WebSocket echo-сервера выложил сюда.
Код хорошо документирован, но я всё же опишу некоторые тонкости реализации.
Чтобы «поднять» WebSocket сервер нужно создать обычный TCP-сервер.
В PHP TCP-сервер реализуется через «stream_socket» или через PHP расширение «sockets».
Различия между ними в том, что «stream_socket» работает на встроенных функциях PHP для работы с потоками, «sockets» же работает через модуль PHP и повторяет функции для работы с сокетами в языке «C».
Я выбрал «sockets».

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

Фреймы парсю через «pack/unpack».
Сервер не понимает фрагментированных фреймов.
Сервер выдаёт только незамаскированные сообщения, т.к. некоторые браузеры не понимают замаскированных сообщений.
Сервер реагирует только на текстовые фреймы и фрейм закрытия соединения, бинарные фреймы не понимает.

Ну собственно всё, удачи в исследовании этого не простого протокола.

License¶

Copyright (c) 2013-2014 Aymeric Augustin and contributors.
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

    * Redistributions of source code must retain the above copyright notice,
      this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright notice,
      this list of conditions and the following disclaimer in the documentation
      and/or other materials provided with the distribution.
    * Neither the name of websockets nor the names of its contributors may
      be used to endorse or promote products derived from this software without
      specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

HTTP Streaming

HTTP Streaming — provides a long-lived connection for instant and continuous data push (Image from realtimeapi.io)

The client makes an HTTP request, and the server trickles out a response of indefinite length (it’s like polling infinitely).HTTP streaming is performant, easy to consume and can be an alternative to WebSockets.

Issue: Intermediaries can interrupt the connection (e.g. timeout, intermediaries serving other requests in a round-robin manner). In such cases, it cannot guarantee the complete realtimeness.

00:00:00 CLIENT-> I need cakes 00:00:01 SERVER-> Wait for a moment.00:00:01 SERVER-> Cake-1 is in process.00:00:02 SERVER-> Have cake-1.00:00:02 SERVER-> Wait for cake-2.00:00:03 SERVER-> Cake-2 is in process.00:00:03 SERVER-> You must be enjoying cake-1.00:00:04 SERVER-> Have cake-2.00:00:04 SERVER-> Wait for cake-3.00:00:05 CLIENT-> Enough, I'm full.

Приостановка долгоживущих запросов

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

Очевидно, нельзя просто задерживать запрос и ответ – это может привести к дефициту потоков и высокому потреблению памяти. Для приостановки запроса при ждущем опросе в среде неблокирующего ввода/вывода требуется специальный API. В Java такой API обеспечивает спецификация Servlet 3.0 (см. часть 1 этого цикла). Пример приведен в листинге 9.

Листинг 9. Определение асинхронного сервлета с помощью Servlet 3.0
<?xml version="1.0" encoding="UTF-8"?> 

<web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee" 
         xmlns:j2ee="http://java.sun.com/xml/ns/javaee" 
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
         xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml
/ns/j2ee/web-app_3.0.xsd"> 

    <servlet> 
        <servlet-name>events</servlet-name> 
        <servlet-class>ReverseAjaxServlet</servlet-class> 
        <async-supported>true</async-supported> 
    </servlet> 

    <servlet-mapping> 
        <servlet-name>events</servlet-name> 
        <url-pattern>/ajax</url-pattern> 
    </servlet-mapping> 

</web-app>

Определив асинхронный сервлет, можно использовать API Servlet 3.0 для приостановки и возобновления запроса, как показано в листинге 10.

Листинг 10. Приостановка и возобновление действия запроса
AsyncContext asyncContext = req.startAsync(); 
// Ссылка asyncContext где-то запоминается,

// а затем, при необходимости, ее можно продолжить или завершить в другом потоке 
HttpServletResponse req = 
    (HttpServletResponse) asyncContext.getResponse(); 
req.getWriter().write("data"); 
req.setContentType(); 
asyncContext.complete();

До появления Servlet 3.0 каждый контейнер должен был иметь (и до сих пор имеет) свой собственный механизм. Хорошо известным примером является Jetty Continuations; на Jetty Continuations опираются многие библиотеки Reverse Ajax в Java. При этом не обязательно запускать приложение в контейнере Jetty. API достаточно «умен», чтобы определить контейнер, с которым вы работаете, и вернуться к Servlet 3.0 API, если он есть, при запуске в другом контейнере, таком как Tomcat или Grizzly. Это справедливо для Comet, но если вы хотите воспользоваться преимуществами WebSockets, другого выбора, кроме использования функций, зависящих от контейнера, пока нет.

Спецификация Servlet 3.0 еще не вышла, но многие контейнеры уже реализуют этот API, так как это стандартный способ работы с Reverse Ajax.

wsdump.py

wsdump.py is simple WebSocket test(debug) tool.

sample for echo.websocket.org:

$ wsdump.py ws://echo.websocket.org/
Press Ctrl+C to quit
> Hello, WebSocket
< Hello, WebSocket
> How are you?
< How are you?

Usage

usage:

wsdump.py  ] ws_url

WebSocket Simple Dump Tool

positional arguments:
ws_url websocket url. ex. ws://echo.websocket.org/
optional arguments:
-h, --help show this help message and exit
WebSocketApp
-v VERBOSE, --verbose VERBOSE
  set verbose mode. If set to 1, show opcode. If set to 2, enable to trace websocket module

example:

$ wsdump.py ws://echo.websocket.org/
$ wsdump.py ws://echo.websocket.org/ -v
$ wsdump.py ws://echo.websocket.org/ -vv

FlashSockets

Для браузеров, не поддерживающих WebSockets, некоторые библиотеки позволяют прибегнуть к FlashSockets (сокеты через Flash). Эти библиотеки обычно предоставляют тот же официальный API WebSocket, но реализуют его путем делегирования вызовов скрытому Flash-компоненту, включенному в Web-сайт.

Преимущества

FlashSockets прозрачно предоставляют возможности WebSockets, даже в браузерах, не поддерживающих WebSockets из HTML5.

Недостатки

Метод FlashSockets имеет следующие недостатки:

  • он требует установки Flash-плагина (как правило, он есть во всех браузерах);
  • он требует, чтобы порт 843 брандмауэра был открыт, так чтобы Flash-компонент мог делать HTTP-запросы для получения файла политики с авторизацией домена.

    Если порт 843 недоступен, библиотека откажется работать или выдаст сообщение об ошибке. Все это требует времени (до 3 секунд в зависимости от библиотеки), что замедляет работу Web-сайта;

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

Соответствующее решение предоставляет проект WebSocketJS. Он требует наличия версии Flash не ниже 10 и обеспечивает поддержку WebSockets в Firefox 3, Internet Explorer 8 и Internet Explorer 9.

Рекомендации

WebSockets имеет много преимуществ по сравнению с Comet. Клиенты, поддерживающие WebSockets, обычно работают быстрее и создают меньше запросов (и, следовательно, потребляют меньше трафика). Но, поскольку не все браузеры поддерживают WebSockets, лучшим выбором библиотеки Reverse Ajax будет та, которая способна обнаруживать поддержку WebSockets и возвращаться к методу Comet (ждущий опрос), если WebSockets не поддерживаются.

Эти два метода необходимы, чтобы добиться максимальной отдачи от любого браузера и сохранить совместимость, поэтому рекомендуется использовать клиентскую библиотеку JavaScript, которая создает уровень абстракции поверх этих методов. Некоторые библиотеки будут рассмотрены в частях 3 и 4 этого цикла, а их применение ― в части 5. На стороне сервера дело обстоит немного сложнее, как будет видно из следующего раздела.

WebSocket compression

The extension is disabled by default on the server and enabled by default on the
client. It adds a significant overhead in terms of performance and memory
consumption so we suggest to enable it only if it is really needed.

Note that Node.js has a variety of issues with high-performance compression,
where increased concurrency, especially on Linux, can lead to catastrophic
memory fragmentation and slow performance. If you intend to use
permessage-deflate in production, it is worthwhile to set up a test
representative of your workload and ensure Node.js/zlib will handle it with
acceptable performance and memory usage.

See for more options.

const WebSocket = require('ws');

const wss = new WebSocket.Server({
  port: 8080,
  perMessageDeflate: {
    zlibDeflateOptions: {
      // See zlib defaults.
      chunkSize: 1024,
      memLevel: 7,
      level: 3
    },
    zlibInflateOptions: {
      chunkSize: 10 * 1024
    },
    // Other options settable:
    clientNoContextTakeover: true, // Defaults to negotiated value.
    serverNoContextTakeover: true, // Defaults to negotiated value.
    serverMaxWindowBits: 10, // Defaults to negotiated value.
    // Below options specified as default values.
    concurrencyLimit: 10, // Limits zlib concurrency for perf.
    threshold: 1024 // Size (in bytes) below which messages
    // should not be compressed.
  }
});

The client will only use the extension if it is supported and enabled on the
server. To always disable the extension on the client set the
option to .

const WebSocket = require('ws');

const ws = new WebSocket('ws://www.host.com/path', {
  perMessageDeflate: false
});

A simple example

To open a websocket connection, we need to create using the special protocol in the url:

There’s also encrypted protocol. It’s like HTTPS for websockets.

Always prefer

The protocol is not only encrypted, but also more reliable.

That’s because data is not encrypted, visible for any intermediary. Old proxy servers do not know about WebSocket, they may see “strange” headers and abort the connection.

On the other hand, is WebSocket over TLS, (same as HTTPS is HTTP over TLS), the transport security layer encrypts the data at sender and decrypts at the receiver. So data packets are passed encrypted through proxies. They can’t see what’s inside and let them through.

Once the socket is created, we should listen to events on it. There are totally 4 events:

  • – connection established,
  • – data received,
  • – websocket error,
  • – connection closed.

…And if we’d like to send something, then will do that.

Here’s an example:

For demo purposes, there’s a small server server.js written in Node.js, for the example above, running. It responds with “Hello from server, John”, then waits 5 seconds and closes the connection.

So you’ll see events → → .

That’s actually it, we can talk WebSocket already. Quite simple, isn’t it?

Now let’s talk more in-depth.

Сервисы, ограниченные запросом (request-scoped)

Многие среды предоставляют сервисы, или фильтры, которые управляют Web-запросами, поступающими в сервлет. Например, фильтр может выполнять:

  • привязку JDBC-соединения к потоку запроса, чтобы для всего запроса использовалось только одно соединение;
  • фиксацию изменений в конце запроса.

Другой пример ― расширение Guice Servlet службы Google Guice (библиотека внедрения зависимостей). Как и Spring, Guice может связывать сервисы в пределах запроса. Для каждого нового запроса экземпляр создается не более одного раза (подробности см. в разделе ).

К типичным применениям такого подхода относится кэширование в запросе пользовательского объекта, извлекаемого из хранилища (например, базы данных), с помощью идентификатора пользователя, взятого из кластерного HTTP-сеанса. В Google Guice можно написать код, аналогичный коду листинга 7.

Листинг 7. Связывание с областью видимости в пределах запроса
@Provides 
@RequestScoped 
Member member(AuthManager authManager, 
              MemberRepository memberRepository) { 
    return memberRepository.findById(authManager.getCurrentUserId());
}

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

Сервисы с областью видимости в пределах запроса можно использовать с любым решением Reverse Ajax, кроме WebSockets. Любое другое решение основывается на HTTP-запросах, коротких или долгоживущих, так что каждый запрос проходит через системы диспетчеризации сервлетов, и фильтры выполняются. В последующих частях этого цикла вы увидите, что при выполнении приостановленного (долгоживущего) HTTP-запроса есть также возможность еще раз пропустить запрос через цепочку фильтров.

В WebSockets данные поступают непосредственно по обратному вызову , как в TCP-сокете. В этом случае никакого HTTP-запроса нет, поэтому нет контекста запроса, из которого можно брать и сохранять объекты с областью видимости в пределах запроса. Таким образом, использовать сервис, требующий от обратного вызова request-scoped объект, не удастся.

Пример guice-и-websocket в показывает, как обойти это ограничение и использовать request-scoped объекты для обратного вызова . Если запустить пример и нажать каждую кнопку на Web-странице для проверки вызова Ajax (request-scoped), WebSocket и WebSocket с имитацией request-scoped объекта, получится результат, показанный на рисунке 3.

Рисунок 3. Результат обработчика WebSocket с использованием request-scoped сервисов

С такими проблемами можно столкнуться, используя:

  • Spring;
  • Hibernate;
  • любую другую среду, которая требует request-scoped или per-request модели, например, ;
  • любую систему, использующую средства для привязки переменных к потоку запроса внутри фильтра и последующего обращения к нему.

В Guice есть элегантное решение, которое показано в листинге 8.

Листинг 8. Имитация request-scoped модели из обратного вызова
// Ссылка на запрос удерживается при вызове 
// doWebSocketMethod
HttpServletRequest request =  
Map<Key<?>, Object> bindings = new HashMap<Key<?>, Object>(); 
// У меня есть сервис, которому требуется запрос для получения доступа к сеансу, 
// и я подаю такой запрос, но вы можете предоставить любое другое 
// связывание, которое может вам понадобиться
bindings.put(Key.get(HttpServletRequest.class), request); 
ServletScopes.scopeRequest(new Callable<Object>() {
 	@Override 
	public Object call() throws Exception { 
  		// обращение к хранилищу или любому сервису с использованием объектов, 
  		// ограниченных одним запросом. 
		outbound.sendMessage(); 
		return null; 
	} 
}, bindings).call();

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

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

Начнем с процесса взаимодействия с сервисом.

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

Чтобы смоделировать сообщение, содержащее имя, вы можете создать POJO с полем
и соответствующим методом :

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

Чтобы смоделировать представление приветствия, вы добавляете другой POJO с полем
и соответствующим методом :

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

Secure example¶

Secure WebSocket connections improve confidentiality and also reliability
because they reduce the risk of interference by bad proxies.

The WSS protocol is to WS what HTTPS is to HTTP: the connection is encrypted
with Transport Layer Security (TLS) — which is often referred to as Secure
Sockets Layer (SSL). WSS requires TLS certificates like HTTPS.

Here’s how to adapt the server example to provide secure connections. See the
documentation of the module for configuring the context securely.

#!/usr/bin/env python

# WSS (WS over TLS) server example, with a self-signed certificate

import asyncio
import pathlib
import ssl
import websockets

async def hello(websocket, path):
    name = await websocket.recv()
    print(f"< {name}")

    greeting = f"Hello {name}!"

    await websocket.send(greeting)
    print(f"> {greeting}")

ssl_context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
localhost_pem = pathlib.Path(__file__).with_name("localhost.pem")
ssl_context.load_cert_chain(localhost_pem)

start_server = websockets.serve(
    hello, "localhost", 8765, ssl=ssl_context
)

asyncio.get_event_loop().run_until_complete(start_server)
asyncio.get_event_loop().run_forever()

Here’s how to adapt the client.

#!/usr/bin/env python

# WSS (WS over TLS) client example, with a self-signed certificate

import asyncio
import pathlib
import ssl
import websockets

ssl_context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
localhost_pem = pathlib.Path(__file__).with_name("localhost.pem")
ssl_context.load_verify_locations(localhost_pem)

async def hello():
    uri = "wss://localhost:8765"
    async with websockets.connect(
        uri, ssl=ssl_context
    ) as websocket
        name = input("What's your name? ")

        await websocket.send(name)
        print(f"> {name}")

        greeting = await websocket.recv()
        print(f"< {greeting}")

asyncio.get_event_loop().run_until_complete(hello())

This client needs a context because the server uses a self-signed certificate.

Заключение

Несмотря на некоторые недостатки, WebSockets – это чрезвычайно мощное решение Reverse Ajax. Оно пока реализовано не во всех браузерах, и его непросто использовать на стороне сервера в Java без помощи библиотеки Reverse Ajax. Так как стандартный стиль запрос-ответ не используется, нельзя полагаться на выполнение цепочки фильтров для реализации request-scoped сервисов. Для Comet и WebSockets требуются специальные функции контейнеров, зависящие от реализации на стороне сервера, и об этом нужно помнить при использовании нового контейнера, иначе он не будет масштабироваться.

В части 3 этого цикла мы рассмотрим различные API для Comet и WebSockets на стороне сервера, а также расскажем об Atmosphere — среде Reverse Ajax.

Похожие темы

  • Оригинал статьи
  • Start using HTML5 WebSockets today (Nettuts+): о том, как выполнять WebSocket-сервер в PHP и как построить клиент для отправки и получения сообщений от него по протоколу WebSocket.
  • The WebSocket API (W3C, июль 2011 г.): спецификация, определяющая API, который позволяет Web-страницам использовать протокол WebSocket для двусторонней связи с удаленным хостом.
  • Браузеры с поддержкой jWebSocket: все о браузерах, поддерживаемых jWebSocket и мостом Flash Socket.
  • WebSocketJS (WebSocket Flash Bridge): реализация HTML5 WebSocket на базе Flash.
  • Подробнее о поддержке асинхронной обработки в Servlet 3.0
  • Блог Филиппа Маккарти: Comet и Java: потоковый ввод/вывод против неблокирующего.
  • The Rox Java NIO Tutorial: накопленный автором опыт использования библиотек Java NIO и десятки советов, предложений и предостережений, найденных в Интернете.
  • Читайте в Википедии об:
    • Ajax
    • Reverse Ajax
    • Comet
    • WebSockets
  • Exploring Reverse AJAX (блог Google Maps .Net Control, август 2006 г.): введение в некоторые методы Reverse Ajax.
  • Cross-domain communications with JSONP, Part 1: Combine JSONP and jQuery to quickly build powerful mashups (developerWorks, февраль 2009 г.): как сочетать метод скрытого междоменного вызова (JSONP) с гибкой библиотекой JavaScript (JQuery) для создания мощных и на удивление быстрых гибридных приложений.
  • Спецификация Cross-Origin Resource Sharing (CORS) (W3C, июль 2010 г.): подробнее о механизме, который позволяет XHR выполнять кросс-доменные запросы.
  • Build Ajax applications with Ext JS (developerWorks, июль 2008 г.): обзор концепций объектно-ориентированного JavaScript-программирования, стоящих за Ext JS, и способов применения среды Ext JS для элементов пользовательского интерфейса многофункциональных интернет-приложений.
  • Compare JavaScript frameworks (developerWorks, февраль 2010 г.): обзор интегрированных сред разработки, которые значительно упрощают разработку JavaScript.
  • Mastering Ajax, Part 2: Make asynchronous requests with JavaScript and Ajax (developerWorks, январь 2006 г.): как использовать Ajax и объект для создания модели запрос/ответ, которая никогда не заставляет пользователей ждать реакции сервера.
  • Create Ajax applications for the mobile Web (developerWorks, март 2010 г.): как создавать не зависящие от браузера Web-приложения для смартфона с использованием Ajax.
  • Where and when to use Ajax in your applications (developerWorks, февраль 2008 г.): как использовать Ajax для совершенствования Web-сайтов, избегая неудобств для пользователей.
  • Improve the performance of Web 2.0 applications (developerWorks, декабрь 2009 г.): различные механизмы кэширования на стороне браузера.
  • Introducing JSON (JSON.org): введение в синтаксис JSON.
  • Google Guice: легкая среда внедрения зависимостей для Java 5 и выше.
  • Jetty: Jetty, Web-сервер и контейнер javax.servlet с поддержкой WebSockets.
  • Apache Maven: инструмент для управления проектами разработки программного обеспечения.
Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Adblock
detector