RPC之——HTTP協議棧

來源:互聯網
上載者:User

標籤:

轉載請註明出處: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、協議請求的定義

協議的請求主要包括:編碼、命令和命令長度三個欄位。

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;}@Overridepublic String toString() {return "Request [encode=" + encode + ", command=" + command+ ", commandLength=" + commandLength + "]";}}

2、響應協議的定義

協議的響應主要包括:編碼、響應內容和響應長度三個欄位。

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;}@Overridepublic String toString() {return "Response [encode=" + encode + ", response=" + response+ ", responseLength=" + responseLength + "]";}}

3、編碼常量定義

編碼常量的定義主要包括UTF-8和GBK兩種編碼。

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對象。

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”資訊。

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位,所以,在傳遞和接收資料時,需要進行相應的轉化操作。

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類的實現

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;}}
至此,我們這個應用程式層通訊協定範例程式碼開發完成。

RPC之——HTTP協議棧

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.