精华内容
下载资源
问答
  • java socket通信自定义消息协议

    热门讨论 2010-05-26 16:26:34
    java socket通信自定义消息协议,socket以字节码的方式通信传递,客户端与服务器端分别进行转换与解析的过程实现简单的消息协议
  • java 自定义通讯协议

    千次阅读 2017-07-12 17:58:09
    JAVA默认提供了对file,ftp,gopher,http,https,jar,mailto,netdoc协议的支持。当我们要利用这些协议来创建应用时,主要会涉及到如下几个类:  1.java.net.URL:URL资源  2.java.net.URLConnection:各种URL...

     JAVA默认提供了对file,ftp,gopher,http,https,jar,mailto,netdoc协议的支持。当我们要利用这些协议来创建应用时,主要会涉及到如下几个类:

          1.java.net.URL:URL资源

          2.java.net.URLConnection:各种URL资源连接器

        例如,当我们利用HTTP协议获取Web资源时,通常的过程如下:

     

    Java代码   收藏代码
    1. URL url = new URL("http://www.163.com");  
    2. URLConnection conneciotn = url.openConnection();  

     

     

            URL和URLConnection是如何做到对协议支持的呢?在它们的内部,主要涉及到了如下几个类:

            1.URLStreamHandler:协议的流处理器,负责对特定URL协议的解析,并创建符合当前协议的URLConnection;

            2.URLStreamHandlerFactory:协议处理工厂,负责为特定协议找到正确的URLStreamHandler。

            当利用URL对象创建资源时,其构造函数在去掉协议字段后将其传给URLStreamHandlerFactory,由该工厂来接受协议,为该协议找到并创建适当的URLStreamHandler实现类,最后存储在URL对象的一个字段中(即URL类中transient修饰的URLStreamHandler成员属性)。
           URLStreamHandler和URLConnection总是成对出现的。因此,若要实现对新协议的支持时,需同时实现这两个抽象类,分别负责对协议的解析,以及与服务器的交互(数据转换等)。

          另外,JAVA是如何识别当前URL协议该由哪个URLStreamHandler和URLConnection来处理的呢?在创建URL对象时,其内部调用了一个getURLStreamHandler(String protocol)静态方法,它将根据协议的名称来找到对应的URLStreamHandler实现类,其查找规则如下:

         1)检测是否创建了URLStreamHandlerFactory对象:如果创建,则直接使用createURLStreamHandler(String protocol)方法创建的协议处理器,否则进入步骤2);

         2)在java.protocol.handler.pkgs系统属性指定的包中查找与协议同名的子包和名为Handler的类,即负责处理当前协议的URLStreamHandler实现类必须在的<包名>.<协议名定义的包>中,并且类名称必须为Handler。例如:com.company.net.protocol.rdp包中的Handler类将用于处理RDP协议。若仍未找到则进入步骤3);

        3)在JDK rt.jar中的sun.net.www.protocol.<name>包中查找Handler类。例如,若当前协议为ftp,则URL所使用协议处理器就应该为sun.net.www.protocol.ftp包中的Handler类。如下图:
             下面结合一个实例来说明如何开发一个新的网络协议。

              背景:senv(Server Environment Protocol)协议可让客户端连接远程服务器后发送出请求,请求的内容就是URL查询参数部分,该协议的默认端口为9527。例如:senv://192.168.1.101:9527?pro=os.name,java.version表示获取192.168.1.101这台主机的操作系统名和JRE的版本信息,如果没有查询参数,客户端将默认发送一个"?",表示获取所有的系统属性信息。

           
     1.Senv协议处理器

    Java代码   收藏代码
    1. package com.daniele.appdemo.net.protocol.custom.senv;  
    2.   
    3. import java.io.IOException;  
    4. import java.net.URL;  
    5. import java.net.URLConnection;  
    6. import java.net.URLStreamHandler;  
    7.   
    8. /** 
    9.  * <p> 
    10.  *      自定义的senv协议处理器。 
    11.  *      由于senv协议的格式符合标准的URL格式: 
    12.  *          protocol://username@hostname:port/path/filename?query#fragment 
    13.  *      因此,这个实现类只需实现父类中的openConnection()方法即可。否则,需重写父类方法 
    14.  *      protected void parseURL(URL u, String spec, int start, int limit), 
    15.  *      来重新正确的设置URL的各个属性值,例如:host,port,query等。 
    16.  * </p>  
    17.  * @author  <a href="mailto:code727@gmail.com">Daniele</a> 
    18.  * @version 1.0.0, 2013-5-8 
    19.  * @see      
    20.  * @since   AppDemo1.0.0 
    21.  */  
    22. public class Handler extends URLStreamHandler {  
    23.   
    24.     /** 
    25.      * <p>当URL根据协议找到该处理器并调用openConnection()方法后,返回负责处理该协议连接的连接器</p>  
    26.      * @author <a href="mailto:code727@gmail.com">Daniele</a>  
    27.      * @param u 
    28.      * @return 
    29.      * @throws IOException  
    30.      * @since AppDemo1.0.0 
    31.      */  
    32.     @Override  
    33.     protected URLConnection openConnection(URL u) throws IOException {  
    34.         return new SenvURLConnection(u);  
    35.     }  
    36.       
    37. }  

     2.Senv协议连接器

    Java代码   收藏代码
    1. package com.daniele.appdemo.net.protocol.custom.senv;  
    2.   
    3. import java.io.IOException;  
    4. import java.io.InputStream;  
    5. import java.io.OutputStream;  
    6. import java.net.Socket;  
    7. import java.net.URL;  
    8. import java.net.URLConnection;  
    9.   
    10. import com.daniele.appdemo.util.StringUtils;  
    11.   
    12. /** 
    13.  * <p>自定义senv协议连接器</p>  
    14.  * @author  <a href="mailto:code727@gmail.com">Daniele</a> 
    15.  * @version 1.0.0, 2013-5-8 
    16.  * @see      
    17.  * @since   AppDemo1.0.0 
    18.  */  
    19. public class SenvURLConnection extends URLConnection {  
    20.       
    21.     /** senv协议的默认端口号 */  
    22.     public static final int DEFAULT_PORT = 9527;  
    23.       
    24.     private Socket connection = null;  
    25.   
    26.     public SenvURLConnection(URL url) {  
    27.         super(url);  
    28.     }  
    29.       
    30.     /** 
    31.      * <p>由于父类URLConnection中的getInputStream()方法不提供输入流的获取实现逻辑,因此这里需要重写此方法</p>  
    32.      * @author <a href="mailto:code727@gmail.com">Daniele</a>  
    33.      * @return 
    34.      * @throws IOException  
    35.      * @since AppDemo1.0.0  
    36.      */  
    37.     @Override  
    38.     public synchronized InputStream getInputStream() throws IOException {  
    39.         if (!connected)  
    40.             this.connect();  
    41.         return connection.getInputStream();  
    42.     }  
    43.       
    44.     /** 
    45.      * <p>senv协议连接操作</p>  
    46.      * @author <a href="mailto:code727@gmail.com">Daniele</a>  
    47.      * @throws IOException  
    48.      * @since AppDemo1.0.0  
    49.      */  
    50.     @Override  
    51.     public synchronized void connect() throws IOException {  
    52.         if (!connected) {  
    53.             int port = url.getPort();  
    54.             if (port < 1 || port > 65535)  
    55.                 port = DEFAULT_PORT;  
    56.             this.connection = new Socket(url.getHost(), port);  
    57.             connected = true;  
    58.             // 连接后立即发送请求  
    59.             sendRequest(url);  
    60.         }  
    61.     }  
    62.       
    63.     /** 
    64.      * <p>发送senv协议请求</p>  
    65.      * @author <a href="mailto:code727@gmail.com">Daniele</a>  
    66.      * @param u 
    67.      * @throws IOException  
    68.      * @since AppDemo1.0.0 
    69.      */  
    70.     protected void sendRequest(URL u) throws IOException {  
    71.         OutputStream outputStream = this.connection.getOutputStream();  
    72.           
    73.         String queryString = u.getQuery();  
    74.           
    75.         /* 
    76.          *  将URL的查询参数部分发送给服务器,由服务器负责解析查询后返回结果。 
    77.          *  当参数参数部分为空时,则发送一个"?",表示查询服务器系统环境的所有信息。 
    78.          */  
    79.         outputStream.write(StringUtils.isNotNullOrBlank(queryString)? queryString.getBytes() : "?".getBytes());  
    80.         outputStream.flush();  
    81.     }  
    82.   
    83. }  

     

    3.协议处理器工厂

    Java代码   收藏代码
    1. package com.daniele.appdemo.net.protocol.factory;  
    2.   
    3. import java.net.URLStreamHandler;  
    4. import java.net.URLStreamHandlerFactory;  
    5.   
    6. /** 
    7.  * <p> 
    8.  *       自定义协议的处理器工厂,负责针对每种自定义的协议而返回它们各自对应的协议处理器 
    9.  *       如果要用上述的查找规则1来安装协议处理器时,则需要用到这个类 
    10.  *</p>  
    11.  * @author  <a href="mailto:code727@gmail.com">Daniele</a> 
    12.  * @version 1.0.0, 2013-5-9 
    13.  * @see      
    14.  * @since   AppDemo1.0.0 
    15.  */  
    16. public class CustomProtocolFactory implements URLStreamHandlerFactory {  
    17.   
    18.     public URLStreamHandler createURLStreamHandler(String protocol) {  
    19.         if ("senv".equalsIgnoreCase(protocol))  
    20.             return new com.daniele.appdemo.net.protocol.custom.senv.Handler();  
    21.         return null;  
    22.     }  
    23.   
    24. }  

     

    4.处理Senv协议的服务器

    Java代码   收藏代码
    1. package com.daniele.appdemo.net.protocol.test;  
    2.   
    3. import java.io.IOException;  
    4. import java.net.InetAddress;  
    5. import java.net.InetSocketAddress;  
    6. import java.nio.ByteBuffer;  
    7. import java.nio.channels.SelectionKey;  
    8. import java.nio.channels.Selector;  
    9. import java.nio.channels.ServerSocketChannel;  
    10. import java.nio.channels.SocketChannel;  
    11. import java.nio.charset.Charset;  
    12. import java.nio.charset.CharsetDecoder;  
    13. import java.util.Iterator;  
    14.   
    15. import org.apache.log4j.Logger;  
    16.   
    17. import com.daniele.appdemo.util.StringUtils;  
    18. import com.daniele.appdemo.util.SystemUtils;  
    19.   
    20. /** 
    21.  * <p>处理Senv协议的服务器 
    22.  *    1.接收客户端请求 
    23.  *    2.发送响应结果 
    24.  *  </p>  
    25.  * @author  <a href="mailto:code727@gmail.com">Daniele</a> 
    26.  * @version 1.0.0, 2013-5-10 
    27.  * @see      
    28.  * @since   AppDemo1.0.0 
    29.  */  
    30.   
    31. public class SenvProtocolServer {  
    32.       
    33.     private static final Logger logger = Logger.getLogger(SenvProtocolServer.class);  
    34.       
    35.     /** Senv协议的请求参数标识 */  
    36.     public static final String REQUEST_PARAM_MARK = "pro=";  
    37.       
    38.     /** Senv协议服务的默认端口号 */  
    39.     private static final int DEFAULT_PORT = 9527;  
    40.   
    41.     /** 服务器的IP或主机名 */  
    42.     private String host;  
    43.       
    44.     /** 绑定了Senv协议服务的端口号 */  
    45.     private int port = 9527;  
    46.       
    47.     /** 当前就绪的服务端通道 */  
    48.     private ServerSocketChannel serverChannel;  
    49.       
    50.     /** 当前就绪的客户端通道 */  
    51.     private SocketChannel clientChannel;  
    52.       
    53.     /** 服务端的事件注册器 */  
    54.     private Selector selector;  
    55.       
    56.     /** 
    57.      * <p>启动Senv协议服务器</p>  
    58.      * @author <a href="mailto:code727@gmail.com">Daniele</a>  
    59.      * @throws IOException  
    60.      * @since AppDemo1.0.0 
    61.      */  
    62.     public void start() throws IOException {  
    63.           
    64.         serverChannel = ServerSocketChannel.open();  
    65.           
    66.         if (port < 1 || port > 65535)  
    67.             port = DEFAULT_PORT;  
    68.           
    69.         if (StringUtils.isNotNullOrBlank(host)) {  
    70.             serverChannel.socket().bind(new InetSocketAddress(InetAddress.getByName(host), port));  
    71.             logger.info("Start server " + host + ":" + port);  
    72.         } else {  
    73.             serverChannel.socket().bind(new InetSocketAddress(port));  
    74.             logger.info("Start server on port " + port);  
    75.         }  
    76.         serverChannel.configureBlocking(false);  
    77.         selector = Selector.open();  
    78.         serverChannel.register(selector, SelectionKey.OP_ACCEPT);   
    79.         handle();  
    80.     }  
    81.       
    82.     /** 
    83.      * <p>处理Senv协议请求</p>  
    84.      * @author <a href="mailto:code727@gmail.com">Daniele</a>  
    85.      * @throws IOException  
    86.      * @since AppDemo1.0.0 
    87.      */  
    88.     protected void handle() throws IOException {  
    89.         while (true) {  
    90.             selector.select();  
    91.             Iterator<SelectionKey> keySetIterator = selector.selectedKeys().iterator();  
    92.             SelectionKey cuurentKey = null;  
    93.             while (keySetIterator.hasNext()) {  
    94.                 // 获取当前就绪通道的键对象  
    95.                 cuurentKey = keySetIterator.next();  
    96.                 // 避免同一个就绪通道被重复处理  
    97.                 keySetIterator.remove();  
    98.                 try {  
    99.                     if (cuurentKey.isAcceptable()) {  
    100.                         serverChannel = (ServerSocketChannel) cuurentKey.channel();  
    101.                         clientChannel = serverChannel.accept();  
    102.                         if (clientChannel != null) {  
    103.                             logger.info("Receive request from "   
    104.                                     + clientChannel.socket().getInetAddress().getHostAddress() + ":"  
    105.                                     + clientChannel.socket().getLocalPort());  
    106.                             clientChannel.configureBlocking(false);  
    107.                             clientChannel.register(selector, SelectionKey.OP_READ | SelectionKey.OP_WRITE);  
    108.                         }   
    109.                     } else {  
    110.                         clientChannel = (SocketChannel) cuurentKey.channel();  
    111.                         if (cuurentKey.isReadable())   
    112.                             writeResponse();  
    113.                     }  
    114.                 } catch (IOException e) {  
    115.                     if (clientChannel != null && clientChannel.isOpen())  
    116.                         try {  
    117.                             /* 
    118.                              *  为防止服务端在读写客户端信息时,客户端由于某种原因被意外关闭引起服务端也被强制关闭的情况发生。 
    119.                              *  需在catch块中也需要对客户端的通道做关闭处理, 从而防止服务端也被强制关闭的严重问题。 
    120.                              *  另外,对就绪通道的读写过程需单独的在一个try...catch块中。 
    121.                              */  
    122.                             clientChannel.close();  
    123.                         } catch (IOException ioe) {  
    124.                             ioe.printStackTrace();  
    125.                         }  
    126.                 }   
    127.             }  
    128.         }  
    129.     }  
    130.       
    131.     /** 
    132.      * <p>读取客户端请求</p>  
    133.      * @author <a href="mailto:code727@gmail.com">Daniele</a>  
    134.      * @return  
    135.      * @throws IOException  
    136.      * @throws   
    137.      * @since AppDemo1.0.0 
    138.      */  
    139.     protected String readRequest() throws IOException {  
    140.         StringBuffer request = new StringBuffer();  
    141.         CharsetDecoder decoder = Charset.forName("UTF-8").newDecoder();  
    142.         ByteBuffer buffer = ByteBuffer.allocate(1024);  
    143.         while (clientChannel.read(buffer) > 0) {  
    144.             buffer.flip();  
    145.             request.append(decoder.decode(buffer).toString());  
    146.             buffer.clear();  
    147.         }  
    148.         return request.toString();  
    149.     }  
    150.       
    151.     /** 
    152.      * <p>向客户端返回响应结果</p>  
    153.      * @author <a href="mailto:code727@gmail.com">Daniele</a>  
    154.      * @throws IOException  
    155.      * @since AppDemo1.0.0 
    156.      */  
    157.     protected void writeResponse() throws IOException {  
    158.         String request = readRequest();  
    159.         int start = -1;  
    160.         // 如果发送的请求为"?"或请求中无指定的参数时,则查询所有的系统环境属性  
    161.         if ("?".equals(request) ||   
    162.                 (start = request.toLowerCase().indexOf(REQUEST_PARAM_MARK)) < 0) {  
    163.             clientChannel.write(ByteBuffer.wrap(SystemUtils.formatSystemProperties().getBytes()));  
    164.         } else {  
    165.             // 获取请求参数值  
    166.             String queryValueString = request.substring(start + REQUEST_PARAM_MARK.length());  
    167.             if (StringUtils.isNullOrBlank(queryValueString))  
    168.                 clientChannel.write(ByteBuffer.wrap(SystemUtils.formatSystemProperties().getBytes()));  
    169.             else {  
    170.                 int index = queryValueString.indexOf("&");  
    171.                 if (index > -1)  
    172.                     /* 
    173.                      *  如果请求参数值里出现了"&"字符, 
    174.                      *  则说明这个字符后面的内容则认为是其它一些请求参数的内容, 
    175.                      *  因此不对这部分内容作处理 
    176.                      */  
    177.                     queryValueString = queryValueString.substring(0, index);  
    178.                 clientChannel.write(ByteBuffer.wrap(SystemUtils.formatSystemProperties(queryValueString.split(",")).getBytes()));  
    179.             }  
    180.         }  
    181.         /* 
    182.          *  响应内容被发送出去之后添加换行标识, 
    183.          *  目的是让客户端的BufferedReader对象调用readLine()方法后能将当前行的内容读取出来 
    184.          */  
    185.         clientChannel.write(ByteBuffer.wrap("\n".getBytes()));  
    186.           
    187.         /* 
    188.          *  发送完响应信息后马上关闭与客户端之间的通道。 
    189.          *  目的在于让客户端读取完这些响应之后,就立即释放掉资源,从而让读操作不会一直处于阻塞状态 
    190.          */  
    191.         clientChannel.close();  
    192.     }  
    193.     public String getHost() {  
    194.         return host;  
    195.     }  
    196.   
    197.     public void setHost(String host) {  
    198.         this.host = host;  
    199.     }  
    200.   
    201.     public int getPort() {  
    202.         return port;  
    203.     }  
    204.   
    205.     public void setPort(int port) {  
    206.         this.port = port;  
    207.     }  
    208.   
    209.     public static void main(String[] args) {  
    210.         SenvProtocolServer server = new SenvProtocolServer();  
    211.         server.setHost("192.168.1.101");  
    212.         try {  
    213.             server.start();  
    214.         } catch (IOException e) {  
    215.             e.printStackTrace();  
    216.         }  
    217.     }  
    218.   
    219. }  

     

     4.Senv协议请求客户端

    Java代码   收藏代码
    1. /** 
    2.  * <p> 
    3.  *    Senv协议请求的客户端,主要功能分为: 
    4.  *    1.在创建第一次创建URL对象之前,添加对自定义协议的支持 
    5.  *    2.发送请求 
    6.  *    3.展示响应数据 
    7.  * </p>  
    8.  * @author  <a href="mailto:code727@gmail.com">Daniele</a> 
    9.  * @version 1.0.0, 2013-5-9 
    10.  * @see      
    11.  * @since   AppDemo1.0.0 
    12.  */  
    13.   
    14. public class SenvProtocolClient {  
    15.       
    16.     public static void main(String[] args) {  
    17.         BufferedReader reader = null;  
    18.         try {  
    19.             // 配置协议处理器查找规则一  
    20.             if (StringUtils.isNullOrBlank(System.getProperty("java.protocol.handler.pkgs"))) {  
    21.                 // 设置各个协议包所在的父包路径  
    22.                 System.setProperty("java.protocol.handler.pkgs""com.daniele.appdemo.net.protocol.custom");  
    23.             }  
    24.             /* 
    25.                          * 配置协议处理器查找规则二 
    26.                          * 这种方式在整个应用范围之内只能被执行一次。 
    27.                          * 如果多于一次则会出现"java.lang.Error: factory already defined"这样的错误。但不会受规则一的限制. 
    28.                          */   
    29. //          URL.setURLStreamHandlerFactory(new CustomProtocolFactory());  
    30.               
    31.             URL url = new URL("senv://192.168.1.101:9527/");  
    32.             reader = new BufferedReader(new InputStreamReader(url.openConnection().getInputStream()));  
    33.             String result = "";  
    34.             while ((result = reader.readLine()) != null)  
    35.                 System.out.println(result);  
    36.         } catch (IOException e) {  
    37.             e.printStackTrace();  
    38.         } finally {  
    39.             try {  
    40.                 if (reader != null)  
    41.                     reader.close();  
    42.             } catch (IOException e) {  
    43.                 e.printStackTrace();  
    44.             }  
    45.         }  
    46.     }  
    47.   
    48. }  

     

    Java代码   收藏代码
    1. package com.daniele.appdemo.util;  
    2.   
    3. import java.util.Enumeration;  
    4. import java.util.Properties;  
    5.   
    6. /** 
    7.  * <p>运行环境工具类</p>  
    8.  * @author  <a href="mailto:code727@gmail.com">Daniele</a> 
    9.  * @version 1.0.0, 2013-5-9 
    10.  * @see      
    11.  * @since   AppDemo1.0.0 
    12.  */  
    13.   
    14. public class SystemUtils {  
    15.       
    16.     private static Properties properties = null;  
    17.       
    18.     static {  
    19.         properties = System.getProperties();  
    20.     }  
    21.       
    22.     /** 
    23.      * <p>返回格式化后的所有系统属性信息</p>  
    24.      * @author <a href="mailto:code727@gmail.com">Daniele</a>  
    25.      * @return  
    26.      * @since AppDemo1.0.0 
    27.      */  
    28.     @SuppressWarnings("unchecked")  
    29.     public static String formatSystemProperties() {  
    30.         StringBuffer formatResult = new StringBuffer();  
    31.         Enumeration<String> names = (Enumeration<String>) properties.propertyNames();  
    32.         while (names.hasMoreElements()) {  
    33.             String name = names.nextElement();  
    34.             formatResult.append(name).append("=")  
    35.                 .append(properties.getProperty(name)).append("\n");  
    36.         }  
    37.         int length = 0;  
    38.         return (length = formatResult.length()) > 0 ?   
    39.                 formatResult.substring(0, length - 1) : "";  
    40.     }  
    41.       
    42.     /** 
    43.      * <p>返回格式化后的所有指定的系统属性信息</p>  
    44.      * @author <a href="mailto:code727@gmail.com">Daniele</a>  
    45.      * @param propertyKeys 
    46.      * @return  
    47.      * @since AppDemo1.0.0 
    48.      */  
    49.     public static String formatSystemProperties(String[] propertyKeys) {  
    50.         StringBuffer formatResult = new StringBuffer();  
    51.         if (propertyKeys != null && propertyKeys.length > 0) {  
    52.             for (String key : propertyKeys)   
    53.                 formatResult.append(key).append("=")  
    54.                     .append(properties.getProperty(key)).append("\n");  
    55.         }  
    56.         int length = 0;  
    57.         return (length = formatResult.length()) > 0 ?   
    58.                 formatResult.substring(0, length - 1) : "";  
    59.     }  
    60.   
    61. }  
    Java代码   收藏代码
    1. package com.daniele.appdemo.util;  
    2.   
    3. public class StringUtils {  
    4.       
    5.     public static boolean isNullOrBlank(String str) {  
    6.         return str == null || str.length() == 0;  
    7.     }  
    8.       
    9.     public static boolean isNotNullOrBlank(String str) {  
    10.         return !isNullOrBlank(str);  
    11.     }  
    12.   
    13. }  

    运行时,依次启动SenvProtocolServer和SenvProtocolClient类即可。

    展开全文
  • PAGE / NUMPAGES * 定义自动升级客户端与服务端的通讯协议 package com.rochoc.autoupdate; public class AUPD { /* *//* * 无意义操作 */ public static final String NONE = "NONE ; /* *//* * 发送客户端版本信息...
  • java自定义协议

    2020-02-26 18:02:03
    在设计 C-S 结构的应用时,我们一般需要使用一种客户端C与服务端S的通信协议。现在常用的协议有HTTP,XML-RPC,SOAP等,当然,现在XML在很多场景下都用JSON替代了,这个不赘述,有些时候,我们需要设计一些自有协议,...

        在设计 C-S 结构的应用时,我们一般需要使用一种客户端C与服务端S的通信协议。现在常用的协议有HTTP,XML-RPC,SOAP等,当然,现在XML在很多场景下都用JSON替代了,这个不赘述,有些时候,我们需要设计一些自有协议,这里咱们就说一下设计自有协议时需要注意的几个点:

    1、C-S交互

        客户端与服务器之间通过信息(数据)交换来完成具体操作,举例来说,当客户端向服务器发送一个请求来执行某个操作,服务器在执行完成后向客户端回复一个处理结果,这是客户端与服务端的一次完整交互。

        当一台ComputerA向另一台ComputerB 通过网络发送信息,从A发出信息到B接收到信息需要花费一定的时间,这个网络传输时间称为时延。

    在定义协议时,完成某操作需要的交互次数越多,协议的效率就越低,如果时延很高,那效率低下就会更明显。HTTP协议完成一次操作时,只需要一次请求与一次响应,也就是只需要一次交互。而SMTP在完成一次邮件发送前会经过多次交互。

        我们只有在客户端需要向服务端发送大量的数据时,才考虑将协议操作拆分为多次交互。我们一般有两种选择:

    1、使用一次单独的交互来发送头信息

    2、将信息拆分为多个信息块

        如果服务器可以对头信息做一些初始化的预先校验,那在第一次交互时发送头信息是比较好的选择,如果头信息无效,即时数据被正常接收也是无效的。

        在传输大体量的数据时,如果中途网络连接断开,我们需要将所有数据从新发送,但如果我们将数据拆分为小的数据块,那么当网络连接失败时,我们只需要从新发送失败的数据块,然后就可以go on,已经发送成功的数据块就不需要重新发送。

    2、多请求与多响应的划分

        当通过相同连接发送多个请求时,我们需要划分出不同的请求,同时,客户端也需要划分开不同的响应。针对此问题,我们一般有两种选择:

    1、在请求信息的开始部分标明信息的字节长度

    2、在请求内容后添加结束标识

        HTTP使用的是第一种,在请求头中通过“Content-Length”标识请求数据的字节长度。它标识了请求头后有多少字节属于这个请求。这种方式相较第二种优点是减少结束符处理开销的同时,也不需要对原始信息中可能存在的与结束符相同的字符进行转义操作。缺点就是,我们在数据发送之前必须知道数据的长度,如果数据是动态生成的,我们需要缓存整个请求数据,在获取数据长度之后才能执行发送操作。

        使用结束符是,我们不必要知道我们发送的数据有多长,我们只需要在待发送数据的尾部加上结束符,但是我们需要对数据体中可能混淆结束符的字符进行转义。

    3、防火墙穿透

        大多数防火墙会阻止HTTP以外的协议通信,所以我们构建网络通信协议时基于HTTP组建新协议是一个比较好的办法。我们需要在HTTP请求及响应信息的内部来构建我们的协议。我们构建的协议形式可以是文本,可以是xml,也可以是二进制。但因为HTTP的特殊性,我们在组建某些协议时会有一定的额外开销。

    展开全文
  • 下面小编就为大家分享一篇Java自定义协议报文封装 添加Crc32校验的实例,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
  • 哈啰出行(哈啰单车),诚招高级/资深前端开发工程师、Java高级/资深开发工程师,地点上海。 有想法的请发简历到我个人邮箱:huangcheng@hellobike.com   现在即时通信在移动领域应用的非常广了,通过TCP长链接...

    哈啰出行(哈啰单车),诚招高级/资深前端开发工程师、Java高级/资深开发工程师,地点上海。
    有想法的请发简历到我个人邮箱:huangcheng@hellobike.com

     

    现在即时通信在移动领域应用的非常广了,通过TCP长链接实现数据的即时更新,通知,在日常开发中常会用的。今天我就把在在平常开发中的基本实现思路跟大家一起交流下。转载请注明出处:http://blog.csdn.net/mr_oorange/article/details/52353626
    源码地址:https://github.com/Ooorange/java-TCP-long-connection

    一:准备阶段
    android客户端用于消息的接受和发送,一个java的本地的服务端实现消息的转发推送。
    二:实现思路
    如何做到消息的即时通信,1是通过http轮训;2是通过http的长链接服务(跟tcp有点像,但是还是会有断开的可能性,很大),3是基于tcp的长连接。先比较下3中实现方案的优缺点:第一种,缺点:耗费流量,损耗性能,tcp会不断的开启停止,优点:实现简单; 第二种,缺点:需要服务端配合,而且http断开的偶发性很高,不易控制,优点:可实现可接受的即时通信;第三种:通过心跳维持的连接不会经常断开,即可实现即时的通信,而且可自定义头,减小流量的耗用。缺点需要后台配合,实现较复杂(理解了都还好其实)。我选择的是第三种方案。即基于java的tcp长链接。


    客户端和服务端的约定条件:

    服务端通过设置timeout终止消息的接收,以及客户端消息的发送。具体值可以自己设置,其实就是感知客户端心跳。服务端的socket保持打开,可接受多个客户端的连接;每个新加入的客户端标识唯一的ID,用于定向发送;离线消息的存储,发送;自定义协议的封装以及解包;


    android 客户端实现:通过一个线程池维护消息任务的发送以及终止。任务包括:通过socket获得输入输出流,设置timeout时间,当时间到达后输入输出都不可用,起3个线程保持消息的接收,发送以及分发,另外还有一个心跳线程,用于维持跟服务端的连接(要不然服务端就认为你timeout GG了,就给你断开);还有一个重要的地方就是,实现自定义协议将不同的业务线分解,可以用于推送,即时聊天,等等。。。客户端还是现实了离线的消息存储(ORM模式的greenDao)

    服务端实现:同样的通过serverSocket获得输入输出流,并保持连接open状态,为每个新加入的客户端标时唯一的ID,用于定向发送消息,当客户端主动或者是timeout之后关闭流管道, 

    三:上代码
    客户端:

     

    import com.orange.blog.net.protocol.ChatMsgProcotol;
    
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    
    /**
     * 长链接:问题描述
     * Q1:中服务端与客户端socket的输入输出流都不会关闭,所以需要考虑资源得释放长链接时机
     *
     * Q2何时关闭链接,客户端和服务器端,不能关闭任一socket的输入流或者输出流,否则socket通信会关闭;
     *  由于都不关闭连接,而read方法又是阻塞的,会一直读取数据,不知道何时读取结束(何时知道本次数据读取结束);
     * *
     *
     * Q3如果有多个业务需要使用链接,如何兼容其他的业务,使得解析流程一致
     *
     *
     * Q1解决方案:服务器端都是会设定超时时间的,也就是timeout,如果超过timeout服务器没有接收到任何数据,
     * 那么该服务器就会关闭该连接,从而使得服务器资源得到有效地使用。
     *
     * Q2解决方案:约定通信协议,如特定字符,或者使用包头+包体的方式,传递数据,包头固定长度,
     * 里面保存包体长度等信息,这样服务端就知道读取到何时结束了.(本文使用此种方式)以下是代码:
     *
     * Q3解决方案:不同业务定义不同的协议,比如心跳协议,业务协议;  另外一种方案就是实用json数据格式进行传输
     * @Link(http://blog.csdn.net/ljl157011/article/details/19291611)
     * 短链接:建立完一次通信后将被释放,下次发送得重新链接建立,浪费资源
    
     * Created by orange on 16/6/8.
     */
    public class TCPLongConnectClient {
        //通过缓存线程池实现消息的加入以及断开
        ExecutorService executorService= Executors.newCachedThreadPool();
        RequestTask requestTask;
        public TCPLongConnectClient(TCPRequestCallBack tcpRequestCallBack){
            requestTask=new RequestTask(tcpRequestCallBack);
            executorService.execute(requestTask);
        }
    
        public void addNewRequest(ChatMsgProcotol data){
            if (requestTask!=null)
            requestTask.addRequest(data);
        }
    
        public void closeConnect() {
            requestTask.stop();
        }
    }

    客户端核心任务代码:

     

     

    /**
     * 实现5秒的定时发送一个心跳
     * Created by orange on 16/6/8.
     */
    public class HeartBeatTask implements Runnable {
        private static final int REPEATTIME = 4000;
        private volatile boolean isKeepAlive = true;
        private OutputStream outputStream;
        private String uuid;
        public HeartBeatTask(OutputStream outputStream,String uuid) {
            this.outputStream = outputStream;
            this.uuid=uuid;
        }
    
    
        @Override
        public void run() {
            try {
                while (isKeepAlive) {
                    SocketUtil.writeContent2Stream(new HeartBeatProtocol(), outputStream);
                    try {
                        Thread.sleep(REPEATTIME);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                if (outputStream != null) {
                    SocketUtil.closeStream(outputStream);
                }
            } catch (Exception e) {
                Log.d("exception", " : Time is out, request" + " has been closed.");
                e.printStackTrace();
            } finally {
                if (outputStream != null) {
                    SocketUtil.closeStream(outputStream);
                }
            }
        }
    
        public void setKeepAlive(boolean isKeepAlive) {
            this.isKeepAlive = isKeepAlive;
        }
    }

    消息的发送接收

     

     

    @Override
        public void run() {
            uuid = ProjectApplication.getUUID();
            try {
                failedMessage(0, "服务器连接中");
                try {
                    socket = SocketFactory.getDefault().createSocket(ADDRESS, PORT);
                }catch (ConnectException e){
                    failedMessage(-1, "服务区器连接异常,请检查网络");
                    return;
                }
                sendData.add(new RegisterProcotol());
                sendData.add(new UserFriendReuqestProcotol());
    
                sendTask=new SendTask();
    
                sendTask.outputStreamSend = socket.getOutputStream();
                sendTask.start();
    
                reciverTask = new ReciverTask();
                reciverTask.inputStreamReciver = socket.getInputStream();
                reciverTask.start();
    
                if (isLongConnection) {
                    heartBeatTask = new HeartBeatTask(sendTask.outputStreamSend, uuid);
                    executorService = Executors.newCachedThreadPool();
                    executorService.execute(heartBeatTask);
                }
            } catch (IOException e) {
                failedMessage(-1, "IOException");
                e.printStackTrace();
            }
        }


    通信协议的的自定义以及解包

     

     

    public abstract class BasicProtocol {
    
        public static final int VERSION_LEN=2;//协议的版本
        public static final int COMMEND_LEN=4;//协议的类型: 0000心跳,0001普通文字聊天,0002服务端返回协议,0003好友列表请求,0004用户注册连接协议
    
        public static String VERSION="00";    //目前版本号死的
    
        public static String paraseCommend(byte[] data){
            return new String(data,VERSION_LEN,COMMEND_LEN);
        }
    
        public abstract String getCommend();
    
        public byte[] getContentData(){
            ByteArrayOutputStream baos=new ByteArrayOutputStream(VERSION_LEN+COMMEND_LEN);
            baos.write(VERSION.getBytes(),0,VERSION_LEN);
            baos.write(getCommend().getBytes(),0,COMMEND_LEN);
            return baos.toByteArray();
        }
    
        public int parseBinary(byte[] data) throws ProtocolException {
            String version=new String(data,0,VERSION_LEN);
            VERSION=version;
            if (!version.equals("00")){
                throw new ProtocolException("income version is error"+version);
            }
            return VERSION_LEN+COMMEND_LEN;
        }
    }

     

     

     

    代码有点乱,服务端代码就不贴来,需要的话可以大家请点这里是源码,https://github.com/Ooorange/java-TCP-long-connection  欢迎fork,star

    转载请注明出处

    哈啰出行(哈啰单车),诚招高级/资深前端开发工程师、Java高级/资深开发工程师,地点上海。
    有想法的请发简历到我个人邮箱:huangcheng@hellobike.com

     

    展开全文
  • Java实现自定义HTTP协议

    千次阅读 2019-06-12 17:08:37
    Java实现HTTP协议 代码: package com.we; import java.io.IOException; import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.nio.channels.SelectionKey; import java.nio.channels....

    Java实现自定义HTTP协议

    代码:

    package com.we;
    
    import java.io.IOException;
    import java.net.InetSocketAddress;
    import java.nio.ByteBuffer;
    import java.nio.channels.SelectionKey;
    import java.nio.channels.Selector;
    import java.nio.channels.ServerSocketChannel;
    import java.nio.channels.SocketChannel;
    import java.nio.charset.Charset;
    import java.util.Iterator;
    
    public class HttpServer {
    	public static void main(String[] args) throws IOException {
    		
    		 // 创建 ServerSocketChanne1,监听8080端口
    		 ServerSocketChannel ssc = ServerSocketChannel.open(); 
    		 ssc.socket().bind(new InetSocketAddress(8080));
    		 // 设置为非阻塞模式
    		 ssc.configureBlocking(false);
    		 // 为ssc注册选择器
    		 Selector selector = Selector.open();
    		 ssc.register(selector, SelectionKey.OP_ACCEPT);
    		 // 创建处理器
    		 while (true) {
    			// 等待请求,每次等待阻塞3s,超过3s后线程继续向下运行,如果传入0或者不传参数将直阻塞
    			if (selector.select(3000) == 0) {
    				continue;
    			}
    		    // 获取待处理的 SelectionKey
    			Iterator<SelectionKey> keyIter = selector.selectedKeys().iterator();
    			
    			while (keyIter.hasNext()) {
    				SelectionKey key  = keyIter.next();
    				// 启动新线程处理SelectionKey
    				new Thread(new HttpHandler(key)).run();
    				// 处理完后, 从待处理的SelectionKey迭代器中移除当前所使用的key
    				keyIter.remove();
    			}
    		 } 
    	}
    	
    	private static class HttpHandler implements Runnable {
    		private int bufferSize = 1024;
    		private String localCharest = "UTF-8";
    		private SelectionKey key;
    		
    		public HttpHandler(SelectionKey key) {
    			this.key = key;
    		}
    		
    		public void handleAccept() throws IOException {
    			SocketChannel clientChannel = ((ServerSocketChannel)key.channel()).accept();
    			clientChannel.configureBlocking(false);
    			clientChannel.register(key.selector(), SelectionKey.OP_READ, ByteBuffer.allocate(bufferSize));
    		}
    		
    		public void handleRead() throws IOException {
    			// 获取channel
    			SocketChannel sc = (SocketChannel)key.channel();
    			// 获取buffer并重置
    			ByteBuffer buffer = (ByteBuffer)key.attachment();
    			buffer.clear();
    			// 没有读到内容则关闭
    			if (sc.read(buffer) == -1) {
    				sc.close();
    			} else {
    				// 接收请求数据
    				buffer.flip();
    				String  receivedString = Charset.forName(localCharest).newDecoder().decode(buffer).toString();
    			    // 控制台打印请求报文头
    				String[] requestMessage = receivedString.split("\r\n");
    				for (String s : requestMessage) {
    					System.out.println(s);
    					// 遇到空行说明报文头已经打完
    					if (s.isEmpty()) {
    						break;
    					}	
    				}
    				
    				// 控制台打印首行信息
    				String[] firstLine = requestMessage[0].split(" ");
    				System.out.println();
    				System.out.println("Method:\t" + firstLine[0]);
    				System.out.println("url:\t" + firstLine[1]);
    				System.out.println("HTTP Version:\t" + firstLine[2]);
    				System.out.println();
    				
    				// 返回客户端
    				StringBuilder sendString = new StringBuilder();
    				sendString.append("HTTP/1.1 200 OK\r\n");// 响应报文首行,200表示处理成功
    				sendString.append("Content-Type:text/html;charset=" + localCharest + "\r\n");
    				sendString.append("\r\n");// 报文头结束后加一个空行
    				
    				sendString.append("<html><head><title>显示报文</title></head></body>");
    				sendString.append("接收到的请求报文是:<br/>" );
    				
    				for (String s : requestMessage) {
    					sendString.append(s + "<br/>");
    				}
    				sendString.append("</body></html>");
    				buffer = ByteBuffer.wrap(sendString.toString().getBytes(localCharest));
    				sc.write(buffer);
    				sc.close();
    			}		
    		}
    
    		@Override
    		public void run() {
    			try {
    				// 接收到连接请求时
    				if (key.isAcceptable()) {
    					handleAccept();
    				}
    				// 读数据
    				if (key.isReadable()) {
    					handleRead();
    				}
    			} catch (IOException e) {
    				e.printStackTrace();
    			}			
    		}
    	}
    }
    
    

    我们首先启动程序,然后在浏览器中输入http://localhost:8080/发起请求,这时控制台就会打印如下信息:

    控制台输出:

    GET / HTTP/1.1
    Host: localhost:8080
    User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:67.0) Gecko/20100101 Firefox/67.0
    Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
    Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
    Accept-Encoding: gzip, deflate
    Connection: keep-alive
    Cookie: username-localhost-8888="2|1:0|10:1559731820|23:username-localhost-8888|44:ZmI3YWUxN2Y5NGFhNDdjMjg5YzRkNjk5NTMyZDViZjQ=|db2ab862ddca20277b71e254ce930dd60c5ff36dfac213b2f9d1c4916ba39677"; _xsrf=2|9e047beb|520cad25f907324f06794aa09fdac60f|1559182548; username-localhost-8889="2|1:0|10:1559655488|23:username-localhost-8889|44:OTI0NWJiMzMxYjdlNDllODg1Mjg0NjllZTdjMDc2Yzk=|64d0535d8a2ab5016e5c24ac34ca4935e921945c4aad4a39b3cdcb33c6dacd4e"
    Upgrade-Insecure-Requests: 1
    
    Method:	GET
    url:	/
    HTTP Version:	HTTP/1.1
    

    浏览器的显示:
    在这里插入图片描述

    展开全文
  • ![图片说明](https://img-ask.csdn.net/upload/202003/24/1585035030_975869.png) 以上协议头,如何用java封装?
  • URI与URL的区别一.先来序言一段二.协议的自定义的理解三.自定义协议与URL的关系四.URL自定义私有协议实战五.后话,自定义mineType解析器
  • JAVA自定义网络通信协议

    千次阅读 2013-05-11 18:51:23
     JAVA默认提供了对file,ftp,gopher,http,https,jar,mailto,netdoc协议的支持。当我们要利用这些协议来创建应用时,主要会涉及到如下几个类:  1.java.net.URL:URL资源  2.java.net.URLConnection:各种URL资源...
  • 怎样从网页上直接启动客户端应用程序?...面对这种情况也许你会想到ActiveX,但当你发现对方的客户们用IE浏览器的很少,而大多数用浏览器并不支持ActiveX,像这种... 这时再介绍一种方法给你,那就是JAVA自定义协议,其实
  • 除此之外,在开发过程中可以进行header的自定义。 现在进入正题,解析http报文,java代码如下 package HTTPParser ; import HTTPParser . httpMsgParser . HttpMsgParser ; import java . util . ...
  • 首先解释下为什么Socket通信需要一定的协议才能理解消息的内容 1. 安全性, 协议中有判断内容安全的字段(比如报文的长度), 这样可以进行验证,如果被网络连接和篡改,这样的消息就是不安全的,不予处理 2. Socket通信, ...
  • 下面给出一个实现了自定义构建和解析协议消息的Demo(书上例子)。  该例子是一个简单的投票协议。这里,一个客户端向服务器发送一个请求消息,消息中包含了一个候选人的ID,范围在0~1000。程序支持两种请求:一...
  • Java - Apache Mina 自定义协议通信

    千次阅读 2014-06-30 23:13:01
    一、定义协议实体 ... * 自定义协议消息体 */ public class MyMsg { /** * 消息长度 */ private Integer lenth; /** * 发送人 */ private Long sender; /** * 接收人 */ p
  • 基于Java Socket的自定义协议

    千次阅读 2017-04-01 15:07:05
    上一篇文章中,我们对socket编程和自定义协议做了一个简单的了解,本文将在此基础上加以深入,来实现Android和服务器之间的长连接,现定义协议如下: 数据类协议(Data)  长度(length,32bit)版本号(version...
  • 【Java编程系列】Java自定义标签-Tag

    万次阅读 2017-11-13 15:54:48
    1.前言 前段时间,临时调到其他项目组帮忙,做一个页面权限控制模块。当时因为业务要求,一个用户可能会对应多个角色,所以我一...于是之后他自行提出了一种自定义标签的做法,来进行权限控制。 2.自定义标签Tag的使用
  • netty自定义数据包协议示例 ,自定义解码器译码器 解决拆包粘包问题
  • 自定义通信协议

    千次阅读 2018-01-08 09:48:59
    1.自定义数据通信协议  这里所说的数据协议是建立在物理层之上的通信数据包格式。所谓通信的物理层就是指我们通常所用到的RS232、RS485、红外、光纤、无线等等通信方式。在这个层面上,底层软件提供两个基本的操
  • TCP自定义通信协议

    千次阅读 2018-04-12 07:29:19
    Tcp 服务端自定义协议,客户端解析协议的处理 我们想想,为什么要定义协议: 回答:因为我们在传输的过程中会出现分包(没有接受到一个完整的包)与黏包(收到比一包多的数据,除去完整的一包,首尾有数据)的问题...
  • 原型客户端 Clientpackage me...import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.File; import java.io

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 139,059
精华内容 55,623
关键字:

java自定义消息协议

java 订阅