RPC之——HTTP协议栈

mac2022-06-30  115

转载请注明出处: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     package com.lyz.params;    /**  * 协议请求的定义  * @author liuyazhuang  *  */  public class Request {      /**      * 协议编码      */      private byte encode;            /**      * 命令      */      private String command;            /**      * 命令长度      */      private int commandLength;        public Request() {          super();      }        public Request(byte encode, String command, int commandLength) {          super();          this.encode = encode;          this.command = command;          this.commandLength = commandLength;      }        public byte getEncode() {          return encode;      }        public void setEncode(byte encode) {          this.encode = encode;      }        public String getCommand() {          return command;      }        public void setCommand(String command) {          this.command = command;      }        public int getCommandLength() {          return commandLength;      }        public void setCommandLength(int commandLength) {          this.commandLength = commandLength;      }        @Override      public String toString() {          return "Request [encode=" + encode + ", command=" + command                  + ", commandLength=" + commandLength + "]";      }        }  

 

2、响应协议的定义

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

 

[java]  view plain  copy     package com.lyz.params;    /**  * 协议响应的定义  * @author liuyazhuang  *  */  public class Response {      /**      * 编码      */      private byte encode;            /**      * 响应内容      */      private String response;            /**      * 响应长度      */      private int responseLength;        public Response() {          super();      }        public Response(byte encode, String response, int responseLength) {          super();          this.encode = encode;          this.response = response;          this.responseLength = responseLength;      }        public byte getEncode() {          return encode;      }        public void setEncode(byte encode) {          this.encode = encode;      }        public String getResponse() {          return response;      }        public void setResponse(String response) {          this.response = response;      }        public int getResponseLength() {          return responseLength;      }        public void setResponseLength(int responseLength) {          this.responseLength = responseLength;      }        @Override      public String toString() {          return "Response [encode=" + encode + ", response=" + response                  + ", responseLength=" + responseLength + "]";      }        }  

 

3、编码常量定义

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

 

