利用websocket和java完成的消息推送功能,服务器用的是tomcat7.0,一些东西是自己琢磨的,也不知道恰不恰当,不恰当处,还请各位见谅,并指出。

程序简单来说,就是客户A可以发送消息给客户B,但有很多可以扩展的地方,

比如

1.如果加入数据库后,A发消息时客户B未上线,服务端将会把消息存在数据库中,等客户B上线后,在将消息取出发送给客户B

2.服务端也可发送消息到任意客户端上。

程序的运行效果截图如下(在chrome,搜狗,firefox下测试通过):代码将在最后给出

首先我们打开一个浏览器,显示输入您的名字,这里我输入soar

在打开第二个浏览器,这里我输入bill

这是如果我发送hello bill i am soar给bill,点击send

在另一个浏览器上就可以看到

Websocket

1.websocket是什么?

WebSocket是为解决客户端与服务端实时通信而产生的技术。其本质是先通过HTTP/HTTPS协议进行握手后创建一个用于交换数据的TCP连接,

此后服务端与客户端通过此TCP连接进行实时通信。

2.websocket的优点

以前我们实现推送技术,用的都是轮询,在特点的时间间隔有浏览器自动发出请求,将服务器的消息主动的拉回来,在这种情况下,我们需要不断的向服务器 发送请求,然而HTTP request 的header是非常长的,里面包含的数据可能只是一个很小的值,这样会占用很多的带宽和服务器资源。会占用大量的带宽和服务器资源。

WebSocket API最伟大之处在于服务器和客户端可以在给定的时间范围内的任意时刻,相互推送信息。在建立连接之后,服务器可以主动传送数据给客户端。

此外,服务器与客户端之间交换的标头信息很小。

WebSocket并不限于以Ajax(或XHR)方式通信,因为Ajax技术需要客户端发起请求,而WebSocket服务器和客户端可以彼此相互推送信息;

关于ajax,comet,websocket的详细介绍,和websocket报文的介绍,大家可以参看http://www.shaoqun.com/a/54588.aspx  网页设计]Ajax、Comet与Websocket,

我如果以后有时间,也会写出来的

3.如何使用websocket

客户端

在支持WebSocket的浏览器中,在创建socket之后。可以通过onopen,onmessage,onclose即onerror四个事件实现对socket进行响应

一个简单是示例

