基于websocket的简易聊天室的实现

用java实现基于websocket的简易聊天室



   WebSocket protocol 是HTML5一种新的协议。它实现了浏览器与服务器全双工通信(full-duplex)。一开始的握手需要借助HTTP请求完成。

                                                                                                                                                                                                                                                  ————  引自 百度百科

    简介:传统的使用http协议的web应用在实时通信方面存在很大的局限性,由于服务器只能等客户端浏览器主动发出request才能响应一个response,使客户端总要向服务器发出请求才能得到想要的消息(也可以用长连接让客户端hold住这个请求等类似的方式,但是这些方法都会占用比较多的服务器资源),随着html5到来的websocket使web应用的能更方便的实现实时通信。websocket和http协议一样也是基于tcp/ip协议的应用层的协议,它只需在第一次和服务器端连接是借用http协议与服务器端握手,然后就从http协议切换成websocket协议(我们可以把这个过程看做是http升级成了webstocket),关于握手可以参照这篇博文:http://blog.csdn.net/edwingu/article/details/44040961,本文不详细地介绍WebSocket规范,主要介绍下WebSocket在Java Web中的实现。

     websocket有如此强大的功能,作为在web领域数一数二的java当然也提供了实现方法,JavaEE 7中出了JSR-356:Java API for WebSocket规范。不少Web容器,如Tomcat,Nginx,Jetty等都支持WebSocket。Tomcat从7.0.27开始支持WebSocket,从7.0.47开始支持JSR-356,下面我们所要写的简易聊天室也是需要部署在Tomcat7.0.47以上的版本才能运行。

       1、项目演示 : 用两个浏览器模拟多个用户通信


基于websocket的简易聊天室的实现_第1张图片

基于websocket的简易聊天室的实现_第2张图片 

       1.1 创建项目,在这里我用maven和springMVC搭建的,不用也一样源码会全部给出,下方附上项目项目源码下载

基于websocket的简易聊天室的实现_第3张图片

       1.2   后台代码     User.java,这个没什么讲的

package com.chat.pojo;
/**
 * 用户类
 * @author chenxin
 *
 */
public class User {
	private Integer id;
	private String name;
	private String password;
	public Integer getId() {
		return id;
	}
	public void setId(Integer id) {
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public String getPassword() {
		return password;
	}
	public void setPassword(String password) {
		this.password = password;

	}
	@Override

	public String toString() {
		return "User [id=" + id + ", name=" + name + ", password=" + password
				+ "]";
	}
}

    1.3  UserController.java ,控制层

package com.chat.controller;

import java.util.Collection;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;

import javax.servlet.http.HttpServletRequest;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import com.chat.pojo.User;

/**
 * 用户登录
 * 主要是学习websocket,数据库和权限判断就不写了
 * @author chenxin
 *
 */
@Controller
@RequestMapping("/user")
public class UserController {
	
	//分配user的id,需设计为线程安全的
	private static int count=1;
	
	//在线用户列表,需设计成线程安全的
	private static List userList = new  CopyOnWriteArrayList();
	
	
	/**
	 * 跳转到登陆页面
	 * @return
	 */
	@RequestMapping("/tologin")
	public String toregister(){
		return "login";
	}	
	
	/**
	 * 登陆
	 * @param user
	 * @param request
	 * @return
	 */
	@RequestMapping("/login")
	public String login(User user,HttpServletRequest request){
		//生成id
		user.setId(count);
		//id增长
		UserController.increase();
		//把用户存入session域中
		request.getSession().setAttribute("user", user);
		//把登陆用户传入用户列表中
		userList.add(user);
		return "index";
	}
	
	/**
	 * 得到在线人数及用户名
	 * @param request
	 * @return
	 */
	@RequestMapping("/getAll")
	public @ResponseBody Collection getAllUser(HttpServletRequest request){
		return UserController.userList;
	}
	
	/**
	 * 下线,当页面关闭时前台页面通过ajax调用该handler
	 * @return
	 */
	@RequestMapping("/downLine")
	public void downLine(HttpServletRequest request){
		//得到session中的user
		User user = (User)request.getSession().getAttribute("user");
		//遍历用户列表,删除自己
		for(User item:userList){
			if(user.getId()==item.getId())
				userList.remove(item);
		}
	}
	//用来id增长
	private static synchronized void  increase(){
		 UserController.count++;
	}
}

