java websocket实现点对点即时聊天

算是一个入门的demo,使用的是springMVC。

必要环境:JDK1.7以上,tomcat7.0以上。以下是干货:

1、websocket的jar直接从tomcat运行库里面添加到build path里面。

2、前台聊天页面,通过ws://localhost:8080/newProject/websocketTest与后台建立连接

 

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>

<title>My JSP 'socketChart.jsp' starting page</title>
<script type="text/javascript" src="js/jquery-1.8.2.js"></script>
</head>

<body>
	${username}<br/>
	在线人数:<div id="onlineCount">0</div>
	<br />
	发送对象:<input id="username" type="text" width="50px"/>内容:<input id="text" type="text" />
	<button onclick="send()">发送消息</button>
	<hr />
	<button onclick="closeWebSocket()">关闭WebSocket连接</button>
	<hr />
	<div id="message"></div>
</body>
<script type="text/javascript">
	var websocket = null;
	//判断当前浏览器是否支持WebSocket
	if ('WebSocket' in window) {
		websocket = new WebSocket("ws://localhost:8080/newProject/websocketTest");
	} else {
		alert('当前浏览器 Not support websocket')
	}
	//连接发生错误的回调方法
	websocket.onerror = function() {
		setMessageInnerHTML("WebSocket连接发生错误");
	};
	//连接成功建立的回调方法
	websocket.onopen = function() {
		setMessageInnerHTML("WebSocket连接成功");
	}
	//接收到消息的回调方法
	websocket.onmessage = function(event) {
		debugger
		var messageJson=eval("("+event.data+")");
		if(messageJson.messageType=="message"){
			setMessageInnerHTML(messageJson.data);
		}
		if(messageJson.messageType=="onlineCount"){
			document.getElementById('onlineCount').innerHTML=messageJson.data;
		}
		
	}
	//连接关闭的回调方法
	websocket.onclose = function() {
		setMessageInnerHTML("WebSocket连接关闭");
	}
	//监听窗口关闭事件,当窗口关闭时,主动去关闭websocket连接,防止连接还没断开就关闭窗口,server端会抛异常。
	window.onbeforeunload = function() {
		closeWebSocket();
	}
	//将消息显示在网页上
	function setMessageInnerHTML(innerHTML) {
		document.getElementById('message').innerHTML += innerHTML + '<br/>';
	}
	//关闭WebSocket连接
	function closeWebSocket() {
		websocket.close();
	}
	//发送消息
	function send() {
		var message = document.getElementById('text').value;
		var username = document.getElementById('username').value;
		websocket.send(username+"@"+message);
		document.getElementById('message').innerHTML += message + '<br/>';
	}
</script>
</html>

3、登录页面,简单的创建一个session会话模拟用户登录用作点对点聊天的唯一标识。

 

 

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%
	String path = request.getContextPath();
	String basePath = request.getScheme() + "://"
			+ request.getServerName() + ":" + request.getServerPort()
			+ path + "/";
%>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<base href="<%=basePath%>">

<title>My JSP 'login.jsp' starting page</title>

</head>

<body>
<center>
	<form action="chatWebsocket/login.do" method="post">
		<table>
			<tr>
				<td>username:</td><td><input type="text" id="username" name="username"/></td>
			</tr>
			<tr>
				<td colspan="2"> <input type="submit" value="登录"/></td>
			</tr>
		</table>
	</form>
</center>
</body>
</html>

4、controller类,模拟登录创建会话,并向socket工具类里面传送登录用户的唯一标识。

 

 

package com.test.controller;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

import com.test.socket.WebSocketTest;

@Controller
@RequestMapping("chatWebsocket")
public class ChartWebsocketController {
	@RequestMapping("login")
	public void login(String username,HttpServletRequest request,HttpServletResponse response) throws Exception{
		HttpSession session=request.getSession();
		session.setAttribute("username", username);
		WebSocketTest.setHttpSession(session);
		request.getRequestDispatcher("/socketChart.jsp").forward(request, response);
	}
	@RequestMapping("loginOut")
	public void loginOut(HttpServletRequest request,HttpServletResponse response) throws Exception{
		HttpSession session=request.getSession();
		session.removeAttribute("username");
		request.getRequestDispatcher("/socketChart.jsp").forward(request, response);
	}
}

 

 

 

 

 

5、socket消息处理类,负责消息的接收与转发,作用类似于TCP的服务端,@ServerEndpoint("/websocketTest")的作用是声明websocket的连接路径。

package com.test.socket;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;

import javax.servlet.http.HttpSession;
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;
import com.google.gson.Gson;

/**
 * @ServerEndpoint
 */
