自己动手实现简单web容器一

一、前言

    主流系统架构无非分两种:C/S(客户端/服务器)、B/S(浏览器/服务器)

    1、C/S架构

        优点:安全

        缺点:升级维护成本高,一旦要升级,所有终端要逐个升级

    2、B/S架构

        优点:升级维护、部署简单,只需维护服务端即可

        缺点:安全性有所欠缺

    当下B/S无疑是系统开发中主流模式,在B/S架构流行的前提下,就JavaEE规范下出现了各种各样的Web框架,如Struts2、SpringMVC,而这些框架,无不基于Servlet规范,而Servlet容器中几乎没有不基于HTTP协议的实现(当然本文讨论的不是HTTP协议),在开发中我们处处使用框架,忽略底层细节,在部署时,打包扔进Tomcat、Jetty等web容器,简单方便。那么这些web容器的底层实现又是如何的呢?

    今天本文就展示如何一步步实现一个简单web服务器。

二、需求

    1、实现一个web容器,并模拟登陆操作

    2、可并发

    3、线程安全

三、技术点

    1、Socket编程

    2、Java IO

    3、线程池

    4、ThreadLocal

    5、多线程

    6、NIO、BIO

四、单线程模式实现 

    1、写一个HTML页面,里面有登陆需要的用户名、密码等输入元素,值得一提的是,本例使用的是GET方式提交请求,已经足够说明问题了,必须提到的是GET和POST获取参数的方式是有区别的。

<html>
	<head>
	<meta charset="UTF-8">
	<title>登录页</title>
	</head>
	<body>
		<form action="login" method="get">
			<table>
				<tr>
					<td>用户名:</td>
					<td><input type="text" name="username" ></td>
				</tr>
				<tr>
					<td>密码:</td>
					<td><input type="text" name="password" ></td>
				</tr>
				<tr>
					<td><input type="reset" value="重置"></td>
					<td><input type="submit" value="登录"></td>
				</tr>
			</table>
		</form>
	</body>
</html>

     2、有了HTML页面,那么如何把这个页面读到程序里呢,这个时候就要用到IO了,请看下面的方法:

// 为了方便main方法调用,并声明为static方法
private static String getLoginPage() {
	// 用于存储从文件读出的文件
	StringBuilder sb = new StringBuilder();
	try {
		// 声明一个BufferedReader准备读文件
		BufferedReader htmlReader = new BufferedReader(new InputStreamReader(
				new FileInputStream(new File(System.getProperty("user.dir") + "/webapp/login.html"))));
		String html = null;
		// 按行读取,直到最后一行结束
		while ((html = htmlReader.readLine()) != null) {
			sb.append(html);
		}
		// 关闭
		htmlReader.close();
	} catch (Exception e) {
		e.printStackTrace();
	}
	// 以字符串形式返回读到的HTML页面
	return sb.toString();
}

    3、那么服务端怎么与浏览器端交互,那么需要Socket,请看下面例子:

    (1)、先定义要返回的响应头

// HTTP请求的返回头
private static final StringBuilder responseContent = new StringBuilder();
static {
	responseContent.append("HTTP/1.1 200 OK");
	responseContent.append("Content-Type: text/html; charset=utf-8");
	responseContent.append("Content-Length:%s");
	responseContent.append("Date:%s");
	responseContent.append("Server:This is a simulation web container");
}

    (2)、启动web服务

public static void main(String[] args) {
	try {
		// 监听在8080端口
		ServerSocket serverSocket = new ServerSocket(8080);
		while (true) {
			// 接收请求
			Socket accept = serverSocket.accept();
			// 获取请求中的流
			BufferedReader br = new BufferedReader(new InputStreamReader(accept.getInputStream()));
			// 只读取第一行数据
			String request = br.readLine();
			String result = "";
			if (!"".equals(request) && null != request) {
				String urlAndPrams = request.split(" ")[1];
				if (urlAndPrams.indexOf("/login") != -1) {
					// 获取用户传过来的用户名密码
					String[] params = urlAndPrams.split("\\?")[1].split("&");
					String username = params[0].split("=").length < 2 ? null : params[0].split("=")[1];
					String password = params[1].split("=").length < 2 ? null : params[1].split("=")[1];
					if ("admin".equals(username) && "admin".equals(password)) {
						result = "login success";
					} else {
						result = "username or password error";
					}
				} else {
					// 调用前1面定义的获取登录页方法,获取已经写好的登录页
					result = getLoginPage();
				}
			} else {
				// 调用前面定义的获取登录页方法,获取已经写好的登录页
				result = getLoginPage();
			}
			// 拿到该请求对应Socket的输出流,准备向浏览器写数据了
			PrintWriter printWriter = new PrintWriter(accept.getOutputStream());
			// 把文件长度、日期填回Response字符串中
			printWriter.println(String.format(responseContent.toString(), result.length(), new Date()));
			// 必须有个换行,换行之后才能向浏览器端写要传回的数据(HTTP协议)
			printWriter.println();
			// 写页面数据
			printWriter.println(result);
			printWriter.flush();
			printWriter.close();
			accept.close();
		}
	} catch (Exception e) {
		e.printStackTrace();
	}
}

    (3)、说明

    上一步代码,只读一行数据   拿到用户传过来的参数,其实是可以拿到HTTP请求的所有信息的,如下:

