java搭建http协议服务器,半小时实现Java手撸Http协议,爽!(含完整源码)

大家好,我是冰河~~

很多小伙伴跟我说,学习网络太难了,怎么办?其实很多技术都是相通的,只要你理解了技术的本质,你自己都可以实现它。这不,冰河就趁着周末,只用了几个Java类就简单的实现了Http协议,爽!!

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

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

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

我们接下来用Java实现这个简单的应用层通信协议,说干就干,走起~~

协议请求的定义

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

package com.binghe.params;

/**

* 协议请求的定义

* @author binghe

*

*/

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 + "]";

}

}

响应协议的定义

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

package com.binghe.params;

/**

* 协议响应的定义

* @author binghe

*

*/

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 + "]";

}

}

编码常量定义

编码常量的定义主要包括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;

}

客户端的实现

客户端先构造一个request请求,通过Socket接口将其发送到远端,并接收远端的响应信息,并构造成一个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();

//发送请求

ProtocolUtils.writeRequest(out, request);

//读取响应数据

InputStream in = client.getInputStream();

Response response = ProtocolUtils.readResponse(in);

System.out.println("获取的响应结果信息为: " + response.toString());

}

}

服务端的实现

服务端接收客户端的请求,根据接收命令的不同,响应不同的消息信息,如果是“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();

//读取请求数据

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);

}

}

}

ProtocolUtils工具类的实现

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

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

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;

/**

* 协议工具类

* @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类的实现

package com.binghe.utils;

/**

* 字节转化工具类

* @author binghe

*

*/

public final class ByteUtils{

/**

* 将byte数组转化为int数字

* @param bytes

* @return

*/

public static int byte2Int(byte[] bytes){

int num = bytes[3] & 0xFF;

num |= ((bytes[2] <

num |= ((bytes[1] <

num |= ((bytes[0] <

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;

}

}

至此,我们这个应用层通信协议示例代码开发完成,怎么样,小伙伴们,是不是很简单呢?赶紧打开你的环境,手撸个Http协议吧!!

好了,今天就到这儿吧,我是冰河,大家有啥问题可以在下方留言,也可以加我微信:sun_shine_lyz,我拉你进群,一起交流技术,一起进阶,一起进大厂~~

你可能感兴趣的:(java搭建http协议服务器)