大家好,我是冰河~~

很多小伙伴跟我说,学习网络太难了,怎么办?其实很多技术都是相通的,只要你理解了技术的本质,你自己都可以实现它。这不,冰河就趁着周末,只用了几个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;}@Overridepublic 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;}@Overridepublic 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] << 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;}
}

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

写在最后

如果你想进大厂,想升职加薪,或者对自己现有的工作比较迷茫,都可以私信我交流,希望我的一些经历能够帮助到大家~~

推荐阅读:

  • 《奉劝那些刚参加工作的学弟学妹们:要想进大厂,这些核心技能是你必须要掌握的!完整学习路线!!(建议收藏)》
  • 《奉劝那些刚参加工作的学弟学妹们:这些计算机与操作系统基础知识越早知道越好!万字长文太顶了!!(建议收藏)》
  • 《我用三天时间开发了一款老少皆宜的国民级游戏,支持播放音乐,现开放完整源代码和注释(建议收藏)!!》
  • 《我是全网最硬核的高并发编程作者,CSDN最值得关注的博主,大家同意吗?(建议收藏)》
  • 《毕业五年,从月薪3000到年薪百万,我掌握了哪些核心技能?(建议收藏)》
  • 《我入侵了隔壁妹子的Wifi,发现。。。(全程实战干货,建议收藏)》
  • 《千万不要轻易尝试“熊猫烧香”,这不,我后悔了!》
  • 《清明节偷偷训练“熊猫烧香”,结果我的电脑为熊猫“献身了”!》
  • 《7.3万字肝爆Java8新特性,我不信你能看完!(建议收藏)》
  • 《在业务高峰期拔掉服务器电源是一种怎样的体验?》

好了,今天就到这儿吧,小伙伴们点赞、收藏、评论,一键三连走起呀,我是冰河,我们下期见~~

半小时实现Java手撸Http协议,爽!!(附完整源码,建议收藏)相关推荐

  1. C++ semi implicit euler半隐式向后欧拉法解算常微分方程(附完整源码)

    C++semi implicit euler半隐式向后欧拉法解算常微分方程 C++semi implicit euler半隐式向后欧拉法解算常微分方程完整源码(定义,实现,main函数测试) C++s ...

  2. C++kruskals算法生成最小协议树(附完整源码)

    C++kruskals算法生成最小协议树 C++kruskals算法生成最小协议树完整源码(定义,实现,main函数测试) C++kruskals算法生成最小协议树完整源码(定义,实现,main函数测 ...

  3. C++prims算法生成最小协议树(附完整源码)

    C++prims算法生成最小协议树 C++实现prims算法生成最小协议树完整源码(定义,实现,main函数测试) C++实现prims算法生成最小协议树完整源码(定义,实现,main函数测试) #i ...

  4. java 教室借用管理系统_[内附完整源码和文档] 基于JAVA语言的学生选课信息管理系统...

    摘 要 本系统运用Java面向对象的方法设计而成. 近年来,学生选课系统越来越在高校学生群体中得到普及,其所承担的功能也变得越来越丰富,所起到的作用也变得越来越重要,在被学校学生重视的同时,也意味着它 ...

  5. JAVA:实现求StandardDeviation标准差算法(附完整源码)

    JAVA:实现求StandardDeviation标准差算法 package com.thealgorithms.maths;public class StandardDeviation {publi ...

  6. Java:实现​lz4格式解压缩算法(附完整源码)

    Java:实现​lz4格式解压缩算法 public static byte[] compress(byte srcBytes[]) throws IOException {LZ4Factory fac ...

  7. JAVA:实现二进制转八进制算法(附完整源码)

    JAVA:实现二进制转八进制算法 package com.thealgorithms.conversions;import java.util.Scanner;public class BinaryT ...

  8. JAVA:实现十进制转二进制算法(附完整源码)

    JAVA:实现十进制转二进制算法 package com.thealgorithms.conversions;import java.util.Scanner;/*** This class conv ...

  9. JAVA:实现XXTea加解密算法(附完整源码)

    JAVA:实现XXTea加解密算法 public class XXTEAprivate XXTEA() {}public static byte[] encrypt(byte[] data, byte

最新文章

  1. zabbix3.2监控centos6网卡流量
  2. Netty网络聊天室完整代码实现
  3. 上传数据时 ajax请求成功 上传完成,ajaxSubmit请求返回数据成功,但是不执行success回调函数...
  4. mysql limit asc_MySql sql优化之order by desc/asc limit M-阿里云开发者社区
  5. 免费的编程中文书籍索引(2018第三版)
  6. GenerateResource”任务意外失败的解决方法
  7. phython拟合曲面方程_python数据关系型图表散点图系列曲面拟合图
  8. php中的索引数组和关联数组
  9. PTA-Hello World(C语言)
  10. 《Effective C#》Item 20:区分接口实现与虚函数重载
  11. 广东2022年下半年系统集成项目管理工程师上午真题及答案解析
  12. hutool 读取扩展名文件_好多公司都要用的一些知识点Office办公软件、文件加密、文件扩展名!...
  13. 深究C语言4.链表和结构体
  14. 一款轻巧简单疫情动态网站源码
  15. win7如何调整计算机c盘,win7系统让c盘和d盘合并的两种方法
  16. input输入框去除历史记录
  17. AsyncTask——AsyncTask串行and并行
  18. 笔记:文澜:桥接视觉和语言的大规模多模态预训练 WenLan: Bridging Vision and Language by Large-Scale Multi-Modal Pre-Training
  19. kernel编译练习2:给ubuntu22升级5.19内核
  20. T3054 高精度练习-文件操作 codevs

热门文章

  1. CST入门——求解器简介与时域、频域和积分求解器设置
  2. ZYNQ开发系列——ZYNQ系统的搭建
  3. python 赋值语句
  4. contains用法
  5. 电视机接口中英文介绍
  6. MySQL时间戳与时间格式的转换
  7. ui九宫格切图_【九宫切图】什么是九宫绘图,九宫格绘法
  8. 关于大数据的视频资料
  9. AutoLayout的使用
  10. 【转载】关于重定向RedirectAttributes的用法