@ServerEndpoint("/websocketTest")
public class WebSocketTest {
	private static int onlineCount = 0;
	//存放所有登录用户的Map集合,键:每个用户的唯一标识(用户名)
	private static Map<String,WebSocketTest> webSocketMap = new HashMap<String,WebSocketTest>();
	//session作为用户简历连接的唯一会话,可以用来区别每个用户
	private Session session;
	//httpsession用以在建立连接的时候获取登录用户的唯一标识(登录名),获取到之后以键值对的方式存在Map对象里面
	private static HttpSession httpSession;
	
	public static void setHttpSession(HttpSession httpSession){
		WebSocketTest.httpSession=httpSession;
	}
	/**
	 * 连接建立成功调用的方法
	 * @param session
	 * 可选的参数。session为与某个客户端的连接会话,需要通过它来给客户端发送数据
	 */
	@OnOpen
	public void onOpen(Session session) {
		Gson gson=new Gson();
		this.session = session;
		webSocketMap.put((String) httpSession.getAttribute("username"), this);
		addOnlineCount(); // 
		MessageDto md=new MessageDto();
		md.setMessageType("onlineCount");
		md.setData(onlineCount+"");
		sendOnlineCount(gson.toJson(md));
		System.out.println(getOnlineCount());
	}
	/**
	 * 向所有在线用户发送在线人数
	 * @param message
	 */
	public void sendOnlineCount(String message){
		for (Entry<String,WebSocketTest> entry  : webSocketMap.entrySet()) {
			try {
				entry.getValue().sendMessage(message);
			} catch (IOException e) {
				continue;
			}
		}
	}
	
	/**
	 * 连接关闭调用的方法
	 */
	@OnClose
	public void onClose() {
		for (Entry<String,WebSocketTest> entry  : webSocketMap.entrySet()) {
			if(entry.getValue().session==this.session){
				webSocketMap.remove(entry.getKey());
				break;
			}
		}
		//webSocketMap.remove(httpSession.getAttribute("username"));
		subOnlineCount(); // 
		System.out.println(getOnlineCount());
	}

	/**
	 * 服务器接收到客户端消息时调用的方法,(通过“@”截取接收用户的用户名)
	 * 
	 * @param message
	 *            客户端发送过来的消息
	 * @param session
	 *            数据源客户端的session
	 */
	@OnMessage
	public void onMessage(String message, Session session) {
		Gson gson=new Gson();
		System.out.println("收到客户端的消息:" + message);
		StringBuffer messageStr=new StringBuffer(message);
		if(messageStr.indexOf("@")!=-1){
			String targetname=messageStr.substring(0, messageStr.indexOf("@"));
			String sourcename="";
			for (Entry<String,WebSocketTest> entry  : webSocketMap.entrySet()) {
				//根据接收用户名遍历出接收对象
				if(targetname.equals(entry.getKey())){
					try {
						for (Entry<String,WebSocketTest> entry1  : webSocketMap.entrySet()) {
							//session在这里作为客户端向服务器发送信息的会话,用来遍历出信息来源
							if(entry1.getValue().session==session){
								sourcename=entry1.getKey();
							}
						}
						MessageDto md=new MessageDto();
						md.setMessageType("message");
						md.setData(sourcename+":"+message.substring(messageStr.indexOf("@")+1));
						entry.getValue().sendMessage(gson.toJson(md));
					} catch (IOException e) {
						e.printStackTrace();
						continue;
					}
				}
				
			}
		}
		
	}

	/**
	 * 发生错误时调用
	 * 
	 * @param session
	 * @param error
	 */
	@OnError
	public void onError(Session session, Throwable error) {
		error.printStackTrace();
	}

	/**
	 * 这个方法与上面几个方法不一样。没有用注解,是根据自己需要添加的方法。
	 * 
	 * @param message
	 * @throws IOException
	 */
	public void sendMessage(String message) throws IOException {
		this.session.getBasicRemote().sendText(message);
		// this.session.getAsyncRemote().sendText(message);
	}

	public static synchronized int getOnlineCount() {
		return onlineCount;
	}

	public static synchronized void addOnlineCount() {
		WebSocketTest.onlineCount++;
	}

	public static synchronized void subOnlineCount() {
		WebSocketTest.onlineCount--;
	}
}

6、消息封装的数据传输对象

package com.chart.dto;

public class MessageDto {
	private String messageType;
	private String data;
	public String getMessageType() {
		return messageType;
	}
	public void setMessageType(String messageType) {
		this.messageType = messageType;
	}
	public String getData() {
		return data;
	}
	public void setData(String data) {
		this.data = data;
	}
	
}

 

 

 

 

 

 

 

 

 

  • 3
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 15
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 15
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值