基于Java和websocket的在线聊天程序(可群发和选择用户)

来源:互联网 发布:网络十大公会 编辑:程序博客网 时间:2024/05/21 08:38

最近在开发程序过程中需要用到服务器端推送,查阅资料主要有三种方式:

  第一是使用ajax长轮询;

  第二是使用cmet4j;

  第三是使用websocket。

  关于这三种方式,websocket优点明显,主要包括:

(1)建立在 TCP 协议之上,服务器端的实现比较容易。

(2)与 HTTP 协议有着良好的兼容性。默认端口也是80和443,并且握手阶段采用 HTTP 协议,因此握手时不容易屏蔽,能通过各种 HTTP 代理服务器。

(3)数据格式比较轻量,性能开销小,通信高效。

(4)可以发送文本,也可以发送二进制数据。

(5)没有同源限制,客户端可以与任意服务器通信。

(6)协议标识符是ws(如果加密,则为wss),服务器网址就是 URL。

websocket的介绍网上有很多,用websocket实现的聊天室也很多,但功能都不完善(也许是我没看懂别人的代码),而且也不容易看懂。对于没有接触过这的来说,因为很多基础概念没弄懂,比如远程端点,websocket的session等。关于websocket有本教材——Java.WebSocket.Programming,国内翻译的感觉不怎么样,简单了解看前面四章就够了,下面是教材:http://www.java1234.com/a/javabook/javabase/2016/0605/6215.html

websocket需要websocket-api.jar这个包,这个包tomcat已经自带,因此不用手动将这个包导入自己的项目;传输数据使用JSON的jar包(6个)需要添加到自己的项目中。

一、服务器代码

package com;import java.io.IOException;import java.util.ArrayList;import java.util.List;import java.util.Set;import java.util.concurrent.ConcurrentHashMap;import java.util.concurrent.ConcurrentMap;import net.sf.json.*; import javax.websocket.*;import javax.websocket.server.*;@ServerEndpoint(value = "/chat")public class EndPoint{//远程endPoint,使用userName作为Key进行绑定private static ConcurrentMap<String,EndPoint>clientSet = new ConcurrentHashMap<>(); //连接时,获得用户名private String user;//_websocket会话private Session session;@OnOpenpublic void start(Session session){this.session = session;user=this.session.getRequestParameterMap().get("user").get(0);clientSet.put(user, this);List<String>list =new ArrayList<>( clientSet.keySet());    broadcast(list);        Message msg=new Message("server","All",String.format("【%s %s】",user, "加入了聊天室!"));sendMessage(msg);}// 当端断开连接时自动激发该方法@OnClosepublic void end(){clientSet.remove(user);Message msg=new Message("server","All",String.format("【%s %s】",user, "离开了聊天室!"));sendMessage(msg);List<String>list =new ArrayList<>( clientSet.keySet());    broadcast(list);}// 每当收到客户端消息时自动激发该方法@OnMessagepublic void onMessage(String message){JSONObject json = JSONObject.fromObject(message);    Message msg = (Message)JSONObject.toBean(json,Message.class);sendMessage(msg);}// 当客户端通信出现错误时,激发该方法@OnErrorpublic void onError(Throwable t) throws Throwable{System.out.println("WebSocket服务端错误 " + t);}// 发送消息private static void sendMessage(Message msg){//如果为all,遍历历用户列表,逐个发送,否则只发送给一个if(msg.getTo().equals("All")){Set<String>clientKey = clientSet.keySet();    for(String k: clientKey){    EndPoint client=clientSet.get(k);    try{synchronized (client){// 发送消息client.session.getBasicRemote().sendText(JSONObject.fromObject(msg).toString());}}catch (IOException e){clientSet.remove(k);try{client.session.close();}catch (IOException e1){}Message msgError=new Message("server","All",String.format("【%s %s】",k, "已经被断开了连接。"));sendMessage(msgError);List<String>list =new ArrayList<>( clientSet.keySet());    broadcast(list);}    }    }else{    EndPoint client=clientSet.get(msg.getTo());    try{synchronized (client){// 发送消息client.session.getBasicRemote().sendText(JSONObject.fromObject(msg).toString());}}catch (IOException e){clientSet.remove(msg.getTo());try{client.session.close();}catch (IOException e1){}Message msgError=new Message("server","All",String.format("【%s %s】",msg.getTo(), "已经被断开了连接。"));sendMessage(msgError);List<String>list =new ArrayList<>( clientSet.keySet());    broadcast(list);}        EndPoint clientSend=clientSet.get(msg.getFrom());    try{synchronized (clientSend){// 发送消息clientSend.session.getBasicRemote().sendText(JSONObject.fromObject(msg).toString());}}catch (IOException e){}    }}// 广播用户列表消息,客户端更新private static void broadcast(List<String> list){    for(String k: list){    try{synchronized (k){// 发送消息clientSet.get(k).session.getBasicRemote().sendText(JSONArray.fromObject(list).toString());}}    catch (IOException e){System.out.println("聊天错误,向客户端 "+ k + " 发送消息出现错误。");clientSet.remove(k);try{clientSet.get(k).session.close();}catch (IOException e1){}Message msgError=new Message("server","All",String.format("【%s %s】",k, "已经被断开了连接。"));sendMessage(msgError);}    }}}

二、客户端代码

<!DOCTYPE html><html><head><meta name="author" content="Yeeku.H.Lee(CrazyIt.org)" /><meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /><title>WebSocket聊天室 </title><style>.height{height:6px;}</style><script type="text/javascript">//创建客户端使用的用户名var userName;var websocket=null;//穿件websocket函数function ws(){userName=document.getElementById('user').value;webSocket = new WebSocket("ws://"+location.host+"/MySocket/chat?user="+userName);// 为onmessage事件绑定监听器,接收消息webSocket.onmessage= function(event){//分析json,如果没有to则是用户列表,否则为普通消息var json = JSON.parse(event.data);if((typeof json.to) == 'undefined'){var html='<option>All</option>';for(var user in json){html+='<option>'+json[user]+'</option>';}document.getElementById('select').innerHTML = html;}else{var show = document.getElementById('show');//判断json,如果发送者为自己,则显示在右侧if(json.from==userName){show.innerHTML+= '<p class="height" align="right">'+json.msg +":"+json.from +'</p>';}else{show.innerHTML+='<p class="height" >'+json.from+":"+json.msg +'</p>';}show.scrollTop = show.scrollHeight;}}webSocket.onclose = function (){Console.log('WebSocket已经被关闭。');};}//发送消息function sendMsg(){var inputMsg = document.getElementById('msg');var selectTo=document.getElementById('select');//普通消息格式var msg={from:userName,to:selectTo.value,msg:inputMsg.value}// 发送消息webSocket.send(JSON.stringify(msg));// 清空单行文本框inputMsg.value = "";}</script></head><body><div style="width:600px;height:50px;"><input type="text" size="80" id="user" name="user" placeholder="输入姓名"/><input type="button" value="链接" id="coon" onclick="ws()"/></div><div style="width:600px;height:240px;overflow-y:auto;border:1px solid #333;" id="show"></div><select id="select"></select><input type="text" size="80" id="msg" placeholder="输入聊天内容"/><input type="button" value="发送" id="sendBn" onclick="sendMsg()"/></body></html>

三、最终效果


需要项目文件的可以在我的主页下载。。。

阅读全文
0 0
原创粉丝点击