var ws = new WebSocket(“ws://localhost:8080”);
ws.onopen = function()
{
  console.log(“open”);
  ws.send(“hello”);
};
ws.onmessage = function(evt)
{
  console.log(evt.data)
};
ws.onclose = function(evt)
{
  console.log(“WebSocketClosed!”);
};
ws.onerror = function(evt)
{
  console.log(“WebSocketError!”);
};

1.var ws = new WebSocket(“ws://localhost:8080”);

申请一个WebSocket对象,参数是需要连接的服务器端的地址,同http协议使用http://开头一样,WebSocket协议的URL使用ws://开头,另外安全的WebSocket协议使用wss://开头。

ws.send(“hello”);

用于叫消息发送到服务端

2.ws.onopen = function() { console.log(“open”)};

当websocket创建成功时,即会触发onopen事件

3.ws.onmessage = function(evt) { console.log(evt.data) };

当客户端收到服务端发来的消息时,会触发onmessage事件,参数evt.data中包含server传输过来的数据

4.ws.onclose = function(evt) { console.log(“WebSocketClosed!”); };

当客户端收到服务端发送的关闭连接的请求时,触发onclose事件

5.ws.onerror = function(evt) { console.log(“WebSocketError!”); };

如果出现连接,处理,接收,发送数据失败的时候就会触发onerror事件

我们可以看出所有的操作都是采用事件的方式触发的,这样就不会阻塞UI,使得UI有更快的响应时间,得到更好的用户体验。

服务端

现在有很多的服务器软件支持websocket,比如node.js,jetty,tomcat等

这里我使用的是tomat 7.0和eclipse4.2

在tomcat下使用websocket首先需要导入相关的jar

tomcat7提供的与WebSocket相关的类均位于包org.apache.catalina.websocket之中(包org.apache.catalina.websocket的实现包含于文件catalina.jar之中

这里我们把tomcat的全部导入就行了

在build path->configure build path->librarise->add library->server runtime->apache tomcat v7.0

image

同时需要import以下包

import org.apache.catalina.websocket.MessageInbound;
import org.apache.catalina.websocket.StreamInbound;
import org.apache.catalina.websocket.WsOutbound;
import org.apache.catalina.websocket.WebSocketServlet;

我们需要两个类

第一个用于处理websocket请求

第二个用于处理每一次具体的WebSocket任务

第一个类

public class SocketServer extends WebSocketServlet {
    private static final long serialVersionUID = 1L;
    //……
    @Override
    protected StreamInbound createWebSocketInbound(String arg0,
            HttpServletRequest arg1) {
        // TODO Auto-generated method stub
        return new ChatWebSocket(users);
    }
}

这个Servlet继承自WebSocketServlet,实现createWebSocketInbound方法。该方法返回第二个类的实例。

第二个类

public class ChatWebSocket extends MessageInbound {

@Override
        protected void onTextMessage(CharBuffer message) throws IOException {

}

@Override
        protected void onOpen(WsOutbound outbound) {
            
        }

@Override
        protected void onClose(int status) {

}

@Override
        protected void onBinaryMessage(ByteBuffer arg0) throws IOException {

}
//其余略

}

protected void onTextMessage(CharBuffer message) throws IOException { }

文本消息响应

protected void onBinaryMessage(ByteBuffer arg0) throws IOException { }

二进制消息响应

protected void onOpen(WsOutbound outbound) { }

建立连接的触发的事件

protected void onClose(int status) { }

关闭连接时触发的事件

4.程序代码

html部分

<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<script type="text/javascript" src="js/jquery.js"></script>
<script type="text/javascript" src="js/socket.js"></script>
<title>无标题文档</title>
</head>
<script language="javascript">

</script>
<body>
<table>
  <tr>
    <td>Message</td>
    <td><input type="text" id="message"></td>
  </tr>
  <tr>
    <td>Name</td>
    <td><input type="text" id="othername"></td>
  </tr>
  <tr>
    <td><input id="sendbutton" type="button" value="send" onClick="click"  disabled="true">
      </input></td>
  </tr>
</table>
<script>

</script>
</body>
</html>

js部分(关于jquery部分不进行讲解)

var username = window.prompt("输入你的名字:");

document.write("Welcome<p id=\"username\">"+username+"</p>");

if (!window.WebSocket && window.MozWebSocket)
    window.WebSocket=window.MozWebSocket;
if (!window.WebSocket)
    alert("No Support ");
var ws;

$(document).ready(function(){
    
     $("#sendbutton").attr("disabled", false);
     $("#sendbutton").click(sendMessage);

startWebSocket();
})

function sendMessage()
{
    var othername=$("#othername").val();
    var msg="MSG\t"+username+"_"+othername+"_"+$("#message").val();
    send(msg);
}
function send(data)
{
    console.log("Send:"+data);
    ws.send(data);
}
function startWebSocket()
{    
    ws = new WebSocket("ws://" + location.host + "/WebSocket/SocketServer");
    ws.onopen = function(){
        console.log("success open");
        $("#sendbutton").attr("disabled", false);
    };
     ws.onmessage = function(event)
     {
         console.log("RECEIVE:"+event.data);
         handleData(event.data); 
     };
      ws.onclose = function(event) { 
    console.log("Client notified socket has closed",event); 
  }; 
  
}

function handleData(data)
{
    var vals=data.split("\t");
    var msgType=vals[0];
    switch(msgType)
    {
    case "NAME":
        var msg=vals[1];
        var mes="NAME"+"\t"+msg+"_"+ username;
        send(mes);
        break;
    case "MSG":
        var val2s=vals[1].split("_");
        var from=val2s[0];
        var message=val2s[2];
        alert(from+":"+message);
        break;
    default:
        break;
            
    }
}

java部分

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;

import javax.servlet.http.HttpServletRequest;
import java.util.Set;

import java.util.concurrent.CopyOnWriteArraySet;

import org.apache.catalina.websocket.MessageInbound;
import org.apache.catalina.websocket.StreamInbound;
import org.apache.catalina.websocket.WsOutbound;
import org.apache.catalina.websocket.WebSocketServlet;

public class SocketServer extends WebSocketServlet {
    private static final long serialVersionUID = 1L;
    public final Set<ChatWebSocket> users = new CopyOnWriteArraySet<ChatWebSocket>();

public static int USERNUMBER = 1;
    @Override
    protected StreamInbound createWebSocketInbound(String arg0,
            HttpServletRequest arg1) {
        // TODO Auto-generated method stub
        return new ChatWebSocket(users);
    }
    public class ChatWebSocket extends MessageInbound {

private String username;
        private Set<ChatWebSocket> users = new CopyOnWriteArraySet<ChatWebSocket>();;

public ChatWebSocket() {

}

public ChatWebSocket(Set<ChatWebSocket> users) {
            this.users = users;
        }

@Override
        protected void onTextMessage(CharBuffer message) throws IOException {
            // 这里处理的是文本数据
        }

public void onMessage(String data) {
            String[] val1 = data.split("\\t");
            if(val1[0].equals("NAME"))
            {
                String[] val2=val1[1].split("_");
                for(ChatWebSocket user:users){
                    if (user.username.equals(val2[0])){
                        user.username=val2[1];
                    }
                }
            }
            else if(val1[0].equals("MSG"))
            {
                String[] val2=val1[1].split("_");
                for(ChatWebSocket user:users){
                    if (user.username.equals(val2[1])){
                        try {
                            CharBuffer temp=CharBuffer.wrap(data);
                            user.getWsOutbound().writeTextMessage(temp);
                        } catch (IOException e) {
                            // TODO Auto-generated catch block
                            e.printStackTrace();
                        }
                    }
                }        
            }
            else
            {
                System.out.println("ERROR");
            }

}

@Override
        protected void onOpen(WsOutbound outbound) {
            // this.connection=connection;
            this.username = "#" + String.valueOf(USERNUMBER);
            USERNUMBER++;
            try {
                String message = "NAME" + "\t" + this.username;
                CharBuffer buffer = CharBuffer.wrap(message);
                this.getWsOutbound().writeTextMessage(buffer);
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            users.add(this);
        }

@Override
        protected void onClose(int status) {
            users.remove(this);

}

@Override
        protected void onBinaryMessage(ByteBuffer arg0) throws IOException {

}

}
    
}

解释

这里我的想法是

1 每个用户在访问的时候首先需要输入自己的名字,接着向服务端发送连接请求

2 服务端在接受到客户端的连接请求后,会new ChatWebSocket(users);用于处理这个请求,并把它加入在线的用户列表中,由于这个时候,服务端尚不知道客户的名字。它会给这个用户假定一个名字,#1,然后服务端会发送"NAME" + "\t" +“#1”给客户端,你叫什么?

3 客户端收到这个消息会知道,服务器在问自己叫什么名字,于是客户端会发送"NAME"+"\t"+“#1”+"_"+ 自己的名字到服务端,(我叫xxx)

4 服务端收到这个消息后根据#1在当前在线的用户列表中进行查找,将#1替换为客户的名字,这样服务端就知道了这个客户的名字了

5 当客户离开时,服务端会触发onClose事件,服务端会把当前用户从在线列表中移除

用图画出来类似这样(画的不好,—_—!!)

代码

js

ws = new WebSocket("ws://" + location.host + "/WebSocket/SocketServer");

连接服务端

java

protected StreamInbound createWebSocketInbound(String arg0,
            HttpServletRequest arg1) {
        // TODO Auto-generated method stub
        return new ChatWebSocket(users);
    }

创建一个chatwebsocket用于处理这个请求,触发该chatwebsocket对象的onOpen事件

@Override
    protected void onOpen(WsOutbound outbound) {
        // this.connection=connection;
        this.username = "#" + String.valueOf(USERNUMBER);
        USERNUMBER++;
        try {
            String message = "NAME" + "\t" + this.username;
            CharBuffer buffer = CharBuffer.wrap(message);
            this.getWsOutbound().writeTextMessage(buffer);
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        users.add(this);
    }

为这个客户假定一个姓名,并发送NAME+“\t”+假定的姓名  给该客户端,同时将该客户端加入当前连接的客户列表中

js

function handleData(data)
{
    var vals=data.split("\t");
    var msgType=vals[0];
    switch(msgType)
    {
    case "NAME":
        var msg=vals[1];
        var mes="NAME"+"\t"+msg+"_"+ username;
        send(mes);
        break;
   //………            
    }
}

接受并处理服务端发来到的消息,发现是服务端问自己叫什么名字,于是发送”NAME"+"\t"+假定的名字+"_"+ 真正的名字 给服务端

java

public void onMessage(String data) {
        String[] val1 = data.split("\\t");
        if(val1[0].equals("NAME"))
        {
            String[] val2=val1[1].split("_");
            for(ChatWebSocket user:users){
                if (user.username.equals(val2[0])){
                    user.username=val2[1];
                }
            }
        }

//………
}

处理并接受客户端发来的消息,发现是客户端回复自己叫什么名字,于是在根据先前假定的名字在当前连接的客户列表中进行查找,将假名变成真名

js

function sendMessage()
{
    var othername=$("#othername").val();
    var msg="MSG\t"+username+"_"+othername+"_"+$("#message").val();
    send(msg);
}

客户对另一个人发起对话,消息格式为:“MSG”+自己的名字+_+对方的名字+_+消息

java

public void onMessage(String data) {
       ///…………
        else if(val1[0].equals("MSG"))
        {
            String[] val2=val1[1].split("_");
            for(ChatWebSocket user:users){
                if (user.username.equals(val2[1])){
                    try {
                        CharBuffer temp=CharBuffer.wrap(data);
                        user.getWsOutbound().writeTextMessage(temp);
                    } catch (IOException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                }
            }        
        }
       ///…………

}

发现是客户发送的消息,根据对方的姓名,在当前连接的客户列表中查找,并将消息发给他

js

function handleData(data)
{
    var vals=data.split("\t");
    var msgType=vals[0];
    switch(msgType)
    {
    ///…

case "MSG":
        var val2s=vals[1].split("_");
        var from=val2s[0];
        var message=val2s[2];
        alert(from+":"+message);
        break;
    default:
        break;
            
    }
}

发现是另一个客户发来的消息,通过alert显示出来

java

@Override
        protected void onClose(int status) {
            users.remove(this);

}

发现客户离开了,将客户从连接的客户列表中移除

可以改进的地方

1.若客户端A发送消息给B时,B不在线,可将消息存入数据库中,当发现B上线时,从数据库中取出,发送给B

2 服务端发送你叫什么时,可加入超时机制,若客户端一定时间内没有回复自己叫什么,则可将该客户从在线列表中删掉

html5利用websocket完成的推送功能的更多相关文章

  1. html5利用websocket完成的推送功能(tomcat)

    html5利用websocket完成的推送功能(tomcat) 利用websocket和java完成的消息推送功能,服务器用的是tomcat7.0.42,一些东西是自己琢磨的,也不知道恰不恰当,不恰当 ...

  2. HTML5与php实现消息推送功能

    1.html页面basic_sse.html <!DOCTYPE html> <html lang="en"> <head> <meta ...

  3. 拾人牙慧篇之——基于HTML5中websocket来实现消息推送功能

    一.写在前面 要求做一个,后台发布信息,前台能即时得到通知的消息推送功能.网上搜了也有很多方式,ajax的定时询问,Comet方式,Server-Sent方式,以及websocket.表示除了定时询问 ...

  4. HTML5 学习总结(五)——WebSocket与消息推送

    B/S结构的软件项目中有时客户端需要实时的获得服务器消息,但默认HTTP协议只支持请求响应模式,这样做可以简化Web服务器,减少服务器的负担,加快响应速度,因为服务器不需要与客户端长时间建立一个通信链 ...

  5. HTML5 学习笔记(五)——WebSocket与消息推送

    B/S结构的软件项目中有时客户端需要实时的获得服务器消息,但默认HTTP协议只支持请求响应模式,这样做可以简化Web服务器,减少服务器的负担,加快响应速度,因为服务器不需要与客户端长时间建立一个通信链 ...

  6. 基于Tomcat7、Java、WebSocket的服务器推送聊天室

    http://blog.csdn.net/leecho571/article/details/9707497 http://blog.fens.me/java-websocket-intro/ jav ...

  7. Tomcat学习总结(4)——基于Tomcat7、Java、WebSocket的服务器推送聊天室

    前言           HTML5 WebSocket实现了服务器与浏览器的双向通讯,双向通讯使服务器消息推送开发更加简单,最常见的就是即时通讯和对信息实时性要求比较高的应用.以前的服务器消息推送大 ...

  8. WebSocket与消息推送

    B/S结构的软件项目中有时客户端需要实时的获得服务器消息,但默认HTTP协议只支持请求响应模式,这样做可以简化Web服务器,减少服务器的负担,加快响应速度,因为服务器不需要与客户端长时间建立一个通信链 ...

  9. Asp.NET MVC 中使用 SignalR 实现推送功能

    一,简介Signal 是微软支持的一个运行在 Dot NET 平台上的 html websocket 框架.它出现的主要目的是实现服务器主动推送(Push)消息到客户端页面,这样客户端就不必重新发送请 ...

随机推荐

  1. POJ 2083 Fractal 分形

    去年校赛团队赛就有一道分形让所有大一新生欲生欲死…… 当时就想学了 结果一直拖到…… 今天上午…… 马上要省选了 才会一点基础分形…… 还是自己不够努力啊…… 分形主要是要找到递归点…… 还有深度…… ...

  2. tomcat安装完设定用户名和密码

    vi conf/tomcat-user.xml<tomcat-users> <role rolename="manager"/> <role role ...

  3. Java操作*.properties

    java中的properties文件是一种配置文件,主要用于表达配置信息,文件类型为*.properties,格式为文本文件,文件的内容是格式是"键=值"的格式,在properti ...

  4. DNS 域名系统 (Domain Name System)

      DNS 域名系统 (Domain Name System) 许多应用层软件经常直接使用域名系统 DNS (Domain Name System),但计算机的用户只是间接而不是直接使用域名系统. 因 ...

  5. java 常用的三大集合类

    一.Set集合.其主要实现类有HashSet.TreeSet.存放对象的引用,不允许有重复对象. 代码: public class SetTest { public static void main( ...

  6. c语言-何为编程?

    大牛,请绕过. 新手,如果你怕我误人子弟,那也请绕过. 以下纯属个人YY 何为编程?何为程序? 说简单也简单,说复杂也复杂. 我在自学的道路上也有两三年了,也探索了两三年(非连续性),却只停留在入门阶 ...

  7. java中list集合的内容,如何使用像数据库中group by形式那样排序

    java中list集合的内容,如何使用像数据库中group by形式那样排序,比如:有一个 List<JavaBean> 他中包含了一些如下的内容JavaBean:name    mone ...

  8. /etc/fstab 文件解释

    /etc/fstab 文件解释 文件fstab包含了你的电脑上的存储设备及其文件系统的信息.它是决定一个硬盘(分区)被怎样使用或者说整合到整个系统中的唯一文件. 这个文件的全路径是/etc/fstab ...

  9. Codeforces Round #364 (Div. 2) D. As Fast As Possible

     D. As Fast As Possible time limit per test 1 second memory limit per test 256 megabytes input stand ...

  10. 弹出框,先弹出遮罩有透明度灰色100%高宽,置顶z-index:999---再弹出框最顶部z-index:9999

    <div class="mask"></div> <div class="maskbox"> <form id=&qu ...