“服务器推”之websocket实现之简单聊天室

原创 2015年07月08日 16:40:17

最近在学习Server-push的一些技术,websocket当然也要简单学习一下。

一个简单的websocket实现聊天室的例子:
websocket在tomcat中只有tomcat7支持,tomcat7以下的是没实现这个功能,而tomcat7以上的则是将其remove了,tomcat团队只是对version6中的一个bug作修复,不再继续开发,原因是被JSR356 websocket1.1的实现给代替了。
不过服务端使用JSR356的实现来开发websocket 服务端很方便,浏览器端使用websocket javascript api来编写也很简单方便,不足之处是浏览器对javascript websocket的支持还没那么广泛(Firefox39,chrome38和360都可以,ie8不行,其中360虽然是IE内核,但是它有自己的使用模块)。
IE不支持的解决方案:利用Flash实现websocket的通信功能,其实现请可以参照这位网友的代码:

点击打开链接

点击打开链接

websocket javascript api:websocket javascript api

一、websocket Server端基于annotation的实现

package org.wz.jsrapi.websocket.server;

import java.io.IOException;
import java.util.Calendar;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.concurrent.atomic.AtomicInteger;

import javax.websocket.OnClose;
import javax.websocket.OnError;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint;

/**
 * 
 * @author wz
 * 基于annotation的实现
 */
@ServerEndpoint(value = "/wsWithAnnotation")
public class WebsocketServerWithAnnotation {

	private static final Calendar cl = Calendar.getInstance();
	private static final String NICK_PREFIX = "user";
	private static final AtomicInteger connectionIds = new AtomicInteger(0);
	private static final Set<WebsocketServerWithAnnotation> connections = new CopyOnWriteArraySet<WebsocketServerWithAnnotation>();

	private final String nickname;
	private Session session;

	public WebsocketServerWithAnnotation() {
		nickname = NICK_PREFIX + connectionIds.getAndIncrement();
	}

	@OnOpen
	public void open(Session session) {
		this.session = session;
		String message = String.format("%1$tF %1$tT %2$s ", cl, this.nickname + "joined!");
		connections.add(this);
		broadcast(message);
	}

	@OnMessage
	public void handleMessage(String message) {
		String newmessage = String.format("%1$tF %1$tT %2$s  %3$s", cl, this.nickname, message);
		broadcast(newmessage);
	}

	@OnClose
	public void end() {
		connections.remove(this);
		String message = String.format("%1$tF %1$tT %2$s %3$s", cl, this.nickname, "has disconnected.");
		broadcast(message);
	}

	@OnError
	public void onError(Session session,Throwable t) throws Throwable {
		System.err.println("chat error " + t.toString());
	}

	public static void broadcast(String message) {
		for (WebsocketServerWithAnnotation ws : connections) {
			try {
				synchronized (ws) {
					ws.session.getBasicRemote().sendText(message);
				}
			} catch (IOException e) {
				try {
					ws.session.close();
				} catch (IOException e1) {
				}

				connections.remove(ws);
				broadcast(String.format("%1$tF %1$tT %2$s  %3$s", cl, ws.nickname, "has been disconnected."));
			}
		}
	}
	
}

二、基于继承Endpoint的实现

package org.wz.jsrapi.websocket.server;

import java.io.IOException;
import java.util.Calendar;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.concurrent.atomic.AtomicInteger;

import javax.websocket.CloseReason;
import javax.websocket.Endpoint;
import javax.websocket.EndpointConfig;
import javax.websocket.MessageHandler;
import javax.websocket.Session;

/**
 * 
 * @author wz
 * 基于继承Endpoint的实现
 */
public class WebsocketServerInheritEndpoint extends Endpoint {

	private static final Calendar cl = Calendar.getInstance();
	private static final String NICK_PREFIX = "user";
	private static final AtomicInteger connectionIds = new AtomicInteger(0);
	private static final Set<WebsocketServerInheritEndpoint> connections = new CopyOnWriteArraySet<WebsocketServerInheritEndpoint>();

	private final String nickname;
	private Session session;

	public WebsocketServerInheritEndpoint() {
		nickname = NICK_PREFIX + connectionIds.getAndIncrement();
	}

	//没有onmessage方法,在建立连接后由MessageHandler处理
	public void onOpen(Session session, EndpointConfig config) {
		this.session = session;
		String message = String.format("%1$tF %1$tT %2$s ", cl, this.nickname + "  joined!");
		connections.add(this);
		session.addMessageHandler(new ChatMessageHandler());
				
		broadcast(message);

	}
//移到ChatMessageHandler中去
//	public void handleMessage(String message) {
//		String newmessage = String.format("%1$tF %1$tT %2$s  %3$s", cl, this.nickname, message);
//		broadcast(newmessage);
//	}

