zoukankan      html  css  js  c++  java
  • RPC之——HTTP协议栈

    转载请注明出处:http://blog.csdn.net/l1028386804/article/details/52531185

    今天,给大家带来一篇稍有深度的文章——《RPC之——HTTP协议栈》,好了,我们进入正题吧。

     HTTP协议属于应用层协议,它构建于TCP和IP协议之上,处于TCP/IP协议架构层的顶端,所以,它不用处理下层协议间诸如丢包补发、握手及数据的分段及重新组装等繁琐的细节,使开发人员可以专注于应用业务。

    协议是通信的规范,为了更好的理解HTTP协议,我们可以基于Java的Socket API接口,通过设计一个简单的应用层通信协议,来简单分析下协议实现的过程和细节。

    在我们今天的示例程序中,客户端会向服务端发送一条命令,服务端在接收到命令后,会判断命令是否是“HELLO”,如果是“HELLO”, 则服务端返回给客户端的响应为“hello”,否则,服务端返回给客户端的响应为“bye bye”。

    我们接下来用Java实现这个简单的应用层通信协议:

     1、协议请求的定义

    协议的请求主要包括:编码、命令和命令长度三个字段。

    [java] view plain copy
     
     在CODE上查看代码片派生到我的代码片
    1. package com.lyz.params;  
    2.   
    3. /** 
    4.  * 协议请求的定义 
    5.  * @author liuyazhuang 
    6.  * 
    7.  */  
    8. public class Request {  
    9.     /** 
    10.      * 协议编码 
    11.      */  
    12.     private byte encode;  
    13.       
    14.     /** 
    15.      * 命令 
    16.      */  
    17.     private String command;  
    18.       
    19.     /** 
    20.      * 命令长度 
    21.      */  
    22.     private int commandLength;  
    23.   
    24.     public Request() {  
    25.         super();  
    26.     }  
    27.   
    28.     public Request(byte encode, String command, int commandLength) {  
    29.         super();  
    30.         this.encode = encode;  
    31.         this.command = command;  
    32.         this.commandLength = commandLength;  
    33.     }  
    34.   
    35.     public byte getEncode() {  
    36.         return encode;  
    37.     }  
    38.   
    39.     public void setEncode(byte encode) {  
    40.         this.encode = encode;  
    41.     }  
    42.   
    43.     public String getCommand() {  
    44.         return command;  
    45.     }  
    46.   
    47.     public void setCommand(String command) {  
    48.         this.command = command;  
    49.     }  
    50.   
    51.     public int getCommandLength() {  
    52.         return commandLength;  
    53.     }  
    54.   
    55.     public void setCommandLength(int commandLength) {  
    56.         this.commandLength = commandLength;  
    57.     }  
    58.   
    59.     @Override  
    60.     public String toString() {  
    61.         return "Request [encode=" + encode + ", command=" + command  
    62.                 + ", commandLength=" + commandLength + "]";  
    63.     }  
    64.       
    65. }  

    2、响应协议的定义

    协议的响应主要包括:编码、响应内容和响应长度三个字段。

    [java] view plain copy
     
     在CODE上查看代码片派生到我的代码片
    1. package com.lyz.params;  
    2.   
    3. /** 
    4.  * 协议响应的定义 
    5.  * @author liuyazhuang 
    6.  * 
    7.  */  
    8. public class Response {  
    9.     /** 
    10.      * 编码 
    11.      */  
    12.     private byte encode;  
    13.       
    14.     /** 
    15.      * 响应内容 
    16.      */  
    17.     private String response;  
    18.       
    19.     /** 
    20.      * 响应长度 
    21.      */  
    22.     private int responseLength;  
    23.   
    24.     public Response() {  
    25.         super();  
    26.     }  
    27.   
    28.     public Response(byte encode, String response, int responseLength) {  
    29.         super();  
    30.         this.encode = encode;  
    31.         this.response = response;  
    32.         this.responseLength = responseLength;  
    33.     }  
    34.   
    35.     public byte getEncode() {  
    36.         return encode;  
    37.     }  
    38.   
    39.     public void setEncode(byte encode) {  
    40.         this.encode = encode;  
    41.     }  
    42.   
    43.     public String getResponse() {  
    44.         return response;  
    45.     }  
    46.   
    47.     public void setResponse(String response) {  
    48.         this.response = response;  
    49.     }  
    50.   
    51.     public int getResponseLength() {  
    52.         return responseLength;  
    53.     }  
    54.   
    55.     public void setResponseLength(int responseLength) {  
    56.         this.responseLength = responseLength;  
    57.     }  
    58.   
    59.     @Override  
    60.     public String toString() {  
    61.         return "Response [encode=" + encode + ", response=" + response  
    62.                 + ", responseLength=" + responseLength + "]";  
    63.     }  
    64.       
    65. }  

    3、编码常量定义

    编码常量的定义主要包括UTF-8和GBK两种编码。

    [java] view plain copy
     
     在CODE上查看代码片派生到我的代码片
    1. package com.lyz.constant;  
    2.   
    3. /** 
    4.  * 常量类 
    5.  * @author liuyazhuang 
    6.  * 
    7.  */  
    8. public final class Encode {  
    9.     //UTF-8编码  
    10.     public static final byte UTF8 = 1;  
    11.     //GBK编码  
    12.     public static final byte GBK = 2;  
    13. }  

    4、客户端的实现

    客户端先构造一个request请求,通过Socket接口将其发送到远端,并接收远端的响应信息,并构造成一个Response对象。

    [java] view plain copy
     
     在CODE上查看代码片派生到我的代码片
    1. package com.lyz.protocol.client;  
    2.   
    3. import java.io.IOException;  
    4. import java.io.InputStream;  
    5. import java.io.OutputStream;  
    6. import java.net.Socket;  
    7.   
    8. import com.lyz.constant.Encode;  
    9. import com.lyz.params.Request;  
    10. import com.lyz.params.Response;  
    11. import com.lyz.utils.ProtocolUtils;  
    12.   
    13. /** 
    14.  * 客户端代码 
    15.  * @author liuyazhuang 
    16.  * 
    17.  */  
    18. public final class Client {  
    19.     public static void main(String[] args) throws IOException{  
    20.         //请求  
    21.         Request request = new Request();  
    22.         request.setCommand("HELLO");  
    23.         request.setCommandLength(request.getCommand().length());  
    24.         request.setEncode(Encode.UTF8);  
    25.           
    26.         Socket client = new Socket("127.0.0.1", 4567);  
    27.         OutputStream out = client.getOutputStream();  
    28.           
    29.         //发送请求  
    30.         ProtocolUtils.writeRequest(out, request);  
    31.           
    32.         //读取响应数据  
    33.         InputStream in = client.getInputStream();  
    34.         Response response = ProtocolUtils.readResponse(in);  
    35.         System.out.println("获取的响应结果信息为: " + response.toString());  
    36.     }  
    37. }  

    5、服务端的实现

    服务端接收客户端的请求,根据接收命令的不同,响应不同的消息信息,如果是“HELLO”命令,则响应“hello”信息,否则响应“bye bye”信息。

    [java] view plain copy
     
     在CODE上查看代码片派生到我的代码片
    1. package com.lyz.protocol.server;  
    2.   
    3. import java.io.IOException;  
    4. import java.io.InputStream;  
    5. import java.io.OutputStream;  
    6. import java.net.ServerSocket;  
    7. import java.net.Socket;  
    8.   
    9. import com.lyz.constant.Encode;  
    10. import com.lyz.params.Request;  
    11. import com.lyz.params.Response;  
    12. import com.lyz.utils.ProtocolUtils;  
    13.   
    14. /** 
    15.  * Server端代码 
    16.  * @author liuyazhuang 
    17.  * 
    18.  */  
    19. public final class Server {  
    20.     public static void main(String[] args) throws IOException{  
    21.         ServerSocket server = new ServerSocket(4567);  
    22.         while (true) {  
    23.             Socket client = server.accept();  
    24.             //读取请求数据  
    25.             InputStream input = client.getInputStream();  
    26.             Request request = ProtocolUtils.readRequest(input);  
    27.             System.out.println("收到的请求参数为: " + request.toString());  
    28.             OutputStream out = client.getOutputStream();  
    29.             //组装响应数据  
    30.             Response response = new Response();  
    31.             response.setEncode(Encode.UTF8);  
    32.             if("HELLO".equals(request.getCommand())){  
    33.                 response.setResponse("hello");  
    34.             }else{  
    35.                 response.setResponse("bye bye");  
    36.             }  
    37.             response.setResponseLength(response.getResponse().length());  
    38.             ProtocolUtils.writeResponse(out, response);  
    39.         }  
    40.     }  
    41. }  

    6、ProtocolUtils工具类的实现

    ProtocolUtils的readRequest方法将从传递进来的输入流中读取请求的encode、command和commandLength三个参数,进行相应的编码转化,构造成Request对象返回。而writeResponse方法则是将response对象的字段根据对应的编码写入到响应的输出流中。

    有一个细节需要重点注意:OutputStream中直接写入一个int类型,会截取其低8位,丢弃其高24位,所以,在传递和接收数据时,需要进行相应的转化操作。

    [java] view plain copy
     
     在CODE上查看代码片派生到我的代码片
    1. package com.lyz.utils;  
    2.   
    3. import java.io.IOException;  
    4. import java.io.InputStream;  
    5. import java.io.OutputStream;  
    6.   
    7. import com.lyz.constant.Encode;  
    8. import com.lyz.params.Request;  
    9. import com.lyz.params.Response;  
    10.   
    11. /** 
    12.  * 协议工具类 
    13.  * @author liuyazhuang 
    14.  * 
    15.  */  
    16. public final class ProtocolUtils {  
    17.     /** 
    18.      * 从输入流中反序列化Request对象 
    19.      * @param input 
    20.      * @return 
    21.      * @throws IOException 
    22.      */  
    23.     public static Request readRequest(InputStream input) throws IOException{  
    24.         //读取编码  
    25.         byte[] encodeByte = new byte[1];  
    26.         input.read(encodeByte);  
    27.         byte encode = encodeByte[0];  
    28.           
    29.         //读取命令长度  
    30.         byte[] commandLengthBytes = new byte[4];  
    31.         input.read(commandLengthBytes);  
    32.         int commandLength = ByteUtils.byte2Int(commandLengthBytes);  
    33.           
    34.         //读取命令  
    35.         byte[] commandBytes = new byte[commandLength];  
    36.         input.read(commandBytes);  
    37.         String command = "";  
    38.         if(Encode.UTF8 == encode){  
    39.             command = new String(commandBytes, "UTF-8");  
    40.         }else if(Encode.GBK == encode){  
    41.             command = new String(commandBytes, "GBK");  
    42.         }  
    43.         //组装请求返回  
    44.         Request request = new Request(encode, command, commandLength);  
    45.         return request;  
    46.     }  
    47.     /** 
    48.      * 从输入流中反序列化Response对象 
    49.      * @param input 
    50.      * @return 
    51.      * @throws IOException 
    52.      */  
    53.     public static Response readResponse(InputStream input) throws IOException{  
    54.         //读取编码  
    55.         byte[] encodeByte = new byte[1];  
    56.         input.read(encodeByte);  
    57.         byte encode = encodeByte[0];  
    58.           
    59.         //读取响应长度  
    60.         byte[] responseLengthBytes = new byte[4];  
    61.         input.read(responseLengthBytes);  
    62.         int responseLength = ByteUtils.byte2Int(responseLengthBytes);  
    63.           
    64.         //读取命令  
    65.         byte[] responseBytes = new byte[responseLength];  
    66.         input.read(responseBytes);  
    67.         String response = "";  
    68.         if(Encode.UTF8 == encode){  
    69.             response = new String(responseBytes, "UTF-8");  
    70.         }else if(Encode.GBK == encode){  
    71.             response = new String(responseBytes, "GBK");  
    72.         }  
    73.         //组装请求返回  
    74.         Response resp = new Response(encode, response, responseLength);  
    75.         return resp;  
    76.     }  
    77.       
    78.     /** 
    79.      * 序列化请求信息 
    80.      * @param output 
    81.      * @param response 
    82.      */  
    83.     public static void writeRequest(OutputStream output, Request request) throws IOException{  
    84.         //将response响应返回给客户端  
    85.         output.write(request.getEncode());  
    86.         //output.write(response.getResponseLength());直接write一个int类型会截取低8位传输丢弃高24位  
    87.         output.write(ByteUtils.int2ByteArray(request.getCommandLength()));  
    88.         if(Encode.UTF8 == request.getEncode()){  
    89.             output.write(request.getCommand().getBytes("UTF-8"));  
    90.         }else if(Encode.GBK == request.getEncode()){  
    91.             output.write(request.getCommand().getBytes("GBK"));  
    92.         }  
    93.         output.flush();  
    94.     }  
    95.     /** 
    96.      * 序列化响应信息 
    97.      * @param output 
    98.      * @param response 
    99.      */  
    100.     public static void writeResponse(OutputStream output, Response response) throws IOException{  
    101.         //将response响应返回给客户端  
    102.         output.write(response.getEncode());  
    103.         //output.write(response.getResponseLength());直接write一个int类型会截取低8位传输丢弃高24位  
    104.         output.write(ByteUtils.int2ByteArray(response.getResponseLength()));  
    105.         if(Encode.UTF8 == response.getEncode()){  
    106.             output.write(response.getResponse().getBytes("UTF-8"));  
    107.         }else if(Encode.GBK == response.getEncode()){  
    108.             output.write(response.getResponse().getBytes("GBK"));  
    109.         }  
    110.         output.flush();  
    111.     }  
    112. }  

    7、ByteUtils类的实现

    [java] view plain copy
     
     在CODE上查看代码片派生到我的代码片
    1. package com.lyz.utils;  
    2.   
    3. /** 
    4.  * 字节转化工具类 
    5.  * @author liuyazhuang 
    6.  * 
    7.  */  
    8. public final class ByteUtils {  
    9.     /** 
    10.      * 将byte数组转化为int数字 
    11.      * @param bytes 
    12.      * @return 
    13.      */  
    14.     public static int byte2Int(byte[] bytes){  
    15.         int num = bytes[3] & 0xFF;  
    16.         num |= ((bytes[2] << 8) & 0xFF00);  
    17.         num |= ((bytes[1] << 16) & 0xFF0000);  
    18.         num |= ((bytes[0] << 24) & 0xFF000000);  
    19.         return num;  
    20.     }  
    21.       
    22.     /** 
    23.      * 将int类型数字转化为byte数组 
    24.      * @param num 
    25.      * @return 
    26.      */  
    27.     public static byte[] int2ByteArray(int i){  
    28.         byte[] result = new byte[4];  
    29.         result[0]  = (byte)(( i >> 24 ) & 0xFF);  
    30.         result[1]  = (byte)(( i >> 16 ) & 0xFF);  
    31.         result[2]  = (byte)(( i >> 8 ) & 0xFF);  
    32.         result[3]  = (byte)(i & 0xFF);  
    33.         return result;  
    34.     }  
    35. }  

    至此,我们这个应用层通信协议示例代码开发完成。

  • 相关阅读:
    几何——BAPC2019 K
    dsu on tree —— BAPC2019 A
    概率dp——BAPC2019 L
    计算几何+三分求极值——cf1046I
    上下文管理器
    转 -- 一行式
    转--python 基础
    转--python 面试题
    转 -- Python: 多继承模式下 MRO(Method Resolution Order) 的计算方式关乎super
    转--python之正则入门
  • 原文地址:https://www.cnblogs.com/kms1989/p/5872859.html
Copyright © 2011-2022 走看看