Java实现简易Web服务器
作者:蒋固金 发布时间:2023-11-17 09:10:11
众所周知Web服务器与客户端之间的通信是使用HTTP协议的。HTTP是一个客户端和服务器端请求和应答的标准(TCP)。因为HTTP协议是基于TCP协议的,所以我将使用JAVA中的Socket完成这个简易的Web服务器。关于HTTP更详细的资料,各位可以查阅相关资料进行了解。
在服务器编写之前,我们还是先来看一下浏览器与服务器之间通信的规则到底如何。
首先,我们是用ServerSocket来模拟一个服务端,通过浏览器访问,查看浏览器请求的内容:
import java.io.BufferedWriter;
import java.io.InputStream;
import java.io.OutputStreamWriter;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import org.junit.Test;
/**
* HTTP协议测试
*
* @author jianggujin
*
*/
public class HQHttpProtocolTest
{
@Test
public void server() throws Exception
{
ServerSocket serverSocket = new ServerSocket(80);
Socket socket = serverSocket.accept();
InputStream stream = socket.getInputStream();
int r = -1;
while ((r = stream.read()) != -1)
{
System.out.print((char) r);
}
}
}
使用junit运行,并通过浏览器访问:http://127.0.0.1,我们可以看到控制台上输出浏览器的请求内容如下:
GET / HTTP/1.1
Host: 127.0.0.1
Connection: keep-alive
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
User-Agent: Mozilla/5.0 (Windows NT 5.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.63 Safari/537
.36
Accept-Encoding: gzip,deflate,sdch
Accept-Language: zh-CN,zh;q=0.8
为了更好的分析请求内容,我们编写一个HTML页面提交一些数据,再次查看请求内容:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>test</title>
</head>
<body>
<form method="post" action="http://127.0.0.1?test=123">
<input type="text" name="name"/>
<input type="submit"/>
</form>
</body>
</html>
在输入框中输入bob,点击按钮提交,观察控制台输出:
POST /?test=123 HTTP/1.1
Host: 127.0.0.1
Connection: keep-alive
Content-Length: 8
Cache-Control: max-age=0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Origin: null
User-Agent: Mozilla/5.0 (Windows NT 5.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.63 Safari/537
.36
Content-Type: application/x-www-form-urlencoded
Accept-Encoding: gzip,deflate,sdch
Accept-Language: zh-CN,zh;q=0.8
name=bob
我们来分析一下这段请求内容:
第一行:由三部分组成,中间以空格分开,第一部分为请求方法(GET、POST),第二部分为请求路径以及查询参数,第三部分为HTTP协议版本(HTTP/1.1)
第二行到第十行:请求的头信息,请求头名称与值之间通过:分隔
第十一行:空行
第十二行:提交的表单内容
综上,我们可以得到如下结论:请求信息第一行为请求方法、请求路径以及查询参数、HTTP协议版本,通过\r\n换行后紧跟着请求头信息,各头信息之间通过\r\n换行,请求头信息结束后跟着一个空行,空行之后紧跟着一行为请求数据,需要注意的是,这里面只模拟了最简单的表单提交,至于复杂的文件提交等,这里面不讨论,请求内容格式略有不同。
至此,客户端请求的内容我们已经知道了,下面我们再来看看服务端在接收到请求后响应数据的格式,我们新建一个Web项目用于测试,编辑Html页面内容如下:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>test</title>
</head>
<body>this is test page.
</body>
</html>
启动服务器,然后编写客户端测试代码,获得服务端返回数据:
import java.io.BufferedWriter;
import java.io.InputStream;
import java.io.OutputStreamWriter;
import java.net.ServerSocket;
import java.net.Socket;
import org.junit.Test;
/**
* HTTP协议测试
*
* @author jianggujin
*
*/
public class HQHttpProtocolTest
{
public void server() throws Exception
{
ServerSocket serverSocket = new ServerSocket(80);
Socket socket = serverSocket.accept();
InputStream stream = socket.getInputStream();
// BufferedInputStream inputStream = new BufferedInputStream(stream);
int r = -1;
while ((r = stream.read()) != -1)
{
System.out.print((char) r);
}
}
@Test
public void client() throws Exception
{
Socket socket = new Socket("127.0.0.1", 80);
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(
socket.getOutputStream()));
writer.write("GET /Servlet/test.html HTTP/1.1\r\n");
writer.write("Host: 127.0.0.1\r\n");
writer.write("Connection: keep-alive\r\n");
writer.write("Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8\r\n");
writer.write("User-Agent: Mozilla/5.0 (Windows NT 5.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.63 Safari/537.36\r\n");
writer.write("Accept-Encoding: gzip,deflate,sdch\r\n");
writer.write("Accept-Language: zh-CN,zh;q=0.8\r\n");
writer.write("\r\n");
writer.flush();
InputStream stream = socket.getInputStream();
int r = -1;
while ((r = stream.read()) != -1)
{
System.out.print((char) r);
}
}
}
运行程序获得服务器返回内容如下:
HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
Accept-Ranges: bytes
ETag: W/"129-1456125361109"
Last-Modified: Mon, 22 Feb 2016 07:16:01 GMT
Content-Type: text/html
Content-Length: 129
Date: Mon, 22 Feb 2016 08:08:32 GMT
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>test</title>
</head>
<body>this is test page.
</body>
</html>
同样的,我们来分析一下这段返回消息:
第一行由三部分组成,中间以空格分开,第一部分为HTTP协议版本(HTTP/1.1),第二部分为响应状态码,第三部分为响应状态描述
第二行到第七行为响应头信息,响应头名称与值之间通过:分隔
第八行:空行
第九行到结束:响应内容
综上,我们可以得到如下结论:请求信息第一行为HTTP协议版本、响应状态码、响应状态描述,通过\r\n换行后紧跟着响应头信息,各头信息之间通过\r\n换行,响应头信息结束后跟着一个空行,空行之后紧跟着响应数据,需要注意的是,除这种响应外,其实还有其他的相应方式,比如chunk,此处不讨论,可查阅相关资料。
到现在为止,我们已经分析完了客户端的请求内容格式以及服务端相应内容的格式,这一篇就到此为止了,希望对大家的学习有所帮助。


猜你喜欢
- 这篇文章主要介绍了spring boot如何加入mail邮件支持,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值
- 本文实例为大家分享了Java实现部门员工管理的具体代码,供大家参考,具体内容如下项目作业:部门员工管理题目要求:某公司要开发内部的 &
- 概述一天之计在于晨,每天的早餐也是必不可少,但是很多人为了节约时间,都是简单的吃点凑合一下或干脆不吃早餐,这对于个人身体和工作效率来说,无疑
- public static IPAddress GetHostIP(){ &nbs
- 不废话了,直接给大家贴代码了。class term { String str; int id; &
- 本文将介绍使用Spring Boot集成Mybatis并实现主从库分离的实现(同样适用于多数据源)。延续之前的Spring Boot 集成M
- 算法描述堆排序算法的描述如下:将待排序的数组调整为最大堆,此时未排序的长度 N 为数组的长度,调整的过程就是倒序将数组的
- Android中的翻转动画效果的实现,首先看一下运行效果如上图所示. Android中并没有提供直接做3D翻转的动画,所以关于3D翻转的动画
- 这几天做项目,有些地方的图片需要用到圆形图片,所以百度了一下,在github上找到一个开源项目,处理很简单,效果如下: 使用起来特
- 前言对于线程安全,我们有说不尽的话题。大多数保证线程安全的方法是添加各种类型锁,使用各种同步机制,用限制对共享的、可变的类变量并发访问的方式
- C#版本public static Component AddComponent(GameObject go, string assembl
- 在初始化自己位置的时候请求定位权限:Constants.ACCESS_FINE_LOCATION_COMMANDS_REQUEST_CODE
- 有时,通过Runtime.getRuntime().exec()执行命令的有效负载有时会失败。使用Web Shell,反序列化利用或通过其他
- Dubbo过滤器概述Dubbo中的过滤器和Web应用中的过滤器的概念是一样的,提供了在服务调用前后插入自定义逻辑的途径。过滤器是整个Dubb
- 在说ClassCastException之前,先介绍下引用类型转换;引用类型转换分为向上转型和向下转型两种; 向上转型:多态本身是
- 一、可空类型修饰符(?)C#2.0里面实现了Nullable数据类型//A.比如下面一句,直接定义int为null是错误的,错误提示为无法将
- 我们平时在日常项目中经常会遇到图片的上传和访问的情景,平时我们可能习惯于把图片传到resource或者项项目中的某个位置,这样会有一个缺点,
- 前言本文主要演示一个普通 java 项目导入IDEA的流程步骤及可能出现的问题、原因及解决办法。本文使用的部分软件版本如下:IDEA 201
- 1.之前在使用AutoMapper 框架感觉用着比较不够灵活,而且主要通过表达式树Api 实现对象映射 ,写着比较讨厌,当出现复杂类型和嵌套
- 在winform里拖入一个datagridview控件,跟一个openfiledialog控件using System;using Syst