    1.4   WebsocketChat.java  整个小项目的核心代码,websocket连接有该类接受建立

package com.ssm.websocket;

import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.CopyOnWriteArraySet;

import javax.servlet.http.HttpSession;
import javax.websocket.*;
import javax.websocket.server.ServerEndpoint;

import com.chat.pojo.User;

/**
 * @ServerEndpoint 注解是一个类层次的注解,它的功能主要是将目前的类定义成一个websocket服务器端,
 * 注解的值将被用于监听用户连接的终端访问URL地址,客户端可以通过这个URL来连接到WebSocket服务器端
 * 由于此类不是servlet类,要通过另一种方法才能的到HttpSession
 * configurator 属性 通过GetHttpSessionConfigurator类得到httpsession
 */

@ServerEndpoint(value="/websocket" ,configurator=GetHttpSessionConfigurator.class)//得到httpSession
public class WebSocketChat {
    //静态变量,用来记录当前在线连接数。应该把它设计成线程安全的。
    private static int onlineCount = 0;

    //concurrent包的线程安全Set,用来存放每个客户端对应的MyWebSocket对象。若要实现服务端与单一客户端通信的话,可以使用Map来存放,其中Key可以为用户标识
    private static CopyOnWriteArraySet webSocketSet = new CopyOnWriteArraySet();

    //与某个客户端的连接会话,需要通过它来给客户端发送数据
    private Session session;
     
    //当前会话的httpession
    private HttpSession httpSession;
   
     /**
     * 连接建立成功调用的方法
     * @param session  可选的参数。session为与某个客户端的连接会话,需要通过它来给客户端发送数据
     * @param config   有
     */
    @OnOpen
    public void onOpen(Session session,EndpointConfig config){
    	//得到httpSession
    	this.httpSession = (HttpSession) config.getUserProperties()
                .get(HttpSession.class.getName());
    	this.session = session;
        webSocketSet.add(this);     //加入set中
        addOnlineCount();           //在线数加1
        System.out.println("有新连接加入!当前在线人数为" + getOnlineCount());
    }

    /**
     * 连接关闭调用的方法
     */
    
    @OnClose
    public void onClose(){
        webSocketSet.remove(this);  //从set中删除
        subOnlineCount();           //在线数减1
        System.out.println("有一连接关闭!当前在线人数为" + getOnlineCount());
    }

