博客地址: https://ainyi.com/#/67
有一个月没有写博客了, 也是因为年前需求多, 回家过春节的原因, 现在返回北京的第二天, 想想, 应该也要分享技术专题的博客了!!
主题
基于 websocket 网页端聊天室
WebSocket 协议是基于 TCP 的一种新的网络协议. 它实现了浏览器与服务器全双工 (full-duplex) 通信 -- 允许服务器主动发送信息给客户端.
使用 java 开发后台
需要导入一个 jar 包: javax.websocket-API-1.0-rc4.jar
后台代码
- package com.krry.socket;
- import java.io.IOException;
- import java.util.concurrent.CopyOnWriteArraySet;
- 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;
- // 该注解用来指定一个 URI, 客户端可以通过这个 URI 来连接到 WebSocket. 类似 Servlet 的注解 mapping. 无需在 Web.xml 中配置.
- @ServerEndpoint("/websocket")
- public class MyWebSocket {
- // 静态变量, 用来记录当前在线连接数. 应该把它设计成线程安全的.
- private static int onlineCount = 0;
- //concurrent 包的线程安全 Set, 用来存放每个客户端对应的 MyWebSocket 对象. 若要实现服务端与单一客户端通信的话, 可以使用 Map 来存放, 其中 Key 可以为用户标识
- private static CopyOnWriteArraySet<MyWebSocket> webSocketSet = new CopyOnWriteArraySet<MyWebSocket>();
- // 与某个客户端的连接会话, 需要通过它来给客户端发送数据
- private Session session;
- /**
- * 连接建立成功调用的方法
- * @param session 可选的参数. session 为与某个客户端的连接会话, 需要通过它来给客户端发送数据
- */
- @OnOpen
- public void onOpen(Session session){
- 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) {
- System.out.println("来自客户端的消息:" + message);
- // 群发消息
- for(MyWebSocket item: webSocketSet){
- try {
- item.sendMessage(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 static synchronized int getOnlineCount() {
- return onlineCount;
- }
- public static synchronized void addOnlineCount() {
- MyWebSocket.onlineCount++;
- }
- public static synchronized void subOnlineCount() {
- MyWebSocket.onlineCount--;
- }
- }
前端代码
注意
前端需要实现这几个方法:
- // 注册事件
- // 监听打开连接
- ws.onopen = function(){
- openWs();
- };
- // 监听消息
- ws.onmessage = function(event){
- msgWs(event);
- };
- // 监听关闭连接
- ws.onclose = function(){
- closeWs();
- };
- // 监听发送错误
- ws.onerror = function(){
- errorWs();
- };
具体代码
<%@ page language="java" import="java.util.*" pageEncoding="utf-8"%>
- <!doctype html>
- <HTML>
- <head>
- <meta http-equiv="Content-Type" content="text/html;charset=utf-8">
- <meta name="keywords" content="">
- <meta name="description" content="">
- <title>
基于 Java 服务器端的消息主动推送技术揭秘 --krry
- </title>
- <link rel="stylesheet" href="CSS/animate.css" />
- <link rel="stylesheet" type="text/css" href="css/sg.css" />
- <style>
- *{margin:0;padding:0;} body{background:url("images/5.jpg");background-size:cover;}
- h1{margin-top:50px;text-align:center;color:#fff;text-shadow:1px 1px 1px
- #000;font-family:-webkit-body;font-size:24px;} .box{width:700px;margin:20px
- auto;} .box span{color:#f60;font-size:16px;font-family:"微软雅黑";} .box .shu{text-indent:1em;height:24px;font-family:"微软雅黑";border:0;outline:none;font-size:14px;}
- .box .add{width:300px;margin-right:24px;} .box .user{width:200px;} .box
- .btn{width:80px;height:34px;color:#fff;background:#6c0;border:0;outline:none;cursor:pointer;margin-top:20px;font-size:16px;font-family:"微软雅黑";}
- .box .area{line-height: 29px;height:280px;width:680px;padding:10px;overflow:auto;font-size:16px;font-family:"微软雅黑";margin:20px
- 0;outline:none;box-shadow:1px 2px 18px #000} .box .setex{text-indent:1em;height:28px;border:1px
- solid #6c0;width:618px;outline:none;float:left;font-family:"微软雅黑";} .box
- .send{font-size:14px;width:80px;height:30px;color:#fff;background:#6c0;border:0;outline:none;cursor:pointer;font-family:"微软雅黑";}
- </style>
- </head>
- <body>
- <h1>
基于 Java 服务器端的消息主动推送技术揭秘 --krry
- </h1>
- <div class="box">
- <span>
服务器地址:
- </span>
- <input type="text" class="shu add" value="www.ainyi.com/krry_NetChat/websocket"
- readonly/>
- <span>
用户名:
- </span>
- <input type="text" class="shu user" value="匿名" />
- <input type="button" value="连接" class="btn" />
- <div class="area" id="boxx">
- </div>
- <div class="c_cen">
- <input type="text" class="setex" />
- <input type="button" value="发送" class="send">
- </div>
- </div>
- <script src="js/jquery-1.11.1.min.js">
- </script>
- <script src="js/sg.js">
- </script>
- <script src="js/sgutil.js">
- </script>
- <script>
- var close = true;
- var ws;
- $(function() {
- $(".c_cen").hide();
- // 首先判断浏览器是否支持 webSocket, 支持 h5 的浏览器才会支持
- if (Windows.WebSocket) {
- printMsg("您的浏览器支持 WebSocket, 您可以尝试连接到聊天服务器!", "OK");
- } else {
- printMsg("您的浏览器不支持 WebSocket, 请选择其他浏览器!", "ERROR");
- // 设置按钮不可点击
- $(".btn").attr("disabled", "true");
- }
- });
- // 打印信息
- function printMsg(msg, msgType) {
- if (msgType == "OK") {
- msg = "<span style='color:green'>" + msg + "</span>";
- }
- if (msgType == "ERROR") {
- msg = "<span style='color:red'>" + msg + "</span>";
- }
- $(".area").append(msg + "<br/>");
- var boxx = document.getElementById("boxx");
- boxx.scrollTop = boxx.scrollHeight; // 使滚动条一直在底部
- }
- // 打开 Socket
- function openWs() {
- printMsg("链接已建立", "OK");
- ws.send("[" + $(".user").val() + "] 已进入聊天室");
- $(".c_cen").show();
- }
- // 接收消息的时候
- function msgWs(e) {
- printMsg(e.data);
- }
- // 关闭连接
- function closeWs() {
- $(".btn").val("连接");
- $(".c_cen").hide();
- }
- // 产生错误
- function errorWs() {
- printMsg("您与服务器连接错误...", "ERROR");
- }
- // 点击发送按钮
- $(".send").click(function() {
- var text = $(".setex").val();
- if (text == null || text == "") return;
- $(".setex").val("");
- ws.send("[" + $(".user").val() + "] :" + text);
- });
- // 点击连接
- $(".btn").click(function() {
- if ($(".add").val() && $(".user").val()) {
- if (close) {
- printMsg("正在准备连接服务器, 请稍等...");
- var url = "wss://" + $(".add").val();
- if ("WebSocket" in Windows) {
- ws = new WebSocket(url);
- } else if ("MozWebSocket" in Windows) {
- ws = new MozWebSocket(url);
- }
- // 已连接
- $(".btn").val("断开");
- close = false;
- // 注册事件
- ws.onopen = function() {
- openWs();
- };
- ws.onmessage = function(event) {
- msgWs(event);
- };
- ws.onclose = function() {
- closeWs();
- };
- ws.onerror = function() {
- errorWs();
- };
- // 监听窗口关闭事件, 当窗口关闭时, 主动去关闭 websocket 连接, 防止连接还没断开就关闭窗口, server 端会抛异常.
- Windows.onbeforeunload = function() {
- ws.send("[" + $(".user").val() + "] 离开了聊天室");
- close = true;
- ws.close();
- };
- } else {
- ws.send("[" + $(".user").val() + "] 离开了聊天室");
- close = true;
- ws.close();
- }
- } else {
- $.tmDialog.alert({
- open: "left",
- content: "服务器地址和用户名不能为空哦...",
- title: "提示哦~~~"
- });
- }
- });
- // 回车键
- $(".setex").keypress(function(event) {
- if (event.keyCode == 13) {
- $(".send").trigger("click");
- }
- });
- </script>
- </body>
- </HTML>
到这里大功告成
聊天方法
打开两个窗口输入项目地址进行聊天
可以把链接发给朋友打开, 进行聊天
来一波截图
移动端
在线演示
PC 端: https://www.ainyi.com/krry_NetChat
移动端: https://www.ainyi.com/krry_NetChatPho
打完收工~
博客地址: https://ainyi.com/#/67
来源: https://www.cnblogs.com/ainyi/p/10398665.html