使用 Java 類 實現(xiàn)Http協(xié)議
Java 實現(xiàn)Http協(xié)議
HTTP協(xié)議屬于應用層協(xié)議,它構(gòu)建于TCP和IP協(xié)議之上,處于TCP/IP協(xié)議架構(gòu)層的頂端,所以,它不用處理下層協(xié)議間諸如丟包補發(fā)、握手及數(shù)據(jù)的分段及重新組裝等繁瑣的細節(jié),使開發(fā)人員可以專注于應用業(yè)務。
協(xié)議是通信的規(guī)范,為了更好的理解HTTP協(xié)議,我們可以基于Java的Socket API接口,通過設計一個簡單的應用層通信協(xié)議,來簡單分析下協(xié)議實現(xiàn)的過程和細節(jié)。
在我們今天的示例程序中,客戶端會向服務端發(fā)送一條命令,服務端在接收到命令后,會判斷命令是否是“HELLO”,如果是“HELLO”, 則服務端返回給客戶端的響應為“hello”,否則,服務端返回給客戶端的響應為“bye bye”。
我們接下來用Java實現(xiàn)這個簡單的應用層通信協(xié)議,

一、協(xié)議請求的定義
協(xié)議的請求主要包括:編碼、命令和命令長度三個字段。
package com.binghe.params;
/**
* 協(xié)議請求的定義
* @author binghe
*
*/
public class Request {
/**
* 協(xié)議編碼
*/
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 + "]";
}
}
二、響應協(xié)議的定義
協(xié)議的響應主要包括:編碼、響應內(nèi)容和響應長度三個字段。
package com.binghe.params;
/**
* 協(xié)議響應的定義
* @author binghe
*
*/
public class Response {
/**
* 編碼
*/
private byte encode;
/**
* 響應內(nèi)容
*/
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 + "]";
}
}
三、編碼常量定義
編碼常量的定義主要包括UTF-8和GBK兩種編碼。
package com.binghe.constant;
/**
* 常量類
* @author binghe
*
*/
public final class Encode {
//UTF-8編碼
public static final byte UTF8 = 1;
//GBK編碼
public static final byte GBK = 2;
}
四、客戶端的實現(xiàn)
客戶端先構(gòu)造一個request請求,通過Socket接口將其發(fā)送到遠端,并接收遠端的響應信息,并構(gòu)造成一個Response對象。
package com.binghe.protocol.client;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import com.binghe.constant.Encode;
import com.binghe.params.Request;
import com.binghe.params.Response;
import com.binghe.utils.ProtocolUtils;
/**
* 客戶端代碼
* @author binghe
*
*/
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();
//發(fā)送請求
ProtocolUtils.writeRequest(out, request);
//讀取響應數(shù)據(jù)
InputStream in = client.getInputStream();
Response response = ProtocolUtils.readResponse(in);
System.out.println("獲取的響應結(jié)果信息為: " + response.toString());
}
}
五、服務端的實現(xiàn)
服務端接收客戶端的請求,根據(jù)接收命令的不同,響應不同的消息信息,如果是“HELLO”命令,則響應“hello”信息,否則響應“bye bye”信息。
package com.binghe.protocol.server;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import com.binghe.constant.Encode;
import com.binghe.params.Request;
import com.binghe.params.Response;
import com.binghe.utils.ProtocolUtils;
/**
* Server端代碼
* @author binghe
*
*/
public final class Server {
public static void main(String[] args) throws IOException{
ServerSocket server = new ServerSocket(4567);
while (true) {
Socket client = server.accept();
//讀取請求數(shù)據(jù)
InputStream input = client.getInputStream();
Request request = ProtocolUtils.readRequest(input);
System.out.println("收到的請求參數(shù)為: " + request.toString());
OutputStream out = client.getOutputStream();
//組裝響應數(shù)據(jù)
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);
}
}
}
六、ProtocolUtils工具類的實現(xiàn)
ProtocolUtils的readRequest方法將從傳遞進來的輸入流中讀取請求的encode、command和commandLength三個參數(shù),進行相應的編碼轉(zhuǎn)化,構(gòu)造成Request對象返回。而writeResponse方法則是將response對象的字段根據(jù)對應的編碼寫入到響應的輸出流中。
有一個細節(jié)需要重點注意:OutputStream中直接寫入一個int類型,會截取其低8位,丟棄其高24位,所以,在傳遞和接收數(shù)據(jù)時,需要進行相應的轉(zhuǎn)化操作。
package com.binghe.utils;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import com.binghe.constant.Encode;
import com.binghe.params.Request;
import com.binghe.params.Response;
/**
* 協(xié)議工具類
* @author binghe
*
*/
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();
}
}
七、ByteUtils類的實現(xiàn)
package com.binghe.utils;
/**
* 字節(jié)轉(zhuǎn)化工具類
* @author binghe
*
*/
public final class ByteUtils {
/**
* 將byte數(shù)組轉(zhuǎn)化為int數(shù)字
* @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類型數(shù)字轉(zhuǎn)化為byte數(shù)組
* @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;
}
}
到此這篇關于關于Java 實現(xiàn)Http協(xié)議詳細內(nèi)容的文章就介紹到這了,更多相關Java 實現(xiàn)Http協(xié)議內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章
Java Quartz觸發(fā)器CronTriggerBean配置用法詳解
這篇文章主要介紹了Java Quartz觸發(fā)器CronTriggerBean配置用法詳解,本篇文章通過簡要的案例,講解了該項技術的了解與使用,以下就是詳細內(nèi)容,需要的朋友可以參考下2021-08-08
Java TreeMap升序|降序排列和按照value進行排序的案例
這篇文章主要介紹了Java TreeMap升序|降序排列和按照value進行排序的案例,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-10-10
Springboot傳輸數(shù)據(jù)時日期格式化問題
這篇文章主要介紹了Springboot傳輸數(shù)據(jù)時日期格式化問題,本文通過示例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2023-09-09
java訪問者模式的靜態(tài)動態(tài)及偽動態(tài)分派徹底理解
這篇文章主要為大家介紹了java訪問者模式的靜態(tài)動態(tài)及偽動態(tài)分派徹底理解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-06-06

