JavaWeb网页聊天室(WebSocket即时通讯)

概要:

WebSocket是HTML5一种新的协议,它实现了浏览器与服务器全双工通信,这里就将使用WebSocket来开发网页聊天室,前端框架会使用AmazeUI,后台使用Java,编辑器使用UMEditor。

| |目录

Java和WebSocket开发网页即时聊天室

1一、项目说明及简介

1. 项目简介

WebSocket是HTML5一种新的协议,它实现了浏览器与服务器全双工通信,这里就将使用WebSocket来开发网页聊天室,前端框架会使用AmazeUI,后台使用Java,编辑器使用UMEditor。

2. 涉及知识点

网页前端(HTML5 + CSS3 + JS)和 JavaEE。

3. 软件环境

Tomcat8

JavaEE7

JDK7

Eclipse-JavaEE 或 MyEclipse

支持HTML5的浏览器

4. 相关框架

jQuery—1.X

妹子UI(AmazeUI-2.5.2)

百度富文本编辑器(UMeditor1_2_2)

5. 效果预览


6.Git地址

http://git.oschina.net/loopcc/WebSocketChat


2二、新建空项目

打开Eclipse JavaEE,新建一个名为Chat的Dynamic Web Project,然后导入处理JSON格式字符串所需要的包,

Text |  复制
1
2
3
4
5
6
commons-beanutils-1.8.0.jar
commons-collections-3.2.1.jar
commons-lang-2.5.jar
commons-logging-1.1.1.jar
ezmorph-1.0.6.jar
json-lib-2.4-jdk15.jar

把这几个包放在/WEB-INF/lib目录下,最后把项目发布到Tomcat8服务器上,到此空项目就搭建完成了。

Eclipse的默认项目根目录叫WebContent, MyEclipse默认项目根目录叫WebRoot,

在本篇文档中,我们接下来用WebRoot作为项目根目录。

3三、编写前端页面

这里使用了AmazeUI框架,它是一个跨屏自适应的前端框架

消息输入框使用了UMEditor,它是一个富文本在线编辑器,能让我们的消息内容多姿多彩。

在WebContent目录下新建一个名为index.jsp的页面,

首先从AmazeUI官网下载压缩包,然后解压把assets文件夹拷贝到WebRoot目录下,这样我们就能使用AmazeUI了。

再从UEditer官网下载Mini版的JSP版本压缩包,解压后把整个目录拷贝到WebRoot目录下,

接下来就可以编写前端代码了,代码如下(可按照自己的审美去编写):

Html |  复制
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
<%@ 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>
     <meta charset="UTF-8">
     <title>Landing Page | Amaze UI Example</title>
     <meta http-equiv="X-UA-Compatible" content="IE=edge">
     <meta name="viewport" content="width=device-width, initial-scale=1">
     <meta name="format-detection" content="telephone=no">
     <meta name="renderer" content="webkit">
     <meta http-equiv="Cache-Control" content="no-siteapp"/>
     <script src="assets/js/jquery.min.js"></script>
     <!-- 妹子UI相关资源 -->
     <link rel="alternate icon" type="image/png" href="assets/i/favicon.png">
     <link rel="stylesheet" href="assets/css/amazeui.min.css"/>
     <script src="assets/js/amazeui.min.js"></script>
     <!-- UM相关资源 -->
     <link href="assets/umeditor/themes/default/css/umeditor.css" type="text/css" rel="stylesheet">
     <script type="text/javascript" charset="utf-8" src="assets/umeditor/umeditor.config.js"></script>
     <script type="text/javascript" charset="utf-8" src="assets/umeditor/umeditor.min.js"></script>
     <script type="text/javascript" src="assets/umeditor/lang/zh-cn/zh-cn.js"></script>
</head>
<body>
<header class="am-topbar am-topbar-fixed-top">
   <div class="am-container">
     <h1 class="am-topbar-brand">
       <a href="#">聊天室</a>
     </h1>
     <div class="am-collapse am-topbar-collapse" id="collapse-head">
       <ul class="am-nav am-nav-pills am-topbar-nav">
         <li class="am-active"><a href="#">首页</a></li>
         <li><a href="#">项目</a></li>
       </ul>
       <div class="am-topbar-right">
         <button class="am-btn am-btn-secondary am-topbar-btn am-btn-sm"><span class="am-icon-pencil"></span> 注册</button>
       </div>
       <div class="am-topbar-right">
         <button class="am-btn am-btn-primary am-topbar-btn am-btn-sm"><span class="am-icon-user"></span> 登录</button>
       </div>
     </div>
   </div>
