TCP与Http混合开发,实现WEB应用与Windows桌面应用或者是单片机之间的通信

来源:互联网 发布:武汉家装哪家好 知乎 编辑:程序博客网 时间:2024/06/10 15:59

百度了好久,都没有找到类似的东西,网上的悠悠一个劲的和我解释Http是什么,哎呦我的天,真不知道现在的人是怎么了,连中文都看不懂了吗,这可是母语啊,亲们。

言归正传,我起初的想法是这样的:当客户端(Windows桌面应用、单片机)通过Tcp长连接上后,就可以自由通信了;但是有一个问题,就是与web的通信,怎么样进行数据的交互。然后我想到了web应用中的过滤器,过滤器的特点是一次初始化,多次调用,直到服务器瓦解,才终将消亡。那么这样就可以把过滤器抽象化,让它成为一个数据解析中转器,那么就可以实现与web间的通信了。

假想图:
假想图

项目结构:
项目结构

用到的jar包,有好多没用上的,不过没关系:
jar包

log4j.properties:

 ### \u8BBE\u7F6E###log4j.rootLogger = debug,stdout,D,E### \u8F93\u51FA\u4FE1\u606F\u5230\u63A7\u5236\u62AC ###log4j.appender.stdout = org.apache.log4j.ConsoleAppenderlog4j.appender.stdout.Target = System.outlog4j.appender.stdout.layout = org.apache.log4j.PatternLayoutlog4j.appender.stdout.layout.ConversionPattern = [%-5p] %d{yyyy-MM-dd HH:mm:ss,SSS} method:%l%n%m%n### \u8F93\u51FADEBUG \u7EA7\u522B\u4EE5\u4E0A\u7684\u65E5\u5FD7\u5230=E://logs/error.log ###log4j.appender.D = org.apache.log4j.DailyRollingFileAppenderlog4j.appender.D.File = C://log/log.loglog4j.appender.D.Append = truelog4j.appender.D.Threshold = DEBUG log4j.appender.D.layout = org.apache.log4j.PatternLayoutlog4j.appender.D.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss}  [ %t:%r ] - [ %p ]  %m%n### \u8F93\u51FAERROR \u7EA7\u522B\u4EE5\u4E0A\u7684\u65E5\u5FD7\u5230=E://logs/error.log ###log4j.appender.E = org.apache.log4j.DailyRollingFileAppenderlog4j.appender.E.File =C://log/error.log log4j.appender.E.Append = truelog4j.appender.E.Threshold = ERROR log4j.appender.E.layout = org.apache.log4j.PatternLayoutlog4j.appender.E.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss}  [ %t:%r ] - [ %p ]  %m%n

MyServer.java