GET / HTTP/1.1
Host: localhost:8080
Connection: keep-alive
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.86 Safari/537.36
Accept-Encoding: gzip, deflate, sdch
Accept-Language: zh-CN,zh;q=0.8
Cookie: _ga=GA1.1.1755609982.1449039124; CNZZDATA1258600948=1953676425-1469624561-%7C1469624561; CNZZDATA1258740907=123042277-1469628131-%7C1469628131; userName=admin

五、执行效果

    1、启动程序,浏览器器敲下http://localhost:8080/,回车,便能看到如下界面

      180249_HGSh_1778239.png

    2、输入用户名:admin,密码:admin,回车,提示登录成功

    

    3、输入错误用户名或密码

     

六、后记

    至此,一个单线程的web服务完成,其中存在如下问题

    1、只能处理单个用户请求,多用户无法同时使用

    2、如何实现并行处理用户请求

    3、并行时线程如何安全

    4、阻塞式的IO效率低,如何改进

    请期待下一篇《自己动手实现简单web容器二》

    快乐源于分享。

   此博客乃作者原创, 转载请注明出处

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 《自己动手写Docker》是一本由架构师嵩天和Docker社区联合编写的开源技术书籍。书中介绍了Docker的基本概念、背景知识和原理,并且通过实践的方式帮助读者了解Docker的具体应用。以下是对这本书的回答: 《自己动手写Docker》这本书是作者嵩天和Docker社区共同编写的一本开源技术书籍。通过这本书,读者可以了解到Docker的基本概念、背景知识和工作原理。 首先,书中介绍了Docker的概念和由来。Docker是一个开源的容器化技术,通过容器化的方式,能够将应用程序和其依赖的资源打包在一起,以便于在任何地方都能够运行。Docker的诞生解决了传统虚拟化技术的运行效率问题,并且能够提供更好的资源利用率和应用程序的可移植性。 其次,书中详细介绍了Docker的工作原理和组成部分。Docker的核心是Docker引擎,它负责创建、运行和管理容器。Docker镜像是Docker的核心概念之一,它是一个只读的模板,包含了运行应用程序所需的全部依赖项。通过Docker镜像,我们可以快速创建和部署应用程序。此外,书中还介绍了容器和镜像的关系、Docker的网络和存储管理方式等重要内容。 最后,书中通过实际操作引导读者理解和使用Docker。通过一些具体的例子和实践,读者可以学习如何构建自己的Docker镜像、创建和运行容器、使用Docker网络和存储等技术。通过这些实践,读者不仅能够清晰地了解Docker的工作机制,还能够掌握一些实际应用场景下的技巧和经验。 总而言之,《自己动手写Docker》这本书通过理论和实践相结合的方式,以简洁明了的语言和实例详细解释了Docker的基本概念、工作原理和实际应用。是一本适合开发者、运维人员和对Docker感兴趣的读者了解和学习Docker的好书籍。 ### 回答2: 《自己动手写docker》是一本以Docker为重点的技术书籍,通过学习本书可以了解Docker的原理和使用方法,掌握构建、部署和管理容器化应用的技巧。 这本书首先介绍了Docker的背景和发展,解释了为什么要使用Docker以及它的优势。接着,书中详细讲解了Docker的核心概念,包括镜像、容器、仓库等,帮助读者理解Docker的基本原理和组成部分。 随后,本书介绍了如何在本地环境搭建Docker,并通过实例演示了如何构建自定义镜像、运行容器以及管理容器的生命周期。这些实例涵盖了常见的应用场景,如部署Web应用、数据库容器化等。 此外,本书还介绍了Docker Swarm和Kubernetes等容器编排工具,让读者能够了解如何使用这些工具高效地管理和扩展容器化应用。 总体来说,《自己动手写docker》是一本很实用的技术书籍,适合那些想要系统学习和掌握Docker技术的开发者和运维人员。无论是初学者还是有一定经验的人,都可以从这本书中获得价值。通过亲自动手实践各种实例,读者能够深入理解Docker的原理和使用方法,提升自己的技术水平。 ### 回答3: 《自己动手写docker》是一本讲述如何编写Docker的书籍。Docker是一种流行的容器化技术,可以将应用程序及其依赖项打包到一个可移植的容器中,从而实现应用程序在不同环境中的快速部署和扩展。 这本书主要讲解了Docker的核心原理和技术实现。首先,它介绍了Linux容器的基本概念和原理,包括命名空间、控制组、镜像和容器等。然后,它详细解释了Docker的架构和基本组件,例如Docker引擎、镜像仓库和网络管理等。 接下来,这本书逐步引导读者编写自己的Docker。它从编写一个简单容器隔离工具开始,逐渐引入更多功能,例如容器的生命周期管理、网络隔离和卷管理等。这样,读者能够逐步了解容器化技术的实现细节。 此外,这本书还介绍了一些与Docker相关的技术和工具,例如容器编排工具Docker Compose和容器编排平台Kubernetes。这些技术可以帮助读者更好地管理和扩展自己的Docker应用程序。 总而言之,《自己动手写docker》是一本编写Docker容器的实践指南。通过学习这本书,读者可以深入了解Docker的原理和实现,并掌握使用Docker构建和管理容器的技能。无论是对于开发人员还是系统管理员来说,这本书都是一本不可多得的学习资源。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值