JAVA利用websocket实现多人聊天室、私信(附源码)

声明:此文为原创,转载请声明出处!

小编曾在毕业设计中用到了聊天室这个功能,现在稍作整理分享一下,希望能对大家有所帮助,有不足之处请指出

在学习websocket前,首先得知道它的一些基本操作,可参考此链接:

http://www.runoob.com/html/html5-websocket.html

为了方便演示,小编搭建了一个springmvc框架:


图1.1 聊天室首页

为了方便源码分享,小编舍弃了数据库部分,因此没有做创建房间的功能,直接写死了两个房间。


   图1.2 聊天室房间页


图1.3项目结构图

下面来看下代码:

如代码片段1 ,首先通过http://localhost:8080/Chatroom/home/list.do请求,访问index.jsp(图1.1 聊天室首页)

代码片段1:

package com.controller;

import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.SessionAttributes;

@Controller
@RequestMapping("/home")
@SessionAttributes("uname")
public class ViewController {
	
	@RequestMapping("/list")
	public String cc(ModelMap model){
		return "index";
	  }
	
	@RequestMapping("/room")
	public String h(ModelMap model,String uname,String roomid){
		model.put("uname",uname);
		model.put("roomid", roomid);
		return "room";
	}
}

代码片段2:

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<script type="text/javascript" src="../js/jquery-3.2.1.min.js"></script>
<title>Insert title here</title>
</head>
<script type="text/javascript">
	$(function() {
		$("span").click(function(){
			var uname = $("input").val();
			if(uname == ""){
				alert("请先输入用户名");
			}else {
				var roomid = $(this).html()
				location.href="/Chatroom/home/room.do?uname="+uname+"&roomid="+roomid;
			}
		})
	})
</script>
<style>
	span:HOVER{
		color: red;
	}
	span{
		cursor:pointer;
	}