</header>
<div id="main">
     <!-- 聊天内容展示区域 -->
     <div id="ChatBox" class="am-g am-g-fixed" >
       <div class="am-u-lg-12" style="height:400px;border:1px solid #999;overflow-y:scroll;">
         <ul id="chatContent" class="am-comments-list am-comments-list-flip">
             <li class="am-comment am-comment-success" style="margin-right:20%;">
                 <a href="">
                     <img class="am-comment-avatar" src="assets/images/other.jpg" alt=""/>
                 </a>
                 <div class="am-comment-main" >
                     <header class="am-comment-hd">
                         <div class="am-comment-meta">
                           <a href="#link-to-user" class="am-comment-author">某人</a>
                           <time datetime="" title="">2014-7-12 15:30</time>
                         </div>
                     </header>
                  <div class="am-comment-bd">此处是消息内容</div>
                 </div>
             </li>
             <li class="am-comment am-comment-flip am-comment-warning" style="margin-left:20%;">
                 <a href="">
                     <img class="am-comment-avatar" src="assets/images/xia.jpg" alt=""/>
                 </a>
                 <div class="am-comment-main" >
                     <header class="am-comment-hd">
                         <div class="am-comment-meta">
                           <a href="#link-to-user" class="am-comment-author">某人</a>
                           <time datetime="" title="">2014-7-12 15:30</time>
                         </div>
                     </header>
                  <div class="am-comment-bd">此处是消息内容</div>
                 </div>
             </li>
         </ul>
       </div>
     </div>
     <!-- 聊天内容发送区域 -->
     <div id="EditBox" class="am-g am-g-fixed">
     <!--style给定宽度可以影响编辑器的最终宽度-->
     <script type="text/plain" id="myEditor" style="width:100%;height:140px;"></script>
     <button id="send" type="button" class="am-btn am-btn-primary am-btn-block">发送</button>
     </div></div><script type="text/javascript">$(function(){
     //窗口大小变化时,调整显示效果
     $("#ChatBox div:eq(0)").height($(this).height()-290);
     $(window).resize(function(){
         $("#ChatBox div:eq(0)").height($(this).height()-290);
     });
     //实例化编辑器
     var um = UM.getEditor('myEditor',{
         initialContent:"请输入聊天信息...",
         autoHeightEnabled:false,
         toolbar:[
             'source | undo redo | bold italic underline strikethrough | superscript subscript | forecolor backcolor | removeformat |',
             'insertorderedlist insertunorderedlist | selectall cleardoc paragraph | fontfamily fontsize' ,
             '| justifyleft justifycenter justifyright justifyjustify |',
             'link unlink | emotion image video  | map'
         ]
     });});
     </script>
</body>
</html>

编写完成之后启动Tomcat服务器,然后访问index.jsp ,会看到设计好的页面效果。

上面的实例代码里有两条示例消息,一条别人的消息,一条自己的消息。

4四、消息流转

聊天室的核心就是把一个人的消息广播给所有人,我们这里使用WebSocket技术让所有人的浏览器都与服务器连在一起,当一个人发送消息到服务器后,服务器把消息转发给所有人,包括自己,

这样每个人的浏览器就都能看到这条消息。

出现一条新消息时必备的3个属性:【发言人昵称】,【消息内容】和【发送时间】。

为了区别消息是否为自己,我们还需要在消息中加入一项属性:【是否自己】。

其中【发送时间】和【是否自己】可以由服务器进行处理,那么发言人只需要告诉服务器【昵称】与【消息内容】即可:

JavaScript |  复制
1
2
3
4
5
6
7
8
9
10
11
12
//浏览器发给服务器的数据:
{
     nickname: "风清扬" ,
     content: "HelloWorld"
}
//浏览器接收的数据:
{
     nickname: "风清扬" ,
     content: "HelloWorld" ,
     date: "2016.04.01 14:05:22" ,
     isSelf: true
}