[java]  view plain  copy     package com.lyz.constant;    /**  * 常量类  * @author liuyazhuang  *  */  public final class Encode {      //UTF-8编码      public static final byte UTF8 = 1;      //GBK编码      public static final byte GBK = 2;  }  

 

4、客户端的实现

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

 

[java]  view plain  copy     package com.lyz.protocol.client;    import java.io.IOException;  import java.io.InputStream;  import java.io.OutputStream;  import java.net.Socket;    import com.lyz.constant.Encode;  import com.lyz.params.Request;  import com.lyz.params.Response;  import com.lyz.utils.ProtocolUtils;    /**  * 客户端代码  * @author liuyazhuang  *  */  public final class Client {      public static void main(String[] args) throws IOException{          //请求          Request request = new Request();          request.setCommand("HELLO");          request.setCommandLength(request.getCommand().length());          request.setEncode(Encode.UTF8);                    Socket client = new Socket("127.0.0.1", 4567);          OutputStream out = client.getOutputStream();                    //发送请求          ProtocolUtils.writeRequest(out, request);                    //读取响应数据          InputStream in = client.getInputStream();          Response response = ProtocolUtils.readResponse(in);          System.out.println("获取的响应结果信息为: " + response.toString());      }  }  

 

5、服务端的实现

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

 

[java]  view plain  copy     package com.lyz.protocol.server;    import java.io.IOException;  import java.io.InputStream;  import java.io.OutputStream;  import java.net.ServerSocket;  import java.net.Socket;    import com.lyz.constant.Encode;  import com.lyz.params.Request;  import com.lyz.params.Response;  import com.lyz.utils.ProtocolUtils;    /**  * Server端代码  * @author liuyazhuang  *  */  public final class Server {      public static void main(String[] args) throws IOException{          ServerSocket server = new ServerSocket(4567);          while (true) {              Socket client = server.accept();              //读取请求数据              InputStream input = client.getInputStream();              Request request = ProtocolUtils.readRequest(input);              System.out.println("收到的请求参数为: " + request.toString());              OutputStream out = client.getOutputStream();              //组装响应数据              Response response = new Response();              response.setEncode(Encode.UTF8);              if("HELLO".equals(request.getCommand())){                  response.setResponse("hello");              }else{                  response.setResponse("bye bye");              }              response.setResponseLength(response.getResponse().length());              ProtocolUtils.writeResponse(out, response);          }      }  }  

 

6、ProtocolUtils工具类的实现

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

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

 

[java]  view plain  copy     package com.lyz.utils;    import java.io.IOException;  import java.io.InputStream;  import java.io.OutputStream;    import com.lyz.constant.Encode;  import com.lyz.params.Request;  import com.lyz.params.Response;    /**  * 协议工具类  * @author liuyazhuang  *  */  public final class ProtocolUtils {      /**      * 从输入流中反序列化Request对象      * @param input      * @return      * @throws IOException      */      public static Request readRequest(InputStream input) throws IOException{          //读取编码          byte[] encodeByte = new byte[1];          input.read(encodeByte);          byte encode = encodeByte[0];                    //读取命令长度          byte[] commandLengthBytes = new byte[4];          input.read(commandLengthBytes);          int commandLength = ByteUtils.byte2Int(commandLengthBytes);                    //读取命令          byte[] commandBytes = new byte[commandLength];          input.read(commandBytes);          String command = "";          if(Encode.UTF8 == encode){              command = new String(commandBytes, "UTF-8");          }else if(Encode.GBK == encode){              command = new String(commandBytes, "GBK");          }          //组装请求返回          Request request = new Request(encode, command, commandLength);          return request;      }      /**      * 从输入流中反序列化Response对象      * @param input      * @return      * @throws IOException      */      public static Response readResponse(InputStream input) throws IOException{          //读取编码          byte[] encodeByte = new byte[1];          input.read(encodeByte);          byte encode = encodeByte[0];                    //读取响应长度          byte[] responseLengthBytes = new byte[4];          input.read(responseLengthBytes);          int responseLength = ByteUtils.byte2Int(responseLengthBytes);                    //读取命令          byte[] responseBytes = new byte[responseLength];          input.read(responseBytes);          String response = "";          if(Encode.UTF8 == encode){              response = new String(responseBytes, "UTF-8");          }else if(Encode.GBK == encode){              response = new String(responseBytes, "GBK");          }          //组装请求返回          Response resp = new Response(encode, response, responseLength);          return resp;      }            /**      * 序列化请求信息      * @param output      * @param response      */      public static void writeRequest(OutputStream output, Request request) throws IOException{          //将response响应返回给客户端          output.write(request.getEncode());          //output.write(response.getResponseLength());直接write一个int类型会截取低8位传输丢弃高24位          output.write(ByteUtils.int2ByteArray(request.getCommandLength()));          if(Encode.UTF8 == request.getEncode()){              output.write(request.getCommand().getBytes("UTF-8"));          }else if(Encode.GBK == request.getEncode()){              output.write(request.getCommand().getBytes("GBK"));          }          output.flush();      }      /**      * 序列化响应信息      * @param output      * @param response      */      public static void writeResponse(OutputStream output, Response response) throws IOException{          //将response响应返回给客户端          output.write(response.getEncode());          //output.write(response.getResponseLength());直接write一个int类型会截取低8位传输丢弃高24位          output.write(ByteUtils.int2ByteArray(response.getResponseLength()));          if(Encode.UTF8 == response.getEncode()){              output.write(response.getResponse().getBytes("UTF-8"));          }else if(Encode.GBK == response.getEncode()){              output.write(response.getResponse().getBytes("GBK"));          }          output.flush();      }  }  

 

7、ByteUtils类的实现

 

[java]  view plain  copy     package com.lyz.utils;    /**  * 字节转化工具类  * @author liuyazhuang  *  */  public final class ByteUtils {      /**      * 将byte数组转化为int数字      * @param bytes      * @return      */      public static int byte2Int(byte[] bytes){          int num = bytes[3] & 0xFF;          num |= ((bytes[2] << 8) & 0xFF00);          num |= ((bytes[1] << 16) & 0xFF0000);          num |= ((bytes[0] << 24) & 0xFF000000);          return num;      }            /**      * 将int类型数字转化为byte数组      * @param num      * @return      */      public static byte[] int2ByteArray(int i){          byte[] result = new byte[4];          result[0]  = (byte)(( i >> 24 ) & 0xFF);          result[1]  = (byte)(( i >> 16 ) & 0xFF);          result[2]  = (byte)(( i >> 8 ) & 0xFF);          result[3]  = (byte)(i & 0xFF);          return result;      }  }  

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

转载于:https://www.cnblogs.com/kms1989/p/5872859.html

相关资源:JAVA上百实例源码以及开源项目
最新回复(0)