</style>
<body>
	用户名:<input type="text">   /*注:请先输入用户名,且保证用户名唯一,再点击下面的房间加入房间
	<h1><span>room1</span></h1>
	<h1><span>room2</span></h1>
</body>
</html>

代码片段3和4代码量较多,中间有详细的注解,不再做更多的解释

代码片段3:

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%>
<base href="<%=basePath%>" />
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<script type="text/javascript" src="js/jquery-3.2.1.min.js"></script>
<link rel="stylesheet" type="text/css" href="css/chat.css" />
<title>聊天室</title>
</head>
<script type="text/javascript">
$(function(){
	var roomid=$(".roomid").html();//房间名
	var nickname = $(".uname").html();//自己的昵称
	var flag = "join";
	var info = flag + "|" +roomid + "|" +nickname;
	//建立一条与服务器之间的连接
	var socket = new WebSocket("ws://${pageContext.request.getServerName()}:${pageContext.request.getServerPort()}${pageContext.request.contextPath}/websocket/"+info);
	var text = "";
	var welcome = JSON.stringify({			//加入房间时的欢迎消息
						nickname:nickname,    //用户名
						content:text,		//消息内容
						target:roomid,		//推送到目标房间
						flag:"chatroom"});	//推送标识
						
	var exitroom = JSON.stringify({		//退出房间
		nickname:nickname,
		flag:"exitroom",
		roomid:roomid
	})
	//接收服务器的消息
	socket.onmessage=function(ev){
		var obj = eval(   '('+ev.data+')' );
		addMessage(obj)
	};
	//当服务端执行onopen后触发此方法
	socket.onopen = function(){
		socket.send(welcome); 
	};
	//发送按钮被点击时
	$(".ensure button").click(function(){
	    ensure();
    });
	
	$("body").keyup(function (event) {//监听回车键
		if (event.keyCode == "13") {//keyCode=13是回车键
			$(".ensure button").trigger("click");
		}
	});
	
	function ensure(){
		//获取输入框的内容
	    var txt = $(".center-input").val()
		if(txt==''){
			  alert("不能发送空内容")
		}else{
	    	//构建一个标准格式的JSON对象
	    	var obj = JSON.stringify({
		    		nickname:nickname,    //用户名
					content:txt,		//消息内容
					flag:'chatroom',			//标识--chatroom代表是聊天室的消息
					target:roomid	//消息推送的目的地
				});	
	        // 向服务器发送消息
	        socket.send(obj);
	     	// 清空消息输入框
	        $(".center-input").val("")
	        // 消息输入框获取焦点
	        $(".center-input").focus(); 
		}
	}
	
	function addMessage(msg){
		if(msg.isSelf&&msg.content==""){ //该消息是自己发送的,并且内容为空
			$(".center-info").append("<div class='welcome'>欢迎你加入群聊</div>");
			refreshMember(msg.uname);  //刷新成员 
		}
		if(!msg.isSelf&&msg.content==""){//该消息是别人发送的,并且内容为空
			$(".center-info").append("<div class='welcome'>欢迎"+msg.nickname+"加入群聊</div>");
			//刷新成员列表
			refreshMember(msg.uname)
		} 
		if(!msg.content==""){			//内容不为空时 
			var align;
			if(msg.isSelf){
				align = "right";
			}else{
				align = "left";
			}
			$(".center-info").append(
					"<div class='basicInfo' style=float:"+align+">"+
					"<div class='basicInfo-left' style=float:"+align+">"+
						"<img src='img/touxiang.jpg'>"+
					"</div>"+
					"<div class='basicInfo-right' style=float:"+align+">"+
						"<div class='username' style=text-align:"+align+">"+
							"<span>"+msg.nickname+"</span> "+
							"<span>"+msg.date+"</span>"+
						"</div>"+
						"<div class='context'>"+
							"<span>"+
								msg.content+
							"</span>"+
						"</div>"+
					"</div>"+
				"</div>"
			); 
		}
		if(msg.flag == "exitroom"){		//退出房间
			$(".center-info").append("<div class='welcome'>"+msg.message+"</div>");
			//刷新成员列表
			refreshMember(msg.uname)
		}
		$(".center-info").scrollTop(999999); //让滚动条始终保持在最下 
	}
	
	$(".exitroom").click(function(){			//退出房间
		socket.send(exitroom); //向服务器发送退出房间的信号
		location.href="/Chatroom/home/list.do"; //跳转到前一个页面
	})
	
	function refreshMember(data){
		$(".member").html("");
		for(var i=0;i<data.length;i++){
			$(".member").append(
				"<div class='memberInfo'>"+
					"<div class='userpic'>"+
						"<img src='img/touxiang.jpg'>"+
					"</div>"+
					"<span class='username'>"+data[i]+"</span>"+
				"</div>"
			)
		}
	}
}) 
</script>

<body>
	<div class="body-left">
		<div class="left-info">
			<div class="exitroom">
				<--退出房间
			</div>
			<div class="roomname">
				欢迎来到:<h1 style="display: inline-block;" class="roomid">${roomid }</h1>
			</div>
			<div class="member">
				<c:forEach items="${requestScope.memberlist }" var="member">
					<div class="memberInfo">
						<div class="userpic">
							<img src="img/touxiang.jpg">
						</div>
						<span class="username">${member.username }</span>
						<span style = "display:none">${member.userid }</span>
					</div>
				</c:forEach>
			</div>
		</div>
	</div>
	<div class="body-center">
		<div class="center-info">

		</div>
		<textarea class="center-input"></textarea>
		<div class="ensure">
			<button>发送</button>
		</div>
	</div>
	
	<div class="body-right">
	</div>
	
	<span class="uname" style="display:none">${sessionScope.uname }</span>
	
</body>

</html>

代码片段4:

package com.controller;

import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;

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


import net.sf.json.JSONObject;


@ServerEndpoint("/websocket/{info}")
public class WebSocketService {
	private static SimpleDateFormat df = new SimpleDateFormat("HH:mm:ss");//创建时间格式对象
	//concurrent包的线程安全Set,用来存放每个客户端对应的WebSocketService对象。
	//创建一个房间的集合,用来存放房间
	private static ConcurrentHashMap<String,ConcurrentHashMap<String, WebSocketService>> roomList = new  ConcurrentHashMap<String,ConcurrentHashMap<String, WebSocketService>>();
	//与某个客户端的连接会话,需要通过它来给客户端发送数据
	private Session session;
    //重新加入房间的标示;
    private int rejoin = 0;
    static {
    	roomList.put("room1", new ConcurrentHashMap<String, WebSocketService>());
    	roomList.put("room2", new ConcurrentHashMap<String, WebSocketService>());
    }
	/**
	 * 用户接入
	 * @param session 
	 */
	@OnOpen
	public void onOpen(@PathParam(value = "info") String param,Session session){
		this.session = session;
		String flag = param.split("[|]")[0]; 		//标识
		String member = param.split("[|]")[1];		//成员名
		if(flag.equals("join")){
			String user = param.split("[|]")[2];
			joinRoom(member,user);
			
		}
	}
	
	//加入房间
	public void joinRoom(String member,String user){
		ConcurrentHashMap<String, WebSocketService> r =  roomList.get(member);
		if(r.get(user) != null){		//该用户有没有出
			this.rejoin = 1;
		}
		r.put(user, this);//将此用户加入房间中
	}
	public void sendMessage(String message) throws IOException {
        this.session.getBasicRemote().sendText(message);
    }
	/**
	 * 接收到来自用户的消息
	 * @param message
	 * @param session
	 * @throws IOException 
	 */
	@OnMessage
	public void onMessage(String message,Session session) throws IOException{
		//把用户发来的消息解析为JSON对象
		JSONObject obj = JSONObject.fromObject(message);
		if(obj.get("flag").toString().equals("exitroom")){		//退出房间操作
			String roomid = obj.get("roomid").toString();
			//将用户从聊天室中移除
			int f2 = 1;
			roomList.get(roomid).remove(obj.get("nickname").toString());//将用户直接移除
			if(roomList.get(roomid).size() == 0){//判断房间该房间是否还有用户,如果没有,则将此房间也移除
				f2 = 2;
			}
			if(f2 == 1){		//证明该房间还有其它成员,则通知其它成员更新列表
				obj.put("flag","exitroom");
				String m = obj.get("nickname").toString()+" 退出了房间";
				obj.put("message", m);
				ConcurrentHashMap<String, WebSocketService> r =roomList.get(roomid);
				List<String> uname = new ArrayList<String>();
				for(String u:r.keySet()){
					uname.add(u);
				}
				obj.put("uname", uname.toArray());
				for(String i:r.keySet()){  //遍历该房间
					r.get(i).sendMessage(obj.toString());//调用方法 将消息推送
				}
			}
		}else if(obj.get("flag").toString().equals("chatroom")){		//聊天室的消息 加入房间/发送消息
			//向JSON对象中添加发送时间
			obj.put("date", df.format(new Date()));
			//获取客户端发送的数据中的内容---房间号 用于区别该消息是来自于哪个房间
			String roomid = obj.get("target").toString();
			//获取客户端发送的数据中的内容---用户
			String username = obj.get("nickname").toString();
			//从房间列表中定位到该房间
			ConcurrentHashMap<String, WebSocketService> r =roomList.get(roomid);
			List<String> uname = new ArrayList<String>();
			for(String u:r.keySet()){
				uname.add(u);
			}
			obj.put("uname", uname.toArray());
			if(r.get(username).rejoin == 0){			//证明不是退出重连
				for(String i:r.keySet()){  //遍历该房间
					obj.put("isSelf", username.equals(i));//设置消息是否为自己的
					r.get(i).sendMessage(obj.toString());//调用方法 将消息推送
				}
			}else{
				obj.put("isSelf", true);
				r.get(username).sendMessage(obj.toString());
			}
			r.get(username).rejoin = 0;
		}
		
	}
	
	/**
	 * 用户断开
	 * @param session
	 */
	@OnClose
	public void onClose(Session session){

	}
	
	/**
	 * 用户连接异常
	 * @param t
	 */
	@OnError
	public void onError(Throwable t){
		
	}
}

