JavaWeb--使用Websocket实现在线聊天功能

首先简单介绍下WebSocket,WebSocket是HTML5中内容,是基于TCP的一种新的网络协议,它支持全双工、长连接的通信。在它出现之前,实时消息发送与接收通过轮询实现,但是频繁与服务器建立连接十分消耗资源。因此WebSocket出现了,在不断开连接的情况下,处于连接的用户可以任意发送消息,从而实现了在线聊天的功能。

因为我们JavaWeb课程作业是实现在线聊天功能,看到这题目我内心是崩溃的,不停百度,偶然发现Tomcat中自带了一个WebSocket的小例子, 有点小激动,运行一看,它竟然是群聊的。。。没办法,只好查看他的源代码,经过几天的推敲,终于看懂他的代码,并改写成一对一私聊。下面上代码 

var Chat = {};
    Chat.socket = null;
    // 创建一个websocket实例
    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('Console.log:你的浏览器不支持WebSocket');
            return;
         }
        Chat.socket.onopen = function(){
             Console.log('Console.log:WebSocket链接打开');
             //按下回车键发送消息
             document.getElementById('chat').onkeydown = function(event) {
                 if (event.keyCode == 13) {
                     Chat.sendMessage();
                 }
             };
         };
         Chat.socket.onclose = function () {
             document.getElementById('chat').onkeydown = null;
             Console.log('Console.log:WebSocket前端链接关闭');
         };
         Chat.socket.onmessage = function (message) {
             Console.log(message.data);
         };
    });
    Chat.initialize = function() {
    	//链接地址选择聊天页面的URL  
        if (window.location.protocol == 'http:') {
            Chat.connect("ws://" + window.location.host + "/onLineChat/index.jsp");
        } else {
            Chat.connect("wss://" + window.location.host + "/onLineChat/index.jsp");
        }
    };
    //发送消息函数,后面动态添加了发送好友的唯一ID 
    Chat.sendMessage = (function() {
    	var fid = $("#hidden-fid").val();
        var messageContain = document.getElementById('chat').value;
        var message = messageContain +"-"+fid;
        if(fid==""){
        	alert("未选择发送消息的好友!");
        	return;
        }else{
        	if (messageContain != "") {
                Chat.socket.send(message);
                document.getElementById('chat').value = '';
            }else{
            	alert("发送消息不能为空!");
            }
        }
    });
     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);
         console.scrollTop = console.scrollHeight;
     });
    //初始化函数
     Chat.initialize();
     document.addEventListener("DOMContentLoaded", function() {
         var noscripts = document.getElementsByClassName("noscript");
         for (var i = 0; i < noscripts.length; i++) {
             noscripts[i].parentNode.removeChild(noscripts[i]);
         }
     }, false);

这是前端的JS代码,初始化WebSocket对象,监听对象的开启、关闭、发送消息等时间,将部分参数传给后台

package websocket.chat;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.http.HttpSession;
import javax.websocket.EndpointConfig;
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 org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;

import com.chat.common.HTMLFilter;
import com.chat.controller.UserDAOImpl;
import com.chat.entity.User;