    /**
     * 收到客户端消息后调用的方法
     * @param message 客户端发送过来的消息
     * @param session 可选的参数
     */
    @OnMessage
    public void onMessage(String message, Session session) {
    	//获得消息发来时间
    	SimpleDateFormat sd = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        String time = sd.format(new Date());
        
    	System.out.println("来自客户端的消息:" + message);
    	//替换表情
     message = replaceImage(message);
     //得到当前用户名 
     User user = (User)this.httpSession.getAttribute("user"); 
     String name = user.getName(); 
     //群发消息 
     for(WebSocketChat item: webSocketSet){
         try { 
              item.sendMessage(name+" "+time+"
"+message);            } catch (IOException e) {                 e.printStackTrace(); continue;              }     }  }    /** * 发生错误时调用    * @param session    * @param error    * @OnError    */    public void onError(Session session, Throwable error){     System.out.println("发生错误");     error.printStackTrace();    }    /** * 这个方法与上面几个方法不一样。没有用注解,是根据自己需要添加的方法。    * @param message    * @throws IOException    */    public void sendMessage(String message) throws IOException{      this.session.getBasicRemote().sendText(message);      //this.session.getAsyncRemote().sendText(message);    }    public void sendMessage(String message,String userid) throws IOException{ }      //对count的加减获取同步      public static synchronized int getOnlineCount() {        return onlineCount;      }      public static synchronized void addOnlineCount() {        WebSocketChat.onlineCount++;      }      public static synchronized void subOnlineCount() {       WebSocketChat.onlineCount--;      }      //替换表情      private String replaceImage(String message){       for(int i=1;i<11;i++){        if(message.contains("<:"+i+":>")){          message = message.replace("<:"+i+":>", "");         }       }      return message;    } }

     1.5  GetHttpSessionConfigurator.java 
  

package com.ssm.websocket;

import javax.servlet.http.HttpSession;
import javax.websocket.HandshakeResponse;
import javax.websocket.server.HandshakeRequest;
import javax.websocket.server.ServerEndpointConfig;
import javax.websocket.server.ServerEndpointConfig.Configurator;
/*
 * 获取HttpSession的配置类
 * 只要在websocket类上@ServerEndpoint注解中加入configurator=GetHttpSessionConfigurator.class
 * 再在 @OnOpen注解的方法里加入参EndpointConfig config
 * 通过config得到httpSession
 */

public class GetHttpSessionConfigurator extends Configurator {

    @Override
    public void modifyHandshake(ServerEndpointConfig sec,
            HandshakeRequest request, HandshakeResponse response) {
        // TODO Auto-generated method stub
        HttpSession httpSession=(HttpSession) request.getHttpSession();
        sec.getUserProperties().put(HttpSession.class.getName(),httpSession);
    }
    
}

    2. 前台页面及js

      2.1 login.jsp

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



    用户登录
    
    
    


    
用户登录
 
快速登陆,无需注册

    2.2 index.jsp ,发起websocket请求放在了chat.js文件中,页面上的js代码是为了方便写路径,显示在线人数和用户下线的ajax方法

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


    聊天室
    
    
        
	


	
    
聊天室
当前在线用户
发送内容不能为空
    2.3  chat.js  里面是前台发送websocket请求的核心代码

$(function () { 
var websocket = null;
    //判断当前浏览器是否支持WebSocket
    if ('WebSocket' in window) {
        websocket = new WebSocket("ws://localhost:8080/chat/websocket");
    }
    else {
        alert('当前浏览器版本过低不支持websocket!!!')
    }

    //连接发生错误的回调方法
    websocket.onerror = function () {
        setMessageInnerHTML("WebSocket连接发生错误");
    }

    //连接成功建立的回调方法
    websocket.onopen = function () {
        setMessageInnerHTML("WebSocket连接成功");
    }

    //接收到消息的回调方法
    websocket.onmessage = function (event) {
    	showMessageInnerHTML(event.data);
    }

    //连接关闭的回调方法
    websocket.onclose = function () {
        setMessageInnerHTML("WebSocket连接关闭");
    }

    //监听窗口关闭事件,当窗口关闭时,主动去关闭websocket连接,防止连接还没断开就关闭窗口,server端会抛异常。
    //同时退出聊天室(下线)
    window.onbeforeunload = function () {
        closeWebSocket();
        downLine();
    }

    //将是否连接websocket消息显示在网页上
    function setMessageInnerHTML(innerHTML) {
        $("#message").append(innerHTML+"
"); } //将消息显示在页面上 function showMessageInnerHTML(innerHTML){ // document.getElementById('chat-dialog-con').innerHTML += innerHTML + '
'; $("#chat-dialog-con ul").append("
  • "+innerHTML+"
  • "); } //关闭WebSocket连接 function closeWebSocket() { websocket.close(); } //发送消息 function send() { var message = $("#txtInput").val(); websocket.send(message); } //添加表情 for (var i = 1; i <= 10; i++) { $("#chat-input-expr").html($("#chat-input-expr").html() + ""); //html和text方法不一样,前者可添加html标签和纯文本,后者只可添加纯文本。 } //向消息中添加表情 $("#chat-input-expr img").click(function () { $("#txtInput").val($("#txtInput").val() + "<:" + $(this).attr("ID") + ":>"); }); //6.发送消息判断 $("#btnSend").click(function () { var sendMsg = $("#txtInput"); if (sendMsg.val() != "") { send(); sendMsg.val("") } else { alert("发送内容不能为空!"); sendMsg.focus(); return false; } }); //得到在线用户 getUser(); setInterval("getUser()",3000); });
        以上为实现聊天室功能的代码,还有一些css样式在下面项目源码中给出

        项目源码:http://download.csdn.net/detail/aaa5438438/9589077

       转载请注明出处:http://blog.csdn.net/aaa5438438


    你可能感兴趣的:(websocket学习总结)