自己动手实现一个 Web Server

项目背景

最近在重温WEB服务器的相关机制和原理,为了方便记忆和理解,就尝试自己用Java写一个简化的WEB SERVER的实现,功能简单,简化了常规服务器的大部分功能和结构封装,但仍然保留从浏览器发送请求到将处理结果返回响应到浏览器的整个流程,现在把相关内容分享出来,供大家参考。

项目环境

IDE : eclipse 4.6.3

JDK : JDK1.8.0_131

Maven : Maven 3.5.2

项目结构

项目比较简单,就用一个普通的Java或Maven工程,引入JDK依赖即可。

工程下只有一个包,共包含六个文件。

WebServer : WEB 服务器主类,里面包含main方法,可直接运行启动服务器。

Request: 请求包装类,包含请求类型,请求URI。

Response:响应包装类,包含输出流,可向浏览器输出响应信息。

RequstParser:请求信息解析类,解析完成后返回一个Request。

ServiceDispacher:服务派发器,这里类似于Srping的DispatcherServlete。(不属于服务器部分)

TestController:模拟控制器返回信息。(不属于服务器部分)

其中ServiceDispacher和TestController,不属于服务器部分,这里为了方便测试,放在一个工程下。

实现流程

实现流程大致如下:

1 创建服务端ServerSocket, 绑定一个 端口号

2 循环监听客户端请求,连接成功后返回一个Socket

3 开启一个新的线程,传入Socket处理当前请求

4 Web Server调用ServiceDispacher进行服务的分发

5 ServiceDispacher根据请求查找并调用相应的控制器

6 控制器方法执行返回结果,并将结果相应到浏览器

代码示例

下面给出完整的代码实现,代码注释已经解释的比较清楚了,在这里就不再多费口舌了,快来源码见。

1 WebServer.java

