http服务器_使用JavaSocket手撸一个http服务器

作为一个java后端,提供http服务可以说是基本技能之一了,但是你真的了解http协议么?你知道知道如何手撸一个http服务器么?tomcat的底层是怎么支持http服务的呢?大名鼎鼎的Servlet又是什么东西呢,该怎么使用呢?

在初学java时,socket编程是逃不掉的一章;虽然在实际业务项目中,使用这个的可能性基本为0,本篇博文将主要介绍如何使用socket来实现一个简单的http服务器功能,提供常见的get/post请求支持,并再此过程中了解下http协议

I. Http服务器从0到1

既然我们的目标是借助socket来搭建http服务器,那么我们首先需要确认两点,一是如何使用socket;另一个则是http协议如何,怎么解析数据;下面分别进行说明

1. socket编程基础

我们这里主要是利用ServerSocket来绑定端口,提供tcp服务,基本使用姿势也比较简单,一般套路如下

  • 创建ServerSocket对象,绑定监听端口
  • 通过accept()方法监听客户端请求
  • 连接建立后,通过输入流读取客户端发送的请求信息
  • 通过输出流向客户端发送乡音信息
  • 关闭相关资源

对应的伪代码如下:

ServerSocket serverSocket = new ServerSocket(port, ip)serverSocket.accept();// 接收请求数据socket.getInputStream();// 返回数据给请求方out = socket.getOutputStream()out.print(xxx)out.flush();;// 关闭连接socket.close()

2. http协议

我们上面的ServerSocket走的是TCP协议,HTTP协议本身是在TCP协议之上的一层,对于我们创建http服务器而言,最需要关注的无非两点

  • 请求的数据怎么按照http的协议解析出来
  • 如何按照http协议,返回数据

所以我们需要知道数据格式的规范了

请求消息

dd480ce4319f839b79782ef4ba44b906.png

响应消息

df8d6102a0a17b39723aa47ff18d7226.png

上面两张图,先有个直观映象,接下来开始抓重点

不管是请求消息还是相应消息,都可以划分为三部分,这就为我们后面的处理简化了很多

  • 第一行:状态行
  • 第二行到第一个空行:header(请求头/相应头)
  • 剩下所有:正文

3. http服务器设计

接下来开始进入正题,基于socket创建一个http服务器,使用socket基本没啥太大的问题,我们需要额外关注以下几点

  • 对请求数据进行解析
  • 封装返回结果

a. 请求数据解析

我们从socket中拿到所有的数据,然后解析为对应的http请求,我们先定义个Request对象,内部保存一些基本的HTTP信息,接下来重点就是将socket中的所有数据都捞出来,封装为request对象

@Datapublic static class Request { /** * 请求方法 GET/POST/PUT/DELETE/OPTION... */ private String method; /** * 请求的uri */ private String uri; /** * http版本 */ private String version; /** * 请求头 */ private Map headers; /** * 请求参数相关 */ private String message;}

根据前面的http协议介绍,解析过程如下,我们先看请求行的解析过程

请求行,包含三个基本要素:请求方法 + URI + http版本,用空格进行分割,所以解析代码如下

/** * 根据标准的http协议,解析请求行 * * @param reader * @param request */private static void decodeRequestLine(BufferedReader reader, Request request) throws IOException { String[] strs = StringUtils.split(reader.readLine(), " "); assert strs.length == 3; request.setMethod(strs[0]); request.setUri(strs[1]); request.setVersion(strs[2]);}

请求头的解析,从第二行,到第一个空白行之间的所有数据,都是请求头;请求头的格式也比较清晰, 形如 key:value, 具体实现如下

/** * 根据标准http协议,解析请求头 * * @param reader * @param request * @throws IOException */private static void decodeRequestHeader(BufferedReader reader, Request request) throws IOException { Map headers = new HashMap<>(16); String line = reader.readLine(); String[] kv; while (!"".equals(line)) { kv = StringUtils.split(line, ":"); assert kv.length == 2; headers.put(kv[0].trim(), kv[1].trim()); line = reader.readLine(); } request.setHeaders(headers);}

最后就是正文的解析了,这一块需要注意一点,正文可能为空,也可能有数据;有数据时,我们要如何把所有的数据都取出来呢?

先看具体实现如下

/** * 根据标注http协议,解析正文 * * @param reader * @param request * @throws IOException */private static void decodeRequestMessage(BufferedReader reader, Request request) throws IOException { int contentLen = Integer.parseInt(request.getHeaders().getOrDefault("Content-Length
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值