一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

服務器之家:專注于服務器技術及軟件下載分享
分類導航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術|正則表達式|C/C++|IOS|C#|Swift|Android|VB|R語言|JavaScript|易語言|vb.net|

服務器之家 - 編程語言 - Java教程 - 基于Tomcat7、Java、WebSocket的服務器推送聊天室實例

基于Tomcat7、Java、WebSocket的服務器推送聊天室實例

2020-07-17 11:44我的執著 Java教程

HTML5 WebSocket實現了服務器與瀏覽器的雙向通訊,本篇文章主要介紹了基于Tomcat7、Java、WebSocket的服務器推送聊天室實例,具有一定的參考價值,有興趣的可以了解一下。

前言

HTML5 WebSocket實現了服務器與瀏覽器的雙向通訊,雙向通訊使服務器消息推送開發更加簡單,最常見的就是即時通訊和對信息實時性要求比較高的應用。以前的服務器消息推送大部分采用的都是“輪詢”和“長連接”技術,這兩中技術都會對服務器產生相當大的開銷,而且實時性不是特別高。WebSocket技術對只會產生很小的開銷,并且實時性特別高。下面就開始講解如何利用WebSocket技術開發聊天室。在這個實例中,采用的是Tomcat7服務器,每個服務器對于WebSocket的實現都是不一樣的,所以這個實例只能在Tomcat服務器中運行,不過目前Spring已經推出了WebSocket的API,能夠兼容各個服務器的實現,大家可以查閱相關的資料進行了解,在這里就不介紹了,下圖是聊天室的效果圖:

基于Tomcat7、Java、WebSocket的服務器推送聊天室實例

在這里實例中,實現了消息的實時推送,還實現了聊天用戶的上下線通知。下面就開始具體講解如何實現。

后臺處理

Tomcat實現WebSocket的主要是依靠org.apache.catalina.websocket.MessageInbound這個類,這個類的在{TOMCAT_HOME}/lib/catalina.jar中,所以你開發的時候需要將catalina.jar和tomcat-coyote.jar引入進來,下面這段代碼就是暴露給客戶端連接地址的Servlet:

?
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
package com.ibcio;
 
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServletRequest;
 
import org.apache.catalina.websocket.StreamInbound;
 
@WebServlet(urlPatterns = { "/message"})
//如果要接收瀏覽器的ws://協議的請求就必須實現WebSocketServlet這個類
public class WebSocketMessageServlet extends org.apache.catalina.websocket.WebSocketServlet {
 
  private static final long serialVersionUID = 1L;
   
  public static int ONLINE_USER_COUNT = 1;
   
  public String getUser(HttpServletRequest request){
    return (String) request.getSession().getAttribute("user");
  }
 
  //跟平常Servlet不同的是,需要實現createWebSocketInbound,在這里初始化自定義的WebSocket連接對象
  @Override
  protected StreamInbound createWebSocketInbound(String subProtocol,HttpServletRequest request) {
    return new WebSocketMessageInbound(this.getUser(request));
  }
}

這個Servlet跟普通的Servlet有些不同,繼承的WebSocketServlet類,并且要重寫createWebSocketInbound方法。這個類中Session中的user屬性是用戶進入index.jsp的時候設置的,記錄當前用戶的昵稱。下面就是自己實現的WebSocket連接對象類WebSocketMessageInbound類的代碼:

?
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
package com.ibcio;
 
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
 
import net.sf.json.JSONObject;
 
import org.apache.catalina.websocket.MessageInbound;
import org.apache.catalina.websocket.WsOutbound;
 
public class WebSocketMessageInbound extends MessageInbound {
 
  //當前連接的用戶名稱
  private final String user;
 
  public WebSocketMessageInbound(String user) {
    this.user = user;
  }
 
  public String getUser() {
    return this.user;
  }
 
  //建立連接的觸發的事件
  @Override
  protected void onOpen(WsOutbound outbound) {
    // 觸發連接事件,在連接池中添加連接
    JSONObject result = new JSONObject();
    result.element("type", "user_join");
    result.element("user", this.user);
    //向所有在線用戶推送當前用戶上線的消息
    WebSocketMessageInboundPool.sendMessage(result.toString());
     
    result = new JSONObject();
    result.element("type", "get_online_user");
    result.element("list", WebSocketMessageInboundPool.getOnlineUser());
    //向連接池添加當前的連接對象
    WebSocketMessageInboundPool.addMessageInbound(this);
    //向當前連接發送當前在線用戶的列表
    WebSocketMessageInboundPool.sendMessageToUser(this.user, result.toString());
  }
 