package com.louis.web.server;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket; /**
* Web Server
* @author Louis
*/
public class WebServer { /**
* 服务器启动端口
*/
private int port = 8888;
/**
* 服务端Socket
*/
private ServerSocket serverSocket; public WebServer() {
init();
} /**
* 初始化服务端Socket
*/
private void init() {
try {
// 创建服务端Socket
serverSocket = new ServerSocket(port);
System.out.println("服务端已启动,等待客户端连接..");
} catch (IOException e) {
e.printStackTrace();
}
} /**
* 启动服务器,监听并处理客户请求
* @throws IOException
*/
public void start() throws IOException {
while (true) {
// 侦听并接受客户请求
Socket socket = serverSocket.accept();
// 新启线程,处理客户请求
new Thread() {
@Override
public void run() {
service(socket);
}
}.start();
}
} /**
* 处理客户请求
* @param socket
*/
private void service(Socket socket) {
InputStream inputStream = null;
OutputStream outputStream = null;
try {
inputStream = socket.getInputStream();
outputStream = socket.getOutputStream();
// 读取请求信息内容
Request request = new RequestParser().parse(inputStream);
Response response = new Response(outputStream);
service(request, response);
} catch (IOException e) {
e.printStackTrace();
} finally {
// 关闭连接
if (socket != null) {
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
System.out.println("接收到客户端连接, " + socket.getInetAddress() + ":" + socket.getPort());
} /**
* 处理客户请求, 把请求交给框架派遣服务,类似Spring的DispatcherServlet
* @param request
* @param response
*/
private void service(Request request, Response response) {
ServiceDispatcher serviceDispatcher = new ServiceDispatcher();
serviceDispatcher.dispatcher(request, response);
} public static void main(String[] args) {
try {
new WebServer().start();
} catch (IOException e) {
e.printStackTrace();
}
}
}

2 Request.java

package com.louis.web.server;

/**
* Request
* @author Louis
*/
public class Request {
/**
* 请求方式: GET\POST\DELETE..
*/
private String type;
/**
* 请求URI
*/
private String uri; public String getType() {
return type;
} public void setType(String type) {
this.type = type;
} public String getUri() {
return uri;
} public void setUri(String uri) {
this.uri = uri;
} }

3 Response.java

package com.louis.web.server;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStream; /**
* Response
*
* @author Louis
*/
public class Response {
private OutputStream output; public Response(OutputStream output) {
this.output = output;
} /**
* 输出文本信息
* @param text
* @throws IOException
*/
public void writeText(String text) {
FileInputStream fis = null;
try {
output.write("HTTP/1.1 200 OK\n".getBytes());
output.write("Content-Type: text/html; charset=UTF-8\n\n".getBytes());
output.write(text.getBytes());
} catch (Exception e) {
e.printStackTrace();
} finally {
if (fis != null) {
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}

4 RequestParser.java

package com.louis.web.server;
import java.io.InputStream; /**
* Request Parser
* @author Louis
*/
public class RequestParser {
private final static int BUFFER_SIZE = 1024; /**
* 解析请求
* @param inputStream
* @return Request
*/
public Request parse(InputStream inputStream) {
Request request = new Request();
// 读取请求信息
String requestMessage = readRequestMessage(inputStream);
// 解析请求方式
String type = parseType(requestMessage);
request.setType(type);
// 解析请求类型
String uri = parseUri(requestMessage);
request.setUri(uri);
return request;
} /**
* 读取请求信息
* @param input
* @return
*/
private String readRequestMessage(InputStream input) {
StringBuffer requestMessage = new StringBuffer();
int readLength = 0;
byte[] buffer = new byte[BUFFER_SIZE];
try {
readLength = input.read(buffer);
} catch (Exception e) {
e.printStackTrace();
readLength = -1;
}
for(int i = 0; i < readLength; i++) {
requestMessage.append((char) buffer[i]);
}
return requestMessage.toString();
} /**
* 解析请求方式
* @param requestString
* @return
*/
private String parseType(String requestString) {
int index = 0;
index = requestString.indexOf(' ');
if (index != -1) {
return requestString.substring(0, index);
}
return null;
} /**
* 解析请求类型
* @param requestString
* @return
*/
private String parseUri(String requestString) {
int index1, index2;
index1 = requestString.indexOf(' ');
if (index1 != -1) {
index2 = requestString.indexOf(' ', index1 + 1);
if (index2 > index1)
return requestString.substring(index1 + 1, index2);
}
return null;
} }

5 ServiceDispatcher.java

package com.louis.web.server;

/**
* 根据请求类型和URI找到对应的控制器,将请求交给控制器处理
*
* @author Louis
*/
public class ServiceDispatcher { /**
* 转发处理请求
* @param request
* @param response
*/
public void dispatcher(Request request, Response response) {
execController(request, response);
} /**
* 根据请求类型及URI等请求信息,找到并执行对应的控制器方法后返回
* 此处直接返回一个控制器,模拟查找和执行控制器方法的过程
* @param request
* @param response
* @return
*/
private void execController(Request request, Response response) {
String text = getControllerResult(request, response);
StringBuilder sb = new StringBuilder();
sb.append("请求类型: " + request.getType());
sb.append("<br/>请求URI: " + request.getUri());
sb.append("<br/>返回结果: " + text);
// 输出控制器返回结果
response.writeText(sb.toString());
} /**
* 模拟查找和执行控制器方法并返回结果
* @param request
* @param response
* @return
*/
private String getControllerResult(Request request, Response response) {
String text = "";
String uri = request.getUri();
String [] uriArray = uri.split("\\/");
if(uriArray.length != 3) {
text = "请求路径没有找到相关匹配服务. ";
} else if("test".equalsIgnoreCase(uriArray[1])) {
TestController testController = new TestController();
if("test1".equalsIgnoreCase(uriArray[2])) {
text = testController.test1();
} else if("test2".equalsIgnoreCase(uriArray[2])) {
text = testController.test2();
} else {
text = "请求路径没有找到相关匹配服务. ";
}
} else {
text = "请求路径没有找到相关匹配服务. ";
}
return text;
} }

6 TestController.java

package com.louis.web.server;

public class TestController {

    public String test1() {
return "TestController.test1() 调用成功";
} public String test2() {
return "TestController.test2() 调用成功";
}
}

启动测试

直接运行WebServer的main方法即可,控制台输出“服务端已启动,等待客户端连接..”, 启动成功。

启动完成之后,浏览器访问分别访问不同路径,查看响应结果。

如下图所示:

http://localhost:8888/test/test1

http://localhost:8888/test/test2

http://localhost:8888/test/test3

http://localhost:8888/test/mack

我们看到,当调用 /test/test1 和 /test/test2 的时候,控制器 TestController 的 test1 和 test2 方法相应被调用成功。而输入 test3 获取其他不存在的服务的时候,将会得到“请求路径没有找到相关匹配服务”的响应。

源码下载

码云:https://gitee.com/liuge1988/web-server


作者:朝雨忆轻尘
出处:https://www.cnblogs.com/xifengxiaoma/
版权所有,欢迎转载,转载请注明原文作者及出处。

自己动手实现一个WEB服务器的更多相关文章

  1. C#中自己动手创建一个Web Server(非Socket实现)

    目录 介绍 Web Server在Web架构系统中的作用 Web Server与Web网站程序的交互 HTTPListener与Socket两种方式的差异 附带Demo源码概述 Demo效果截图 总结 ...

  2. 用java写一个web服务器

    一.超文本传输协议 Web服务器和浏览器通过HTTP协议在Internet上发送和接收消息.HTTP协议是一种请求-应答式的协议——客户端发送一个请求,服务器返回该请求的应答.HTTP协议使用可靠的T ...

  3. 树莓派变成一个Web服务器: nginx + php + sqlite

    将树莓派变成一个Web服务器,通过访问网页,就可以控制树莓派,比如:查看摄像头\开灯等等. 一想到Linux Web服务器,我们首先想到的是,Apache + MySql + Php. 树莓派可以安装 ...

  4. 用C写一个web服务器(二) I/O多路复用之epoll

    .container { margin-right: auto; margin-left: auto; padding-left: 15px; padding-right: 15px } .conta ...

  5. 【重点突破】——使用Express创建一个web服务器

    一.引言 在自学node.js的过程中有一个非常重要的框架,那就是Express.它是一个基于NodeJs http模块而编写的高层模块,弥补http模块的繁琐和不方便,能够快速开发http服务器.这 ...

  6. C++实现一个web服务器, 弱智版服务器

    监听本地的8888端口, 当在浏览器中访问这个地址的时候, 返回一堆HTML数据, 这种方式返回的数据不稳定,不同浏览器解析不同, 因为我们没有定义返回文件类型: #include <stdli ...

  7. 树莓派(raspberry pi)学习11: 将树莓派变成一个Web服务器(转)

    将树莓派变成一个Web服务器,通过访问网页,就可以控制树莓派,比如:查看摄像头\开灯等等. 一想到Linux Web服务器,我们首先想到的是,Apache + MySql + Php. 树莓派可以安装 ...

  8. 十七、创建一个 WEB 服务器(一)

    1.Node.js 创建的第一个应用 var http=require("http") http.createServer(function (req,res) { res.wri ...

  9. 使用node.js 文档里的方法写一个web服务器

    刚刚看了node.js文档里的一个小例子,就是用 node.js 写一个web服务器的小例子 上代码 (*^▽^*) //helloworld.js// 使用node.js写一个服务器 const h ...

随机推荐

  1. (最长回文串 模板) 最长回文 -- hdu -- 3068

    http://acm.hdu.edu.cn/showproblem.php?pid=3068 最长回文 Time Limit: 4000/2000 MS (Java/Others)    Memory ...

  2. Codeforces805 C. Find Amir 2017-05-05 08:41 140人阅读 评论(0) 收藏

    C. Find Amir time limit per test 1 second memory limit per test 256 megabytes input standard input o ...

  3. Django开发Web监控工具-pyDash

      今天发现了一个比较有意思的监控工具,是基于Django开发的,开发大牛已经开放了源代码,向大牛致敬,同时研究研究,目前感觉这个监控比较直观,可以针对个人服务器使用,同时涉及的环境比较简单,部署起来 ...

  4. 一起学习MVC(3)Views的学习

          _ViewStart.cshtml._Layout.cshtml.Index.cshtml三个页面加载时候的先后顺序就是: _Layout.cshtml ViewStart.cshtml ...

  5. Team Foundation Server (TFS)与Project Server集成,使用DNS(友好地址)地址注册PWA

    问题描述: 当Team Foundation Server(TFS 2010/2012/2013)与Project Server高可用性的环境集成时,必然会使用Project Server (PWA) ...

  6. Ajax异步请求阻塞情况的解决办法

    最近使用ExtJs4的mvc模式在开发了在线漫画的后台,因为异步请求比较多,有的回应时间长,有点短.我发现在多次并发的情况下,会造成阻塞的情况.也就是说如果回应时间长的请求还在进行中,短的请求却被挂起 ...

  7. electron-vue 开发环境搭建(Windows环境)

    1.安装 Node.js,一切都基于这个.安装完成后,终端输入 node -v 验证. 2.建立自己的项目目录,然后进入目录. 安装 vue-cli ,终端输入 npm install -g vue- ...

  8. .net core Swagger

    Startup中的  ConfigureServices //注册 Swagger services.AddSwaggerGen(sg => { sg.SwaggerDoc("refu ...

  9. wpf数据绑定 - StringFormat的妙用

    写在前面 WPF中常常有这样的情况:需要在UI上显示一些信息,比如显示一张图片的信息,信息结构是: 图片名:Xxx 图片尺寸:Xxx 而其中的 Xxx 通常通过数据绑定来获得, Xxx 前面的内容是需 ...

  10. 2018-2019-2 20175306实验三敏捷开发与XP实践《Java开发环境的熟悉》实验报告

    2018-2019-2 20175306实验三敏捷开发与XP实践<Java开发环境的熟悉>实验报告 实验内容 XP基础 XP核心实践 相关工具 实验要求 1.没有Linux基础的同学建议先 ...