	@Override
	public void onClose(Session session1, CloseReason closereason) {
		connections.remove(this);
		String message = String.format("%1$tF %1$tT %2$s %3$s", cl, this.nickname, "has disconnected.");
		broadcast(message);
	}

	@Override
	public void onError(Session session, Throwable t) {
		System.err.println("chat error " + t.toString());
	}

	public static void broadcast(String message) {
		for (WebsocketServerInheritEndpoint ws : connections) {
			try {
				synchronized (ws) {
					ws.session.getBasicRemote().sendText(message);
				}
			} catch (IOException e) {
				try {
					ws.session.close();
				} catch (IOException e1) {
				}

				connections.remove(ws);
				broadcast(String.format("%1$tF %1$tT %2$s  %3$s", cl, ws.nickname, "has been disconnected."));
			}
		}
	}

	private class ChatMessageHandler implements MessageHandler.Whole<String> {

		@Override
		public void onMessage(String message) {
			String formatMessage = String.format("%1$tF %1$tT %2$s  %3$s", cl, nickname, message);
			broadcast(formatMessage);
		}
	}

}

三、部署配置类

package org.wz.jsrapi.websocket.config;

import java.util.HashSet;
import java.util.Set;

import javax.websocket.Endpoint;
import javax.websocket.server.ServerApplicationConfig;
import javax.websocket.server.ServerEndpointConfig;

import org.wz.jsrapi.websocket.server.WebsocketServerInheritEndpoint;

/**
 * 
 * @author wz
 * 
 * 将websocket应用部署在web应用里的配置:
 * 1.要实现ServerApplicationConfig接口
 * 2.使用web容器的扫描机制[在servlet3.0中定义的]来扫描websocket的实现类
 * 3.一个容器中可以有多个ServerApplicationConfig
 */
public class WebsocketDeployConfig implements ServerApplicationConfig{

	/**
	 * 扫描部署文件被加以@ServerEndPoint的class(可以写上自己的过滤代码),由容器调用
	 * 
	 */
	@Override
	public Set<Class<?>> getAnnotatedEndpointClasses(Set<Class<?>> set) {
		//不作过滤了,直接返回set(其实里面就一个:WebsocketServerWithAnnotation)
		return set;		
	}

	
	/**
	 * 扫描部署文件中继承Endpoint的class(可以写上自己的过滤代码),由容器调用
	 * 参数是Endpoint的子类集合,返回的是ServerEndpoint的集合,所以我们要在其中作处理的
	 * 
	 */
	@Override
	public Set<ServerEndpointConfig> getEndpointConfigs(Set<Class<? extends Endpoint>> set) {
		
		Set<ServerEndpointConfig> result = new HashSet<ServerEndpointConfig>();
		
		//只写了这么一个,指定其访问path:/wsInheritEndpoint就Build它就OK了
		if(set.contains(WebsocketServerInheritEndpoint.class)){
			result.add(ServerEndpointConfig.Builder.create(WebsocketServerInheritEndpoint.class, "/wsInheritEndpoint").build());
		}
		return result;
	}

}

四、HTMLclient端

<html>
<head>
<title>websocket test: chat</title>
<style type="text/css">
input#chat {
	width: 410px
}

#console-container {
	width: 400px;
}

#console {
	border: 1px solid #CCCCCC;
	border-right-color: #999999;
	border-bottom-color: #999999;
	height: 170px;
	overflow-y: scroll;
	padding: 5px;
	width: 100%;
}

