大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。
Jetbrains全系列IDE使用 1年只要46元 售后保障 童叟无欺
大家好,我是冰河~~
很多小伙伴跟我说,学习网络太难了,怎么办?其实很多技术都是相通的,只要你理解了技术的本质,你自己都可以实现它。这不,冰河就趁着周末,只用了几个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] << 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新特性,我不信你能看完!(建议收藏)》
- 《在业务高峰期拔掉服务器电源是一种怎样的体验?》
好了,今天就到这儿吧,小伙伴们点赞、收藏、评论,一键三连走起呀,我是冰河,我们下期见~~
发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/172003.html原文链接:https://javaforall.cn
【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛
【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...