  @Override
  protected void onClose(int status) {
    // 觸發關閉事件,在連接池中移除連接
    WebSocketMessageInboundPool.removeMessageInbound(this);
    JSONObject result = new JSONObject();
    result.element("type", "user_leave");
    result.element("user", this.user);
    //向在線用戶發送當前用戶退出的消息
    WebSocketMessageInboundPool.sendMessage(result.toString());
  }
 
  @Override
  protected void onBinaryMessage(ByteBuffer message) throws IOException {
    throw new UnsupportedOperationException("Binary message not supported.");
  }
 
  //客戶端發送消息到服務器時觸發事件
  @Override
  protected void onTextMessage(CharBuffer message) throws IOException {
    //向所有在線用戶發送消息
    WebSocketMessageInboundPool.sendMessage(message.toString());
  }
}

代碼中的主要實現了onOpen、onClose、onTextMessage方法,分別處理用戶上線、下線、發送消息。在這個類中有個WebSocketMessageInboundPool連接池類,這個類是用來管理目前在線的用戶的連接,下面是這個類的代碼:

?
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
package com.ibcio;
 
import java.io.IOException;
import java.nio.CharBuffer;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
 
public class WebSocketMessageInboundPool {
 
  //保存連接的MAP容器
  private static final Map<String,WebSocketMessageInbound > connections = new HashMap<String,WebSocketMessageInbound>();
   
  //向連接池中添加連接
  public static void addMessageInbound(WebSocketMessageInbound inbound){
    //添加連接
    System.out.println("user : " + inbound.getUser() + " join..");
    connections.put(inbound.getUser(), inbound);
  }
   
  //獲取所有的在線用戶
  public static Set<String> getOnlineUser(){
    return connections.keySet();
  }
   
  public static void removeMessageInbound(WebSocketMessageInbound inbound){
    //移除連接
    System.out.println("user : " + inbound.getUser() + " exit..");
    connections.remove(inbound.getUser());
  }
   
  public static void sendMessageToUser(String user,String message){
    try {
      //向特定的用戶發送數據
      System.out.println("send message to user : " + user + " ,message content : " + message);
      WebSocketMessageInbound inbound = connections.get(user);
      if(inbound != null){
        inbound.getWsOutbound().writeTextMessage(CharBuffer.wrap(message));
      }
    } catch (IOException e) {
      e.printStackTrace();
    }
  }
   
  //向所有的用戶發送消息
  public static void sendMessage(String message){
    try {
      Set<String> keySet = connections.keySet();
      for (String key : keySet) {
        WebSocketMessageInbound inbound = connections.get(key);
        if(inbound != null){
          System.out.println("send message to user : " + key + " ,message content : " + message);
          inbound.getWsOutbound().writeTextMessage(CharBuffer.wrap(message));
        }
      }
    } catch (IOException e) {
      e.printStackTrace();
    }
  }
}

前臺展示

上面的代碼就是聊天室后臺的代碼,主要是由3個對象組成,Servlet、連接對象、連接池,下面就是前臺的代碼,前臺的代碼主要是實現與服務器進行連接,展示用戶列表及信息列表,前臺的展示使用了Ext框架,不熟悉Ext的同學可以初步的了解下Ext,下面的是index.jsp的代碼:

?
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
<%@ page language="java" pageEncoding="UTF-8" import="com.ibcio.WebSocketMessageServlet"%>
<%
  String user = (String)session.getAttribute("user");
  if(user == null){
    //為用戶生成昵稱
    user = "游客" + WebSocketMessageServlet.ONLINE_USER_COUNT;
    WebSocketMessageServlet.ONLINE_USER_COUNT ++;
    session.setAttribute("user", user);
  }
  pageContext.setAttribute("user", user);
%>
<html>
<head>
  <title>WebSocket 聊天室</title>
  <!-- 引入CSS文件 -->
  <link rel="stylesheet" type="text/css" href="ext4/resources/css/ext-all.css">
  <link rel="stylesheet" type="text/css" href="ext4/shared/example.css" />
  <link rel="stylesheet" type="text/css" href="css/websocket.css" />
   
  <!-- 映入Ext的JS開發包,及自己實現的webscoket. -->
  <script type="text/javascript" src="ext4/ext-all-debug.js"></script>
  <script type="text/javascript" src="websocket.js"></script>
  <script type="text/javascript">
    var user = "${user}";
  </script>
</head>
 