@ServerEndpoint(value = "/index.jsp",configurator=GetHttpSessionConfigurator.class)
public class ChatAnnotation {
	private static final Log log = LogFactory.getLog(ChatAnnotation.class);
    private static final Map<String, ChatAnnotation> connection = new HashMap<String, ChatAnnotation>();
    private String userId;
    private String nickname;
    private Session session;
    public ChatAnnotation() {
    }
    @OnOpen
    public void start(Session session,EndpointConfig config) {
      //获取HttpSession对象
       HttpSession httpSession = (HttpSession) config.getUserProperties().get(HttpSession.class.getName());
       User user = (User)httpSession.getAttribute("user");
       String uid = user.getId();
       this.nickname = user.getUsername();
       this.userId = uid;
       
       this.session = session;
        /*判断链接是否已经在链接队列中,存在就不加*/
       if(!connection.containsKey(uid)){
    	   connection.put(uid, this);
    	   String message = "* 你已经链接";
    	   toConfirmFriend(message,userId);
       }
    }
    @OnClose
    public void end() {
        connection.remove(this.userId);
    }
    @OnMessage
    public void incoming(String message){
    	String[] all = message.split("-");
    	//要接收消息好友的ID
    	String uid = all[1];
    	//判断好友的连接是否存在,不存在就提示好友掉线
    	if(connection.containsKey(uid)){
    		 String filteredMessage = String.format("%s: %s",
    	                nickname, HTMLFilter.filter(all[0].toString()));
    	        sendCommonRecord(filteredMessage,uid,this.userId);
    	}else{
    		ChatAnnotation client = connection.get(uid);
    		 String mes = String.format("* %s %s",
                     client.nickname, "已经掉线!");
    		//将掉线信息发给自己
    		 toConfirmFriend(mes,this.userId);
    	}
    }
    @OnError
    public void onError(Throwable t) throws Throwable {
        log.error("Chat Error: " + t.toString(), t);
    }
    //发送给指定的好友
    private static void toConfirmFriend(String msg,String uid) {
        ChatAnnotation client = connection.get(uid);
        try {
            synchronized (client) {
                client.session.getBasicRemote().sendText(msg);
            }
        } catch (IOException e) {
            log.debug("Chat Error: Failed to send message to "+client.nickname, e);
            try {
                client.session.close();
            } catch (IOException e1) {
                // Ignore
            }
        }
    }
    //给聊天的双发发共同消息
    private static void sendCommonRecord(String msg,String sid,String gid) {
        ChatAnnotation client_send = connection.get(sid);
        ChatAnnotation client_get = connection.get(gid);
        try {
            synchronized (client_send) {
            	client_send.session.getBasicRemote().sendText(msg);
            }
            //将这条聊天记录保存在数据库
            UserDAOImpl userDAOImpl = new UserDAOImpl();
            userDAOImpl.saveCharRecord(msg, sid, gid);
        } catch (IOException e) {
            log.debug("Chat Error: Failed to send message to "+client_send.nickname, e);
            connection.remove(sid);
            try {
            	client_send.session.close();
            }catch (IOException e1) {
                // Ignore
            }
        }
        try {
            synchronized (client_get) {
            	client_get.session.getBasicRemote().sendText(msg);
            }
        } catch (IOException e) {
            log.debug("Chat Error: Failed to send message to "+client_get.nickname, e);
            connection.remove(gid);
            try {
            	client_get.session.close();
            } catch (IOException e1) {
                // Ignore
            }
        }
    }
}

这是后台代码,使用注解方式监听前台的事件
重点:
1)Tomcat中connection是一个set,此处改写成了map,键名保存用户的唯一ID,键值保存用户连接对象,所以只要确定了用户ID,就能向该用户发送消息。创建连接向map中添加,断开链接从map中移除。
2)如何将用户ID从前台传到后台,这里有两种方法,借鉴了这篇博客  http://blog.csdn.net/qq_33171970/article/details/55001587
采用HttpSession的方法在ChatAnnotation中访问session的属性
3)前台发送消息,动态将发送消息的接受者ID加在消息内容结尾,后台接收到数据,将消息内容和用户ID分离开
4)改写发送消息的方法,实现向特定好友发送消息,方法是根据已经传过来的好友ID,从Map中获取指定的连接对象,调用该对象的sendText(msg);方法实现私聊。
5)因为有个导出聊天记录的功能,所以,每发送成功一条消息,这条消息将被存进数据库,永久记录下来。

/****************************************************************************************************************************************/
这个是项目的完成图,已经实现了,前端是模仿网页版微信的。。。。

——————————————下载地址——————————————————

每次登录一批批发太累了,也有些遗漏没有发的情况,所以提供一个下载链接:点击打开链接

猜你喜欢

转载自blog.csdn.net/ae86jaychou/article/details/72872275
今日推荐