基于java servlet的http推送服务

来源:互联网 发布:ip138离线数据库 编辑:程序博客网 时间:2024/06/18 12:01

前几天项目要求实现安卓手机端的推送功能,由于原来的工程与服务端通信用的是http的协议,又不想用极光推送,所以想自己做一个使用http的推送实现,最后研究了几天,今天把例子发出来:
http的原理就是。请求-响应过程,基本流程如下:

http

在一般响应的过程中,服务端一般不会阻塞,也就是说,服务端在收到客户端的请求后会立即进行处理,然后响应

数据给客户端,如果服务端响应时间过长,客户端就会出异常。但是,在http推送的过程中,客户端要和服务端保持一个连接,当服务端需要推送的时候,才把数据响应给客户端。这样就需要另外一个请求来通知服务端进行推送,基本流程如下:
http推送流程

基本流程详解:客户端给服务端发送一个请求,等待响应,服务端收到客户端的请求之后,并不会立即做出响应,而是会阻塞不动(因为http每一个请求都是一个线程)。在这个过程中,需要保证客户端不会出现time out异常(可以设置时间)。当有推送的客户端需要进行推送的时候,会向服务端发送一个推送的请求。服务端收到推送的请求后,解锁前面那个被阻塞的请求,并响应给客户端。客户端收到响应后要再次发送请求给服务端,依次循环。这样就达成了推送的效果。话不多说,直接上代码:

首先,新建一个web项目HttpPlusTest:
这里写图片描述

新建一个客户端servlet请求,用来处理来自客户端的请求,在这里,需要使用一个对象来使线程阻塞,为了让后面的请求可以解锁线程并设置推送内容,我们创造一个Map类型的对象,并将对象放入系统全局变量里面:

package com.sgcc.test;import java.io.IOException;import java.util.ArrayList;import java.util.HashMap;import java.util.List;import java.util.Map;import javax.servlet.ServletException;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;public class ClientPush extends HttpServlet {    private static final long serialVersionUID = 1L;    public ClientPush() {        super();    }    protected void doGet(HttpServletRequest request, HttpServletResponse response)            throws ServletException, IOException {        //获取系统全局变量,在这里为了方便,暂时用Appliection代替。如果获取到的内容为空,则在下面new一个集合放进来,        //这个集合里面存放了所有要被推送的内容的对象,暂时用Map代替,在实际使用的时候可以定义实体类。        List<Map<String, String>> pushList = (List<Map<String, String>>) request.getSession().getServletContext()                .getAttribute("PUSH");        //使用这个对象的线程锁来让当前请求的线程暂停执行,并将这个对象放入全局变量,在需要推送的时候调用 这个对象的notify方法可以唤起线程        Map<String, String> temp = new HashMap<String, String>();        if (pushList == null) {            List<Map<String, String>> list = new ArrayList<>();            request.getSession().getServletContext().setAttribute("PUSH", list);            list.add(temp);        } else {            pushList.add(temp);        }        //将对象和当前线程关联起来。在调用对象的wait()方法的时候,当前线程会阻塞        synchronized (temp) {            try {                //使当前线程阻塞。等待其他线程唤醒它                temp.wait();                //被唤醒后,将内容发送给客户端,响应完成!                response.getWriter().write(temp.get("pushContext"));                response.getWriter().flush();            } catch (InterruptedException e) {                e.printStackTrace();            } finally {                if (pushList != null) {                    pushList.remove(temp);                }            }        }    }    protected void doPost(HttpServletRequest request, HttpServletResponse response)            throws ServletException, IOException {        doGet(request, response);    }}

当客户端请求到这个servlet的时候,会在44行代码的时候被阻塞,需要另外一个线程来唤醒这个线程,所以,我们还需要一个用来处理推送客户端的servlet 来遍历唤醒他:

package com.sgcc.test;import java.io.IOException;import java.util.Date;import java.util.List;import java.util.Map;import javax.servlet.ServletException;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;public class PutPush extends HttpServlet {    private static final long serialVersionUID = 1L;    public PutPush() {        super();    }    protected void doGet(HttpServletRequest request, HttpServletResponse response)            throws ServletException, IOException {        // 获取当前系统所有待推送的请求        List<Map<String, String>> pushList = (List<Map<String, String>>) request.getSession().getServletContext()                .getAttribute("PUSH");        if (pushList == null || pushList.size() == 0) {            response.getWriter().write( "推送失败,");            response.getWriter().flush();            return;        }        // 遍历推送        for (Map<String, String> map : pushList) {            synchronized (map) {                // 设置推送内容                map.put("pushContext", new Date().toString() + "\n");                map.notify();            }        }        response.getWriter().write( "推送成功,共推送:" + pushList.size() + "个");        response.getWriter().flush();    }    protected void doPost(HttpServletRequest request, HttpServletResponse response)            throws ServletException, IOException {        doGet(request, response);    }}

最后还要在web.xml 中声明这两个servlet:

<?xml version="1.0" encoding="UTF-8"?><web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"    xmlns="http://java.sun.com/xml/ns/javaee"    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"    id="WebApp_ID" version="2.5">    <display-name>HttpPlusTest</display-name>    <welcome-file-list>        <welcome-file>index.html</welcome-file>        <welcome-file>index.htm</welcome-file>        <welcome-file>index.jsp</welcome-file>        <welcome-file>default.html</welcome-file>        <welcome-file>default.htm</welcome-file>        <welcome-file>default.jsp</welcome-file>    </welcome-file-list>    <servlet>        <description></description>        <display-name>ClientPush</display-name>        <servlet-name>ClientPush</servlet-name>        <servlet-class>com.sgcc.test.ClientPush</servlet-class>    </servlet>    <servlet-mapping>        <servlet-name>ClientPush</servlet-name>        <url-pattern>/ClientPush</url-pattern>    </servlet-mapping>    <servlet>        <description></description>        <display-name>PutPush</display-name>        <servlet-name>PutPush</servlet-name>        <servlet-class>com.sgcc.test.PutPush</servlet-class>    </servlet>    <servlet-mapping>        <servlet-name>PutPush</servlet-name>        <url-pattern>/PutPush</url-pattern>    </servlet-mapping></web-app>

简易的客户端代码:

package com.brains.test;import java.io.InputStream;import java.net.HttpURLConnection;import java.net.MalformedURLException;import java.net.URL;import java.net.URLConnection;public class TestHttp {    public static void main(String[] args) throws Exception {        URL url=new URL("http://127.0.0.1:7002/HttpPlusTest/ClientPush");        HttpURLConnection connection = (HttpURLConnection) url.openConnection();        connection.connect();        connection.setConnectTimeout(0);        InputStream in = connection.getInputStream();        byte[] b=new byte[1024];        int len=0;        while((len=in.read(b))!=-1){            System.out.println(new String(b));        }    }}

执行这段客户端代码后,控制台暂时不会输出任何东西,然后在浏览器请求推送的时候,客户端会吧当前时间输出出来

ps:个人原创,转载请注明出处!

0 0
原创粉丝点击