<body>
  <h1>WebSocket聊天室</h1>
  <p>通過HTML5標準提供的API與Ext富客戶端框架相結合起來,實現聊天室,有以下特點:</p>
  <ul class="feature-list" style="padding-left: 10px;">
    <li>實時獲取數據,由服務器推送,實現即時通訊</li>
    <li>利用WebSocket完成數據通訊,區別于輪詢,長連接等技術,節省服務器資源</li>
    <li>結合Ext進行頁面展示</li>
    <li>用戶上線下線通知</li>
  </ul>
  <div id="websocket_button"></div>
</body>
</html>

頁面的展示主要是在websocket.js中進行控制,下面是websocket.jsd的代碼:

?
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
//用于展示用戶的聊天信息
Ext.define('MessageContainer', {
 
  extend : 'Ext.view.View',
 
  trackOver : true,
 
  multiSelect : false,
 
  itemCls : 'l-im-message',
 
  itemSelector : 'div.l-im-message',
 
  overItemCls : 'l-im-message-over',
 
  selectedItemCls : 'l-im-message-selected',
 
  style : {
    overflow : 'auto',
    backgroundColor : '#fff'
  },
 
  tpl : [
      '<div class="l-im-message-warn">?交談中請勿輕信匯款、中獎信息、陌生電話。 請遵守相關法律法規。</div>',
      '<tpl for=".">',
      '<div class="l-im-message">',
      '<div class="l-im-message-header l-im-message-header-{source}">{from} {timestamp}</div>',
      '<div class="l-im-message-body">{content}</div>', '</div>',
      '</tpl>'],
 
  messages : [],
 
  initComponent : function() {
    var me = this;
    me.messageModel = Ext.define('Leetop.im.MessageModel', {
          extend : 'Ext.data.Model',
          fields : ['from', 'timestamp', 'content', 'source']
        });
    me.store = Ext.create('Ext.data.Store', {
          model : 'Leetop.im.MessageModel',
          data : me.messages
        });
    me.callParent();
  },
 
  //將服務器推送的信息展示到頁面中
  receive : function(message) {
    var me = this;
    message['timestamp'] = Ext.Date.format(new Date(message['timestamp']),
        'H:i:s');
    if(message.from == user){
      message.source = 'self';
    }else{
      message.source = 'remote';
    }
    me.store.add(message);
    if (me.el.dom) {
      me.el.dom.scrollTop = me.el.dom.scrollHeight;
    }
  }
});

這段代碼主要是實現了展示消息的容器,下面就是頁面加載完成后開始執行的代碼:

?
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
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
Ext.onReady(function() {
    //創建用戶輸入框
    var input = Ext.create('Ext.form.field.HtmlEditor', {
          region : 'south',
          height : 120,
          enableFont : false,
          enableSourceEdit : false,
          enableAlignments : false,
          listeners : {
            initialize : function() {
              Ext.EventManager.on(me.input.getDoc(), {
                    keyup : function(e) {
                      if (e.ctrlKey === true
                          && e.keyCode == 13) {
                        e.preventDefault();
                        e.stopPropagation();
                        send();
                      }
                    }
                  });
            }
          }
        });
    //創建消息展示容器
    var output = Ext.create('MessageContainer', {
          region : 'center'
        });
 
    var dialog = Ext.create('Ext.panel.Panel', {
          region : 'center',
          layout : 'border',
          items : [input, output],
          buttons : [{
                text : '發送',
                handler : send
              }]
        });
    var websocket;
 
    //初始話WebSocket
    function initWebSocket() {
      if (window.WebSocket) {
        websocket = new WebSocket(encodeURI('ws://localhost:8080/WebSocket/message'));
        websocket.onopen = function() {
          //連接成功
          win.setTitle(title + ' (已連接)');
        }
        websocket.onerror = function() {
          //連接失敗
          win.setTitle(title + ' (連接發生錯誤)');
        }
        websocket.onclose = function() {
          //連接斷開
          win.setTitle(title + ' (已經斷開連接)');
        }
        //消息接收
        websocket.onmessage = function(message) {
          var message = JSON.parse(message.data);
          //接收用戶發送的消息
          if (message.type == 'message') {
            output.receive(message);
          } else if (message.type == 'get_online_user') {
            //獲取在線用戶列表
            var root = onlineUser.getRootNode();
            Ext.each(message.list,function(user){
              var node = root.createNode({
                id : user,
                text : user,
                iconCls : 'user',
                leaf : true
              });
              root.appendChild(node);
            });
          } else if (message.type == 'user_join') {
            //用戶上線
              var root = onlineUser.getRootNode();
              var user = message.user;
              var node = root.createNode({
                id : user,
                text : user,
                iconCls : 'user',
                leaf : true
              });
              root.appendChild(node);
          } else if (message.type == 'user_leave') {
              //用戶下線
              var root = onlineUser.getRootNode();
              var user = message.user;
              var node = root.findChild('id',user);
              root.removeChild(node);
          }
        }
      }
    };
 
    //在線用戶樹
    var onlineUser = Ext.create('Ext.tree.Panel', {
          title : '在線用戶',
          rootVisible : false,
          region : 'east',
          width : 150,
          lines : false,
          useArrows : true,
          autoScroll : true,
          split : true,
          iconCls : 'user-online',
          store : Ext.create('Ext.data.TreeStore', {
                root : {
                  text : '在線用戶',
                  expanded : true,
                  children : []
                }
              })
        });
    var title = '歡迎您:' + user;
    //展示窗口
    var win = Ext.create('Ext.window.Window', {
          title : title + ' (未連接)',
          layout : 'border',
          iconCls : 'user-win',
          minWidth : 650,
          minHeight : 460,
          width : 650,
          animateTarget : 'websocket_button',
          height : 460,
          items : [dialog,onlineUser],
          border : false,
          listeners : {
            render : function() {
              initWebSocket();
            }
          }
        });
 
    win.show();
 
    //發送消息
    function send() {
      var message = {};
      if (websocket != null) {
        if (input.getValue()) {
          Ext.apply(message, {
                from : user,
                content : input.getValue(),
                timestamp : new Date().getTime(),
                type : 'message'
              });
          websocket.send(JSON.stringify(message));
          //output.receive(message);
          input.setValue('');
        }
      } else {
        Ext.Msg.alert('提示', '您已經掉線,無法發送消息!');
      }
    }
  });