package me.mxzf.core;import java.io.IOException;import java.net.ServerSocket;import java.net.Socket;import java.util.Hashtable;import org.apache.log4j.Logger;/** * 线程同步锁、单例模式<br> * 通过调用Servers.getInstance()方法获取ServerSocket的唯一实例<br> * 启用Socket服务,并且告知监听PORT指定的端口<br> *  *  *  * @Title: Servers * @Dscription: Socket服务器 * @author Deleter * @date 2016年11月8日 下午2:25:54 * @version 1.0 */public class MyServer {    private static Logger logger = Logger.getLogger(MyServer.class);    public static final int PORT = 60000;    private static ServerSocket serverSocket;    // 在线列表(静态的,无论寄生类实例化多少次都有且仅有一个实例对象)    public static Hashtable<Integer, Socket> clientList = null;    private MyServer() {    }    // http://localhost:8080/WebTcpFixed/TcpData?asdasd=asdasd    public static ServerSocket getInstance() throws IOException {        logger.debug("获取ServerSocket实例对象");        if (serverSocket == null) {            synchronized (ServerSocket.class) {                if (serverSocket == null) {                    logger.debug("创建ServerSocket实例对象");                    serverSocket = new ServerSocket(PORT);                }            }        }        return serverSocket;    }}

EncodeFilter.java

package me.mxzf.filter;import java.io.IOException;import javax.servlet.Filter;import javax.servlet.FilterChain;import javax.servlet.FilterConfig;import javax.servlet.ServletException;import javax.servlet.ServletRequest;import javax.servlet.ServletResponse;import javax.servlet.annotation.WebFilter;import org.apache.log4j.Logger;/** * 主要拦截所有请求<br> * 将编码格式重置为UTF-8<br> * 以及响应的信息为json文本串 *  *  * @Title: EncodeFilter * @Dscription: 编码过滤器 * @author Deleter * @date 2016年11月8日 下午2:15:16 * @version 1.0 */@WebFilter(description = "编码过滤器", urlPatterns = { "/*" })public class EncodeFilter implements Filter {    private static Logger logger = Logger.getLogger(EncodeFilter.class);    public static final String ENCODEING = "UTF-8";    public void destroy() {        logger.debug("编码过滤器 - 销毁...");    }    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)            throws IOException, ServletException {        request.setCharacterEncoding(ENCODEING);        response.setCharacterEncoding(ENCODEING);        // response.setContentType("application/Json;charset=" + ENCODEING);        response.setContentType("text/html;charset=" + ENCODEING);        logger.debug("设置编码参数");        chain.doFilter(request, response);    }    public void init(FilterConfig fConfig) throws ServletException {        logger.debug("编码过滤器 - 初始化...");    }}

HttpFilter.java

package me.mxzf.filter;import java.io.IOException;import java.net.ServerSocket;import java.net.Socket;import java.util.Hashtable;import javax.servlet.Filter;import javax.servlet.FilterChain;import javax.servlet.FilterConfig;import javax.servlet.ServletException;import javax.servlet.ServletRequest;import javax.servlet.ServletResponse;import javax.servlet.annotation.WebFilter;import org.apache.log4j.Logger;import me.mxzf.core.MyServer;import me.mxzf.thread.CilentThread;/** * 根据过滤器只初始化一次、多次调用的特点<br> * 在init()方法中初始化ServerSocket实例对象,生成唯一的在线列表<br> * 在此方法中开启一个匿名的accept线程,当客户端连上的时候,客户端首次发起通信,进行握手注册<br> * 当调用web接口的时候,就会调用此拦截器中的findByKey()方法<br> * 根据对应的key,寻找对应的通道,将数据传输到客户端/服务端<br> *  *  * @Title: HttpFilter * @Dscription: 请求过滤器 * @author Deleter * @date 2016年11月8日 下午2:17:04 * @version 1.0 */@WebFilter(description = "请求过滤器", urlPatterns = { "/TcpData" })public class HttpFilter implements Filter {    private static Logger logger = Logger.getLogger(HttpFilter.class);    // 是否持续监听    private boolean isDone = true;    // 数据接口唯一实例    public ServerSocket socketMain;    @Override    public void init(FilterConfig filterConfig) throws ServletException {// 初始化        logger.debug("请求过滤器 - 初始化...");        try {            socketMain = MyServer.getInstance();// 获得ServerSocket的唯一实例对象            MyServer.clientList = new Hashtable<Integer, Socket>();// 创建在线列表            new Thread(new Runnable() {// 监听线程                @Override                public void run() {                    try {                        while (isDone) {// 持续监听?                            Socket socket = socketMain.accept();// 等待客户端的连接                            new Thread(new CilentThread(socket)).start();// 将注册验证任务放到一个线程中进行                        }                    } catch (IOException e) {                        e.printStackTrace();                    }                }            }).start();        } catch (IOException e) {            e.printStackTrace();        }    }    @Override    public void destroy() {        logger.debug("请求过滤器 - 销毁...");    }    @Override    public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain)            throws IOException, ServletException {        logger.debug("Http过滤 - 调用");        filterChain.doFilter(request, response);    }    /**     * 写出数据     *      * @param key     *            关键key     * @param value     *            数据     */    public static void writeByKey(String key, String value) {        if (MyServer.clientList != null && MyServer.clientList.get(key) != null) {            try {                // 数据写入对应"客户端"的流道                logger.debug("数据写入对应'客户端'的流道 (key=" + key + ") - value:" + value);                MyServer.clientList.get(key).getOutputStream().write(value.getBytes());                return;            } catch (IOException e) {                e.printStackTrace();            }        }    }}

TcpData.java

package me.mxzf.Servlet;import java.io.IOException;import javax.servlet.ServletException;import javax.servlet.annotation.WebServlet;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import org.apache.log4j.Logger;import me.mxzf.filter.HttpFilter;/** * 所有的请求先通过Encode编码过滤器和Http请求拦截器<br> * 当有请求来到TcpData时,执行doPost()方法,从请求体中取出对应的key和值<br> * 当且只当key和value的值不为空的情况下,调用Http请求拦截器的findByKey(),向对应的通道写出数据<br> *  *  *  * @Title: TcpData * @Dscription: Servlet * @author Deleter * @date 2016年11月8日 下午2:29:25 * @version 1.0 */@WebServlet("/TcpData")public class TcpData extends HttpServlet {    private static Logger logger = Logger.getLogger(TcpData.class);    private static final long serialVersionUID = 1L;    public TcpData() {        super();        logger.debug("TcpData - Servlet初始化...");    }    protected void doGet(HttpServletRequest request, HttpServletResponse response)            throws ServletException, IOException {    }    protected void doPost(HttpServletRequest request, HttpServletResponse response)            throws ServletException, IOException {        // 经过TcpData过滤器,来到这里        logger.debug("doPost");        String key = request.getParameter("key");        String value = request.getParameter("value");        if (key != null && value != null) {            HttpFilter.writeByKey(key, value);            response.getWriter().write(value);        }    }    @Override    public void destroy() {        super.destroy();        logger.debug("TcpData - Servlet销毁...");    }}

CilentThread.java

package me.mxzf.thread;import java.io.IOException;import java.net.Socket;import org.apache.log4j.Logger;import me.mxzf.core.MyServer;/** * 当服务器accept()方法接收到一个客户端的时候</br> * 创建一个新的线程去确认来者何人 </br> * 根据关键key确定了来者是谁之后</br> * 添加到在线列表,以及添加对应的socket对象</br> * * </br> * 关键key为int类型</br> *  *  * @Title: CilentThread * @Dscription: 服务器与客户端的注册线程 * @author Deleter * @date 2016年11月8日 下午1:59:58 * @version 1.0 */public class CilentThread implements Runnable {    private static Logger logger = Logger.getLogger(CilentThread.class);    private boolean isDone = true;    private Integer key;    private Socket socket;    public CilentThread(Socket socket) {        this.socket = socket;        logger.debug("新建一个注册线程:" + Thread.currentThread().getName());    }    @Override    public void run() {        int buff = -1;// 关键key        try {            while (isDone) {// 客户端通信(关键key,流通道)                if (socket != null && socket.isConnected() && (buff = socket.getInputStream().read()) != -1) {                    this.key = buff;                    this.isDone = false;                    logger.debug("新用户到达:" + Thread.currentThread().getName());                    break;// 停止阻塞                }            }        } catch (IOException e) {            e.printStackTrace();        }        MyServer.clientList.put(key, this.socket);// 添加到在线列表        logger.debug("server(记录流向、注册) - key:" + key);// 线程结束    }}

好了,基本上实现过程就是这样的,代码我都贴出来了。

附上源代码链接: http://pan.baidu.com/s/1nuVFjZz 密码: 2d8h,欢迎提出问题和建议。

2017.1.20日补充:其实可以不用那么麻烦,直接在spring容器中注册通过配置init-method即可以实现TCP/UDP + Http共存。

0 0