`
kavy
  • 浏览: 888571 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

WebSocket +Jetty+jQuery 实现服务器消息推送例子

 
阅读更多

服务器servlet代码

 

package flowersinthesand.example;

import java.io.IOException;
import java.io.PrintWriter;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Queue;
import java.util.UUID;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.LinkedBlockingQueue;

import javax.servlet.AsyncContext;
import javax.servlet.AsyncEvent;
import javax.servlet.AsyncListener;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.eclipse.jetty.util.UrlEncoded;
import org.eclipse.jetty.websocket.WebSocket;
import org.eclipse.jetty.websocket.WebSocketServlet;

import com.google.gson.Gson;

@WebServlet(urlPatterns = "/chat", asyncSupported = true)
public class ChatServlet extends WebSocketServlet {

	private static final long serialVersionUID = 4805728426990609124L;

	private Map<String, AsyncContext> asyncContexts = new ConcurrentHashMap<String, AsyncContext>();
	private Queue<ChatWebSocket> webSockets = new ConcurrentLinkedQueue<ChatWebSocket>();
	private BlockingQueue<String> messages = new LinkedBlockingQueue<String>();
	private Thread notifier = new Thread(new Runnable() {
		public void run() {
			while (true) {
				try {
					// Waits until a message arrives
					String message = messages.take();

					// Sends the message to all the AsyncContext's response
					for (AsyncContext asyncContext : asyncContexts.values()) {
						try {
							sendMessage(asyncContext.getResponse().getWriter(), message);
						} catch (Exception e) {
							asyncContexts.values().remove(asyncContext);
						}
					}

					// Sends the message to all the WebSocket's connection
					for (ChatWebSocket webSocket : webSockets) {
						try {
							webSocket.connection.sendMessage(message);
						} catch (Exception e) {
							webSockets.remove(webSocket);
						}
					}
				} catch (InterruptedException e) {
					break;
				}
			}
		}
	});

	private void sendMessage(PrintWriter writer, String message) throws IOException {
		// default message format is message-size ; message-data ;
		writer.print(message.length());
		writer.print(";");
		writer.print(message);
		writer.print(";");
		writer.flush();
	}

	@Override
	public void init(ServletConfig config) throws ServletException {
		super.init(config);
		notifier.start();
	}

	// GET method is used to establish a stream connection
	@Override
	protected void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {

		// Content-Type header
		response.setContentType("text/plain");
		response.setCharacterEncoding("utf-8");

		// Access-Control-Allow-Origin header
		response.setHeader("Access-Control-Allow-Origin", "*");

		PrintWriter writer = response.getWriter();

		// Id
		final String id = UUID.randomUUID().toString();
		writer.print(id);
		writer.print(';');

		// Padding
		for (int i = 0; i < 1024; i++) {
			writer.print(' ');
		}
		writer.print(';');
		writer.flush();

		final AsyncContext ac = request.startAsync();
		ac.addListener(new AsyncListener() {
			public void onComplete(AsyncEvent event) throws IOException {
				asyncContexts.remove(id);
			}

			public void onTimeout(AsyncEvent event) throws IOException {
				asyncContexts.remove(id);
			}

			public void onError(AsyncEvent event) throws IOException {
				asyncContexts.remove(id);
			}

			public void onStartAsync(AsyncEvent event) throws IOException {

			}
		});
		asyncContexts.put(id, ac);
	}

	// POST method is used to communicate with the server
	@Override
	protected void doPost(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		request.setCharacterEncoding("utf-8");

		AsyncContext ac = asyncContexts.get(request.getParameter("metadata.id"));
		if (ac == null) {
			return;
		}

		// close-request
		if ("close".equals(request.getParameter("metadata.type"))) {
			ac.complete();
			return;
		}

		// send-request
		Map<String, String> data = new LinkedHashMap<String, String>();
		data.put("username", request.getParameter("username"));
		data.put("message", request.getParameter("message"));

		try {
			messages.put(new Gson().toJson(data));
		} catch (InterruptedException e) {
			throw new IOException(e);
		}
	}

	@Override
	public WebSocket doWebSocketConnect(HttpServletRequest request, String protocol) {
		return new ChatWebSocket();
	}

	class ChatWebSocket implements WebSocket.OnTextMessage {

		Connection connection;

		@Override
		public void onOpen(Connection connection) {
			this.connection = connection;
			webSockets.add(this);
		}

		@Override
		public void onClose(int closeCode, String message) {
			webSockets.remove(this);
		}

		@Override
		public void onMessage(String queryString) {
			// Parses query string
			UrlEncoded parameters = new UrlEncoded(queryString);

			Map<String, String> data = new LinkedHashMap<String, String>();
			data.put("username", parameters.getString("username"));
			data.put("message", parameters.getString("message"));

			try {
				messages.put(new Gson().toJson(data));
			} catch (InterruptedException e) {
				throw new RuntimeException(e);
			}
		}

	}

	@Override
	public void destroy() {
		messages.clear();
		webSockets.clear();
		asyncContexts.clear();
		notifier.interrupt();
	}

}

 

html代码

<!DOCTYPE html>
<html>
	<head>
		<title>Chat - Jetty 8</title>
		<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
		<script type="text/javascript" src="jquery-1.5.0.js"></script>
		<script type="text/javascript" src="jquery.stream-1.2.js"></script>
		<script type="text/javascript">
		$.stream.setup({enableXDR: true});
		
		var chat = {
			lastUsername: "Donghwan Kim",
			username: $.trim(window.prompt("Username?")) || "Anonymous" + $(window).width()
		};
		
		$(function() {
			$.stream("chat", {
				dataType: "json",
				context: $("#content")[0],
				open: function(event, stream) {
					$("#editor .message").removeAttr("disabled").focus();
					stream.send({username: chat.username, message: "Hello"});
				},
				message: function(event) {
					if (chat.lastUsername !== event.data.username) {
						$("<p />").addClass("user").text(chat.lastUsername = event.data.username).appendTo(this);
					}
					
					$("<p />").addClass("message").text(event.data.message).appendTo(this);
					this.scrollTop = this.scrollHeight;
				},
				error: function() {
					$("#editor .message").attr("disabled", "disabled");
				},
				close: function() {
					$("#editor .message").attr("disabled", "disabled");
				}
			});
			
			$("#editor .user").text(chat.username);
			$("#editor .message").keyup(function(event) {
				if (event.which === 13 && $.trim(this.value)) {
					$.stream().send({username: chat.username, message: this.value});
					this.value = "";
				}
			});
			
			$(window).resize(function() {
				var content = $("#content").height($(window).height() - $("#editor").outerHeight(true) - 15)[0];
				content.scrollTop = content.scrollHeight;
			}).resize();
		});
		</script>
		<style>
		body {padding: 0; margin: 0; min-width: 320px; font-family: 'Trebuchet MS','Malgun Gothic',Verdana,Helvetica,Arial,sans-serif; font-size: 62.5%; color: #333333}
		.content {height: 100%; overflow-y: auto; padding: 14px 15px 0 25px;}
		.content p {margin: 0; padding: 0;}
		.content .user {font-size: 1.8em; color: #3e3e3e; font-weight: bold; letter-spacing: -1px; margin-top: 0.5em;}
		.content .message {font-size: 1.3em; color: #444444; line-height: 1.7em; word-wrap: break-word;}
		.editor {margin: 0 25px 15px 25px;}
		.editor .user {font-size: 1.5em; display: inline-block; margin: 1em;}
		.editor input {font-family: 'Trebuchet MS','Malgun Gothic',Verdana,Helvetica,Arial,sans-serif;}
		.editor .message {width: 100%; height: 28px; line-height: 28px; border: medium none; border-color: #E5E5E5 #DBDBDB #D2D2D2; border-style: solid; border-width: 1px;}
		</style>
	</head>
	<body>
		<div id="content" class="content">
			<p class="user"><span>Donghwan Kim</span></p>
			<p class="message">Welcome to jQuery Stream!</p>
		</div>
		<div id="editor" class="editor">
			<p class="user"></p>
			<form action="#" onsubmit="return false;">
				<input class="message" type="text" disabled="disabled" />
			</form>
		</div>
	</body>
</html>

 

另需要

jquery-1.5.0.js

jquery.stream-1.2.js

参考 https://code.google.com/p/jquery-stream/

 

压力测试插件

https://github.com/kawasima/jmeter-websocket

分享到:
评论

相关推荐

    cometd-archetype-spring-dojo-jetty7-2.8.0-RC1.zip

    CometD是一个开源的WebSocket和AJAX双向通信框架,它实现了Bayeux协议,允许服务器主动向客户端推送数据,实现低延迟的实时通信。Spring框架则为这个项目提供了强大的依赖注入和面向切面编程能力,使得服务端组件的...

    cometd 框架实现的几个小程序

    CometD是一个基于Bayeux协议的开源框架,专门用于实现服务器到客户端的实时双向通信。这个框架在Web应用中非常有用,特别是对于需要实时更新数据的场景,比如聊天、股票报价、在线游戏等。CometD的核心理念是提供一...

    PushLet实例,可直接运行

    PushLet是一种基于Java的实时推送技术,用于在服务器和客户端之间进行双向通信,尤其是在Web应用中实现数据的即时更新。这个实例"PushLet实例,可直接运行"提供了一个完整的、可以直接执行的PushLet项目,包括必要的...

    网页聊天页面

    4. **WebSockets**:实现实时通信的关键技术,它提供双向通信通道,使得服务器可以即时推送消息到客户端,而不仅仅是客户端发起请求时才交互。 5. **AJAX**:用于异步数据交换,比如加载更多聊天记录,或者在用户...

    java web 聊天室 源码

    在这个项目中,serverpush可能就是指利用WebSocket实现实时推送消息的技术,确保用户可以即时看到新消息。 5. **Session管理**:为了识别不同的在线用户,聊天室会使用Session来存储用户信息。每个用户的会话信息...

    JAVA+JSPliaotianshi.rar_jsp 源码_jsp 聊天室

    8. **WebSocket**:如果聊天室需要实时通信功能,开发者可能会采用WebSocket协议,它允许双向通信,提供比AJAX更高效的数据推送方式。 9. **MVC(Model-View-Controller)架构**:为了保持代码结构清晰,开发者可能...

    JSP聊天系统(包含所有代码)

    5. **群聊与私聊实现**:群聊通常通过广播消息的方式实现,即Servlet接收到一条消息后,将其推送给所有在线用户。而私聊则需要根据会话中的用户信息,将消息定向发送给指定的接收者。 6. **安全性**:在聊天系统中...

    cometd-3.0.0.beta2-distribution.tar.gz

    它提供了服务器推送技术,使得服务器可以主动向客户端发送数据,而不仅仅是响应客户端的请求。CometD的核心理念是通过长连接来实现双向通信,极大地提高了Web应用的交互性和实时性。 在"cometd-3.0.0.beta2-...

    java web 聊天室源码.rar

    WebSocket协议使得聊天室能够高效地推送新消息给所有参与者。 4. **数据库**:聊天记录可能存储在数据库中,比如MySQL或Oracle,以便于历史记录的查询和检索。数据库操作可能通过JDBC(Java Database Connectivity...

    Java-chat-source.rar_java web 聊天_web chat

    WebSocket使得服务器可以主动推送消息到客户端,非常适合聊天应用。 4. **AJAX**:虽然WebSocket提供了实时通信,但在某些情况下,如旧版浏览器或简单交互,可能仍会使用Ajax(异步JavaScript和XML)来实现页面的...

    WEB简易聊天源码

    WebSocket提供了一种低延迟、全双工的通信方式,使得服务器可以主动推送信息到客户端,非常适合实时聊天应用。 4. **JSON数据交换**:在前后端通信中,JSON(JavaScript Object Notation)作为轻量级的数据交换格式...

    Web聊天室系统源码

    WebSocket提供了全双工通信,允许服务器主动向客户端推送数据,非常适合于聊天室这样需要实时更新的场景。 3. **JavaEE容器**:如Tomcat或Jetty,它们提供了一个运行JavaWeb应用程序的环境,管理Servlet的生命周期...

    简易在线聊天系统

    更高级的在线聊天系统可能会采用WebSocket协议,它提供全双工通信,允许服务器主动向客户端推送信息,比AJAX更高效,适合实时性强的聊天应用。 6. **数据库存储**: 聊天记录通常需要持久化存储,以便用户可以...

    网页版即时通讯源码

    WebSocket提供双向通信,允许服务器主动向客户端推送消息。 3. **消息存储**:为了保证消息的历史记录和离线消息,需要在数据库中存储消息。这可能涉及SQL或NoSQL数据库的选择,以及适当的数据库设计,如关系型...

    Web聊天室系统源码.zip

    WebSocket提供双向通信,允许服务器主动向客户端推送数据,解决了HTTP协议的不足。在Java中,`javax.websocket` API可以用来创建WebSocket服务端。 3. **MVC设计模式**:Model-View-Controller模式是Web应用开发中...

    Java做的企业短信管理平台

    6. **聊天平台**:实现聊天平台可能涉及WebSocket技术,它提供双向通信,允许服务器主动向客户端推送数据。Java中可以使用如Jetty或Tomcat服务器提供的WebSocket API来实现这一功能。 7. **安全性考虑**:任何企业...

    HtmlSocket:开源反向ajax框架

    2. **事件驱动**:基于事件模型,服务器端可以订阅和发布事件,当事件触发时,相关客户端会即时接收到通知,实现数据的实时推送。 3. **API友好**:对于开发者来说,HtmlSocket提供了一套简洁易用的API接口,使得在...

    chat_ajax.rar_电子书籍_Java_

    WebSocket提供双向全双工通信,使得客户端和服务器能持续保持连接,实时推送消息。在Java中,可以使用Java API for WebSocket (JSR 356)来创建WebSocket服务器端点和客户端端点。 五、聊天系统架构设计 一个完整的...

    基于springboot的电影订票网站.zip

    - 可能需要定时任务处理,如电影场次更新、优惠活动推送等,Spring Boot可以通过`@Scheduled`注解实现。 9. **测试** - Spring Boot提供了`@SpringBootTest`、`@WebMvcTest`等注解,方便进行单元测试和集成测试。...

Global site tag (gtag.js) - Google Analytics