5五、JS添加新消息

页面样式已经设计好,而真正聊天的时候肯定需要不断展示新的消息,为了方便控制,我们使用JavaScript统一消息的创建过程。

页面中代表消息的li元素只保留一份,把它作为消息的模板,默认隐藏,每当新消息到来,我们就复制一份模板,修改其中的属性,追加到列表中去展示,给几个元素加上独有的标记: 

【昵称】:ff="nickname" 【内容】:ff="content" 【时间】:ff="msgdate"

JavaScript |  复制
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<li id= "msgtmp"  class= "am-comment"  style= "display:none;" >
     <a href= "" >
         <img class= "am-comment-avatar"  src= "assets/images/other.jpg"  alt= "" />
     </a>
     <div class= "am-comment-main"  >
         <header class= "am-comment-hd" >
             <div class= "am-comment-meta" >
               <a ff= "nickname"  href= "#link-to-user"  class= "am-comment-author" >某人</a>
               <time ff= "msgdate"  datetime= ""  title= "" >2014-7-12 15:30</time>
             </div>
         </header>
      <div ff= "content"  class= "am-comment-bd" >此处是消息内容</div>
     </div>
</li>

使用JS克隆一份模板,设置克隆体里面的数据,最终追加到列表中:

JavaScript |  复制
1
2
3
4
5
6
7
8
9
10
11
12
13
//向聊天窗口加入一条消息
function  addMessage(msg){
     var  box = $( "#msgtmp" ).clone();      //复制一份模板,取名为box
     box.show();                          //设置box状态为显示
     box.appendTo( "#chatContent" );        //把box追加到聊天面板中
     box.find( '[ff="nickname"]' ).html(msg.nickname);  //在box中设置昵称
     box.find( '[ff="msgdate"]' ).html(msg.date);       //在box中设置时间
     box.find( '[ff="content"]' ).html(msg.content);    //在box中设置内容
     box.addClass(msg.isSelf?  'am-comment-flip' : '' );  //右侧显示
     box.addClass(msg.isSelf?  'am-comment-warning' : 'am-comment-success' ); //颜色
     box.css((msg.isSelf?  'margin-left' : 'margin-right' ), "20%" ); //外边距
     $( "#ChatBox div:eq(0)" ).scrollTop(999999);   //滚动条移动至最底部
}

6六、编写后台代码

新建一个com.zhenzhigu.chat的包,在包中创建一个名为ChatServer的类,从JavaEE 7开始就统一了WebSocket的API,因此无论是什么服务器,用Java写的代码都是一样的,代码如下:

Java |  复制
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
package  com.zhenzhigu.chat;
import  java.text.SimpleDateFormat;
import  java.util.Date;
import  java.util.Vector;
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  net.sf.json.JSONObject;
@ServerEndpoint ( "/websocket" )
public  class  ChatServer {
     private  static  SimpleDateFormat df =  new  SimpleDateFormat( "yyyy-MM-dd HH:mm:ss" );
     private  static  Vector<Session> room =  new  Vector<Session>();
     /**
      * 用户接入
      * @param session 可选
      */
     @OnOpen
     public  void  onOpen(Session session){
         room.addElement(session);
     }
     /**
      * 接收到来自用户的消息
      * @param message
      * @param session
      */
     @OnMessage
     public  void  onMessage(String message,Session session){
         //把用户发来的消息解析为JSON对象
         JSONObject obj = JSONObject.fromObject(message);
         //向JSON对象中添加发送时间
         obj.put( "date" , df.format( new  Date()));
         //遍历聊天室中的所有会话
         for (Session se : room){
             //设置消息是否为自己的
             obj.put( "isSelf" , se.equals(session));
             //发送消息给远程用户
             se.getAsyncRemote().sendText(obj.toString());
         }
     }
     /**
      * 用户断开
      * @param session
      */
     @OnClose
     public  void  onClose(Session session){
         room.remove(session);
     }
     /**
      * 用户连接异常
      * @param t
      */
     @OnError
     public  void  onError(Throwable t){
     }
}


7七、前端后端交互