#console p {
	padding: 0;
	margin: 0;
}
</style>
<script type="text/javascript">
        var Chat = {};

        Chat.socket = null;

        Chat.connect = (function(host) {

            if ('WebSocket' in window) {
                Chat.socket = new WebSocket(host);
            } else if ('MozWebSocket' in window) {
                Chat.socket = new MozWebSocket(host);
            } else {
                Console.log('Error: WebSocket is not supported by this browser.');
                return;
            }
			
            Chat.socket.onopen = function () {
                Console.log('Info: WebSocket connection opened.');
                document.getElementById('chat').onkeydown = function(event) {
                    if (event.keyCode == 13) {
                        Chat.sendMessage();
                    }
                };
            };

            Chat.socket.onclose = function () {
                document.getElementById('chat').onkeydown = null;
                Console.log('Info: WebSocket closed.');
            };

            Chat.socket.onmessage = function (message) {
                Console.log(message.data);
            };
            Chat.socket.onerror = function (){
            	Chat.socket.close(1000);
                Console.log('Info: WebSocket happened error!.');
            };
        });

        Chat.initialize = function() {
            if (window.location.protocol == 'http:') {
                Chat.connect('ws://' + window.location.host + '/websocket_jsr356/wsInheritEndpoint');
                 //两种服务端实现方式切换
                 //Chat.connect('ws://' + window.location.host + '/websocket_jdk/wsWithAnnotation');
            } else {
                Chat.connect('wss://' + window.location.host + '/websocket_jsr356/wsInheritEndpoint');
                 //两种服务端实现方式切换
                //Chat.connect('wss://' + window.location.host + '/websocket_jdk/wsWithAnotation');
            }
        };

        //在直接关闭页面在chrome下会触发error事件,unload的时候调用websocket.close()显示关闭即可。
        Chat.close = (function(){
            Chat.socket.close(1000);	//正常关闭,code=1000
            Console.log('Info: WebSocket closed!');
        	
        });
        
        Chat.sendMessage = (function() {
            var message = document.getElementById('chat').value;
            if (message != '') {
                Chat.socket.send(message);
                document.getElementById('chat').value = '';
            }
        });

        var Console = {};

        Console.log = (function(message) {
            var console = document.getElementById('console');
            var p = document.createElement('p');
            p.style.wordWrap = 'break-word';
            p.innerHTML = message;
			console.appendChild(p);
            while (console.childNodes.length > 25) {
                console.removeChild(console.firstChild);
            }
            console.scrollTop = console.scrollHeight;
        });
        
        if (window.attachEvent){	
        	//其实这儿暂时没用,因为IE根本不支持基于HTML5的websocket
        	window.attachEvent("onload",   Chat.initialize);
        	window.attachEvent("onunload", Chat.close);
        }else{
        	window.addEventListener("load", Chat.initialize, false);
        	window.addEventListener("unload", Chat.close, false);
        }
    </script>
</head>
<body>
	<p>
		<input type="text" placeholder="type and press enter to chat"
			id="chat" />
	</p>
	<div id="console-container">
		<div id="console" />
	</div>
	</div>
</body>
</html>

这种方式使用的是容器自动扫描,并不需要在web.xml中配置servlet,直接启动tomcat就可以了。官方文档比较推荐将其作为单独的应用启动,不过要麻烦一点儿。

参考资料:

另外,推荐一个学习网站:
版权声明:本文为博主原创文章,未经博主允许不得转载。 举报

相关文章推荐

使用四种框架分别实现百万websocket常连接的服务器

目录 [−] 服务器的参数调优 TCP/IP参数配置最大文件描述符应用运行时调优OutOfMemory Killer 客户端的参数调优服务器测试 Netty服务器Spray服务...

使用四种框架分别实现1百万websocket常连接的服务器

事实上,最近我又增加了几个框架,现在包括 Netty, Undertow, Jetty, Spray, Vert.x, Grizzly 和 Node.js其中框架。 测试数据可以看下一篇文章: ...
  • yczz
  • yczz
  • 2016-06-07 14:32
  • 964

我是如何成为一名python大咖的?

人生苦短,都说必须python,那么我分享下我是如何从小白成为Python资深开发者的吧。2014年我大学刚毕业..

WebSocket服务器端解决方案

虽然还是菜鸟,但也希望能做点贡献,所以今天把自己工作中总结的一点东西写下来,希望对朋友们有所帮助。 WebSocket服务器端解决方案:        目前支持WebSocket的服务器只有J...

C# SuperWebSocket服务端、客户端学习(三)

1、打开VS2012,新建一个windows窗体程序,选择.NET4.0版本 2、添加引用     SuperSocket的dll文件( SuperSocket.Common.dll, Sup...

使用WebSocket实现即使通讯(实现一个群聊的聊天室)

随着互联网的发展,传统的HTTP协议已经很难满足Web应用日益复杂的需求了。近年来,随着HTML5的诞生,WebSocket协议被提出,它实现了浏览器与服务器的全双工通信,扩展了浏览器与服务端的通信功...
  • canot
  • canot
  • 2016-04-13 09:53
  • 12965

“服务器推”之websocket实现之简单聊天室

最近在学习Server-push的一些技术,websocket当然也要简单学习一下。 一个简单的websocket实现聊天室的例子: websocket在tomcat中只有tomcat7支持,tomc...

websocket 简介、实现简单聊天室

WebSocket 前世今生 众所周知,Web 应用的交互过程通常是客户端通过浏览器发出一个请求,服务器端接收请求后进行处理并返回结果给客户端,客户端浏览器将信息呈现,这种机制对于信息变化不是特别...

WebSocket实现简单的web聊天室

WebSocket实现简单的web聊天室 1.需要Tomcat7.0所以服务器 2.需要JDK7.0 3.手工加入Tomcat7.0中lib目录下的一下三个包catalina.jar、t...

websocket实现简单的聊天室

1创建一个maven工程2页面代码(index.jsp)3java代码(ChatroomServlet.java)4功能实现Guest 0 加入 Guest 1 加入 并聊天 Guest 1 退出
返回顶部
收藏助手
不良信息举报
您举报文章:深度学习:神经网络中的前向传播和反向传播算法推导
举报原因:
原因补充:

(最多只允许输入30个字)