在代码片段4中,可能存在比较难理解的地方,小编通过自己的理解来画图,希望能够帮到大家。


图1.3 websocket执行流程


图1.4 用户加入房间


图1.5消息推送

私信、即时通信都可以利用websocket完成,原理跟聊天室是完全一样的。在此不再做更多的阐述~

小编的编辑经验尚浅,所以此文比较粗糙。不足之处请各位看官指出,不胜感激。

到此,聊天室的功能就演示完了,另附上项目源码

链接:https://pan.baidu.com/s/1NUgzDfU8IEhVHHn8BhddwA 密码:yk7o

环境:eclispe+jdk1.8+tomcat8.

实现在线多人聊天可以使用Java中的WebSocket协议,以下是一个简单的示例代码: 服务端代码: ```java import java.io.IOException; import java.util.Collections; import java.util.HashSet; import java.util.Set; import javax.websocket.OnClose; import javax.websocket.OnMessage; import javax.websocket.OnOpen; import javax.websocket.Session; import javax.websocket.server.ServerEndpoint; @ServerEndpoint("/chat") public class ChatServer { private static final Set<Session> sessions = Collections.synchronizedSet(new HashSet<Session>()); @OnOpen public void onOpen(Session session) { sessions.add(session); } @OnMessage public void onMessage(String message, Session session) throws IOException { synchronized (sessions) { for (Session s : sessions) { s.getBasicRemote().sendText(message); } } } @OnClose public void onClose(Session session) { sessions.remove(session); } } ``` 客户端代码: ```java import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.net.URI; import java.net.URISyntaxException; import javax.websocket.ClientEndpoint; import javax.websocket.CloseReason; import javax.websocket.ContainerProvider; import javax.websocket.DeploymentException; import javax.websocket.OnClose; import javax.websocket.OnMessage; import javax.websocket.OnOpen; import javax.websocket.Session; import javax.websocket.WebSocketContainer; @ClientEndpoint public class ChatClient { private static Object waitLock = new Object(); @OnOpen public void onOpen(Session session) { System.out.println("Connected to server"); } @OnMessage public void onMessage(String message, Session session) { System.out.println(message); } @OnClose public void onClose(Session session, CloseReason closeReason) { System.out.println(String.format("Session %s close because of %s", session.getId(), closeReason)); synchronized (waitLock) { waitLock.notify(); } } public static void main(String[] args) { WebSocketContainer container = ContainerProvider.getWebSocketContainer(); String uri = "ws://localhost:8080/chat"; try { Session session = container.connectToServer(ChatClient.class, new URI(uri)); BufferedReader reader = new BufferedReader(new InputStreamReader(System.in)); String line = ""; while (!line.equals("exit")) { line = reader.readLine(); session.getBasicRemote().sendText(line); } session.close(); } catch (DeploymentException | URISyntaxException | IOException e) { e.printStackTrace(); } } } ``` 在本地运行服务端(如Tomcat),然后运行客户端即可进行在线多人聊天。注意修改服务端代码中的端口号和路径。
评论 87
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值