后台写完了,前台要用WebSocket连接后台,需要新建一个WebSocket对象,然后就可以和服务器端进行交互,从浏览器发送消息给服务器端,同时要验证输入框的内容是否为空,然后接受服务端发送的消息,把它们动态地添加到聊天内容框中。

记得写到文档就绪函数中

JavaScript |  复制
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
//自己的昵称
var  nickname =  "风清扬" +Math.random();
//建立一条与服务器之间的连接
var  socket =  new  WebSocket( "ws://${pageContext.request.getServerName()}:${pageContext.request.getServerPort()}${pageContext.request.contextPath}/websocket" );
//接收服务器的消息
socket.onmessage= function (ev){
     var  obj = eval(    '(' +ev.data+ ')'    );
     addMessage(obj)
}
//发送按钮被点击时
$( "#send" ).click( function (){
     if  (!um.hasContents()) {   // 判断消息输入框是否为空
         // 消息输入框获取焦点
         um.focus();
         // 添加抖动效果
         $( '.edui-container' ).addClass( 'am-animation-shake' );
         setTimeout( "$('.edui-container').removeClass('am-animation-shake')" , 1000);
     else  {
         //获取输入框的内容
         var  txt = um.getContent();
         //构建一个标准格式的JSON对象
         var  obj = JSON.stringify({
             nickname:nickname,
             content:txt
         });
         // 发送消息
         socket.send(obj);
         // 清空消息输入框
         um.setContent( '' );
         // 消息输入框获取焦点
         um.focus();
     }
});

到这步,简单的网页聊天室就完成了,你可以多开几个窗口或在局域网中邀请小伙伴们来一起测试。

8八、思考

本次项目只是简单的实现群体聊天功能,还有很多功能可以增加,例如头像上传、一对一聊天等,请考虑如何完善我们的聊天室。

部分代码如下:client: /* * To change this template, choose Tools | Templates * and open the template in the editor. */ package client; /** * * @author Administrator */ import java.awt.*; import java.io.*; import java.net.*; import java.applet.*; import java.util.Hashtable; public class ClientChat extends Applet implements Runnable { Socket socket=null; DataInputStream in=null; DataOutputStream out=null; InputNameTextField 用户提交昵称界面=null; ChatArea 用户聊天界面=null; Hashtable listTable; Label 提示条; Panel north, center; Thread thread; public void init() { int width=getSize().width; int height=getSize().height; listTable=new Hashtable(); setLayout(new BorderLayout()); 用户提交昵称界面=new InputNameTextField(listTable); int h=用户提交昵称界面.getSize().height; 用户聊天界面=new ChatArea("",listTable,width,height-(h+5)); 用户聊天界面.setVisible(false); 提示条=new Label("正在连接到服务器,请稍等...",Label.CENTER); 提示条.setForeground(Color.red); north=new Panel(new FlowLayout(FlowLayout.LEFT)); center=new Panel(); north.add(用户提交昵称界面); north.add(提示条); center.add(用户聊天界面); add(north,BorderLayout.NORTH); add(center,BorderLayout.CENTER); validate(); } public void start() { if(socket!=null&&in!=null&&out!=null) { try { socket.close(); in.close(); out.close(); 用户聊天界面.setVisible(false); } catch(Exception ee) { } } try { socket = new Socket(this.getCodeBase().getHost(), 6666); in=new DataInputStream(socket.getInputStream()); out=new DataOutputStream(socket.getOutputStream()); } catch (IOException ee) { 提示条.setText("连接失败"); } if(socket!=null) { InetAddress address=socket.getInetAddress(); 提示条.setText("连接:"+address+"成功"); 用户提交昵称界面.setSocketConnection(socket,in,out); north.validate(); } if(thread==null) { thread=new Thread(this); thread.start(); } } public void stop() { try { socket.close(); thread=null; } catch(IOException e) { this.showStatus(e.toString()); } } public void run() { while(thread!=null) { if(用户提交昵称界面.get能否聊天()==true) { 用户聊天界面.setVisible(true); 用户聊天界面.setName(用户提交昵称界面.getName()); 用户聊天界面.setSocketConnection(socket,in,out); 提示条.setText("祝聊天愉快!"); center.validate(); break; } try { Thread.sleep(100); } catch(Exception e) { } } } }
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值