上面的代碼就是頁面完成加載后自動連接服務器,并創建展示界面的代碼。

注意

需要注意的兩點,在部署完成之后需要將在tomcat應用目錄中的lib目錄下的catalina.jar和tomcat-coyote.jar刪掉,比如項目的lib目錄在D:\workspace\WebSocket\WebRoot\WEB-INF\lib,而部署的應用lib目錄是在D:\tools\apache-tomcat-7.0.32\webapps\WebSocket\WEB-INF\lib,刪掉部署目錄的lib目錄中連兩個jar就可以了,否則會包Could not initialize class com.ibcio.WebSocketMessageServlet錯誤,切記。

如果還是無法建立連接,請下載最新的tomcat,忘了是那個版本的tomcatcreateWebSocketInbound是沒有request參數的,現在的這個代碼是有這個參數了,7.0.3XX版本都是帶這個參數的,切記。

總結

使用WebSocket開發服務器推送非常方便,這個是個簡單的應用,其實還可以結合WebRTC實現視頻聊天和語音聊天。

實例下載

下載地址:demo

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持服務器之家。

原文鏈接:http://blog.csdn.net/leecho571/article/details/9707497

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 欧美在线视频一区二区 | 久热人人综合人人九九精品视频 | 97精品国产自在现线免费 | 欧美人xxxxxbbbb | 日韩在线视频免费不卡一区 | 国产三级跑 | 欧美精品国产一区二区三区 | 亚洲国产综合久久久无码色伦 | 欧美视频一 | 视频二区 素人 制服 国产 | 天美传媒影视在线免费观看 | avtt在线| 国产精品在线 | 超高清欧美同性videos | 久久久精品日本一区二区三区 | 欧美一级欧美三级在线 | 欧美成人aaaa免费高清 | 国产大片51精品免费观看 | 日本-区二区三区免费精品 日本破处 | 亚洲国产精品嫩草影院久久 | 亚洲 日韩 在线 国产 视频 | 5g影院天天影院天天爽影院网站 | 搡60一70岁的老女人小说 | 52av我爱avhaose01好| 日本漫画无翼乌 | 精品国产国产精2020久久日 | 91噜噜噜在线观看 | 免费亚洲视频在线观看 | 无人区乱码1区2区3区网站 | 色中色导航| 亚洲成人免费看 | 亚洲午夜精品久久久久久成年 | 欧美va在线 | 91亚洲精品第一综合不卡播放 | 成人18在线观看 | 天堂成人在线观看 | 男男双性生子产乳高辣h | 亚洲国产精品热久久 | 国产91精品在线播放 | 91麻豆精品国产自产在线 | 高清视频在线播放 |