(一)一个简单的Web服务器
万丈高楼平地起,首先我们必须了解 超文本传输协议(HTTP) 以后才能够比较清晰的明白web服务器是怎么回事。
1. 浅析Http协议
HTTP是一种协议,允许web服务器和浏览器通过互联网进行来发送和接受数据。它是一种请求和响应协议。客户端请求一个文件而服务器响应请求。HTTP使用可靠的TCP连接--TCP默认使用80端口。第一个HTTP版是HTTP/0.9,然后被HTTP/1.0所替代。正在取代HTTP/1.0的是当前版本HTTP/1.1,它定义于征求意见文档(RFC) 2616,可以从http://www.w3.org/Protocols/HTTP/1.1/rfc2616.pdf下载。
在HTTP中,始终都是客户端通过建立连接和发送一个HTTP请求从而开启一个事务。web服务器不需要联系客户端或者对客户端做一个回调连接。无论是客户端或者服务器都可以提前终止连接。举例来说,当你正在使用一个web浏览器的时候,可以通过点击浏览器上的停止按钮来停止一个文件的下载进程,从而有效的关闭与web服务器的HTTP连接。
2. http的协议连接的过程的说明:
一次HTTP操作称为一个事务,其工作过程可分为四步:
1)首先客户机与服务器需要建立连接。只要单击某个超级链接,HTTP的工作开始。
2)建立连接后,客户机发送一个请求给服务器,请求方式的格式后面有详细的说明。
3)服务器接到请求后,给予相应的响应信息,其格式在后面有详细的说明。
4)客户端接收服务器所返回的信息通过浏览器显示在用户的显示屏上,然后客户机与服务器断开连接。
如果在以上过程中的某一步出现错误,那么产生错误的信息将返回到客户端,有显示屏输出。对于用户来说,这些过程是由HTTP自己完成的,用户只要用鼠标点击,等待信息显示就可以了。
3. HTTP请求
一个HTTP请求包括三个组成部分:
(1) 方法 统一资源标识符(URI) 协议/版本
(2)请求的头部
(3)主体内容
下面是一个HTTP请求的例子:
- POST /examples/default.jsp HTTP/1.1
- Accept: text/plain; text/html
- Accept-Language: en-gb
- Connection: Keep-Alive
- Host: localhost
- User-Agent: Mozilla/4.0 (compatible; MSIE 4.01; Windows 98)
- Content-Length: 33
- Content-Type: application/x-www-form-urlencoded
- Accept-Encoding: gzip, deflate
- lastName=Franks&firstName=Michael
方法 统一资源标识符(URI) 协议 如上面的第一行显示的那样。需要说明的是HTTP 1.1支持7种类型的请求:GET, POST, HEAD, OPTIONS, PUT, DELETE和TRACE。GET和POST是比较常用的。
URI完全指明了一个互联网资源。URI通常是相对服务器的根目录解释的。因此,始终一斜线/开头。统一资源定位器(URL)其实是一种URI(查看http://www.ietf.org/rfc/rfc2396.txt)。
请求的头部包含了关于客户端环境和请求的主体内容的有用信息。例如它可能包括浏览器设置的语言,主体内容的长度等等。每个头部通过一个回车换行符(CRLF)来分隔的。 对于HTTP请求格式来说,头部和主体内容之间有一个回车换行符(CRLF)是相当重要的。CRLF告诉HTTP服务器主体内容是在什么地方开始。
在上面的例子中,主题的内容只有一行:lastName=Franks&firstName=Michael 实体内容在一个典型的HTTP请求中可以很容易的变得更长。
4. HTTP响应
类似于HTTP请求,一个HTTP响应也包括三个组成部分:
(1)方法—统一资源标识符(URI)—协议/版本
(2)响应的头部
(3)主体内容
下面是一个HTTP响应的例子:
- HTTP/1.1 200 OK
- Server: Microsoft-IIS/4.0
- Date: Mon, 5 Jan 2004 13:13:33 GMT
- Content-Type: text/html
- Last-Modified: Mon, 5 Jan 2004 13:13:12 GMT
- Content-Length: 112
- <html>
- <head>
- <title>HTTP Response Example</title>
- </head>
- <body>
- Welcome to Brainy Software
- </body>
- </html>
响应头部的第一行类似于请求头部的第一行。第一行告诉你该协议使用HTTP 1.1,请求成功(200=成功),表示一切都运行良好。 响应头部和请求头部类似,也包括很多有用的信息。响应的主体内容是响应本身的HTML内容。头部和主体内容通过CRLF分隔开来。
5.Socket类
了解了http协议以后,在java对应的编程中java.net 包下面对应的类。利用的和C++一样的套接字的那一套。套接字是网络连接的一个端点。套接字使得一个应用可以从网络中读取和写入数据。放在两个不同计算机上的两个应用可以通过连接发送和接受字节流。为了从你的应用发送一条信息到另一个应用,你需要知道另一个应用的IP地址和套接字端口。
在Java里边,套接字指的是java.net.Socket类。要创建一个套接字,你可以使用Socket类众多构造方法中的一个。其中一个接收主机名称和端口号:
public Socket (java.lang.String host, int port)
在这里主机是指远程机器名称或者IP地址,端口是指远程应用的端口号。例如,要连接yahoo.com的80端口,你需要构造以下的Socket对象:
new Socket ("yahoo.com", 80);
一旦你成功创建了一个Socket类的实例,你可以使用它来发送和接受字节流。要发送字节流,你首先必须调用Socket类的getOutputStream方法来获取一个java.io.OutputStream对象。要发送文本到一个远程应用,你经常要从返回的OutputStream对象中构造一个java.io.PrintWriter对象。要从连接的另一端接受字节流,你可以调用Socket类的getInputStream方法用来返回一个java.io.InputStream对象。 以下的代码片段创建了一个套接字,可以和本地HTTP服务器(127.0.0.1是指本地主机)进行通讯,发送一个HTTP请求,并从服务器接受响应。它创建了一个StringBuffer对象来保存响应并在控制台上打印出来。
- public static void main(String[] args) throws UnknownHostException, IOException, InterruptedException {
- Socket socket = new Socket("127.0.0.1", 80);
- //想要发送自己流你需要的得到socket类返回的一个OutputStream对象
- OutputStream os = socket.getOutputStream();
- boolean autoflush = true;
- //构建一个PrintWriter对象来向输出流中写数据
- PrintWriter out = new PrintWriter( socket.getOutputStream(), autoflush);
- //从连接的另一端接受数据
- BufferedReader in = new BufferedReader( new InputStreamReader( socket.getInputStream()));
- // send an HTTP request to the web server
- out.println("GET /index.jsp HTTP/1.1");
- out.println("Host: localhost:8080");
- out.println("Connection: Close");
- out.println();
- // read the response
- boolean loop = true;
- StringBuffer sb = new StringBuffer(8096);
- while (loop) {
- if ( in.ready() ) {
- int i=0;
- while (i!=-1) {
- i = in.read();
- sb.append((char) i);
- }
- loop = false;
- }
- Thread.sleep(50);
- }
- // display the response to the out console
- System.out.println(sb.toString());
- socket.close();
- }
6. ServerSocket类
Socket类代表一个客户端套接字,即任何时候你想连接到一个远程服务器应用的时候你构造的套接字,现在,假如你想实施一个服务器应用,例如一个HTTP服务器或者FTP服务器,你需要一种不同的做法。这是因为你的服务器必须随时待命,因为它不知道一个客户端应用什么时候会尝试去连接它。为了让你的应用能随时待命,你需要使用java.net.ServerSocket类。这是服务器套接字的实现。
ServerSocket和Socket不同,服务器套接字的角色是等待来自客户端的连接请求。一旦服务器套接字获得一个连接请求,它创建一个Socket实例来与客户端进行通信。 要创建一个服务器套接字,你需要使用ServerSocket类提供的四个构造方法中的一个。你需要指定IP地址和服务器套接字将要进行监听的端口号。通常,IP地址将会是127.0.0.1,也就是说,服务器套接字将会监听本地机器。服务器套接字正在监听的IP地址被称为是绑定地址。服务器套接字的另一个重要的属性是backlog,这是服务器套接字开始拒绝传入的请求之前,传入的连接请求的最大队列长度。 其中一个ServerSocket类的构造方法如下所示:
public ServerSocket(int port, int backLog, InetAddress bindingAddress);
对于这个构造方法,绑定地址必须是java.net.InetAddress的一个实例。一种构造InetAddress对象的简单的方法是调用它的静态方法getByName,传入一个包含主机名称的字符串,就像下面的代码一样。
InetAddress.getByName("127.0.0.1");
下面一行代码构造了一个监听的本地机器8080端口的ServerSocket,它的backlog为1。
new ServerSocket(8080, 1, InetAddress.getByName("127.0.0.1"));
一旦你有一个ServerSocket实例,你可以让它在绑定地址和服务器套接字正在监听的端口上等待传入的连接请求。你可以通过调用ServerSocket类的accept方法做到这点。这个方法只会在有连接请求时才会返回,并且返回值是一个Socket类的实例。Socket对象接下去可以发送字节流并从客户端应用中接受字节流,就像前一节"Socket类"解释的那样。
下面是一个例子能够比较清晰的表示清楚上面所说的东西。
- import java.io.IOException;
- import java.io.InputStream;
- import java.io.OutputStream;
- import java.net.InetAddress;
- import java.net.ServerSocket;
- import java.net.Socket;
- /**
- * @author zhailzh
- * @created 2014-08-22
- * */
- public class HttpServer {
- // response时的文件地址
- public static final String WEB_ROOT = "D:/"+"webroot/";
- // 关闭的标记
- private static final String SHUTDOWN_COMMAND = "/shutDown";
- private boolean shutdown = false;
- public static void main(String[] args) {
- HttpServer server = new HttpServer();
- //服务器端采用的比较简单的循环等待的方式
- server.await();
- }
- public void await() {
- ServerSocket serverSocket = null;
- //弄一个比较特殊的端口
- int port = 12345;
- try {
- serverSocket = new ServerSocket(port, 1, InetAddress.getByName("127.0.0.1"));
- }
- catch (IOException e) {
- e.printStackTrace();
- System.exit(1);
- }
- //循环等待,直到uri传过来关闭的标记
- while (!shutdown) {
- Socket socket = null;
- InputStream input = null;
- OutputStream output = null;
- try {
- // 得到套接字
- socket = serverSocket.accept();
- //输入对象,用来request
- input = socket.getInputStream();
- //输出对象,用来response
- output = socket.getOutputStream();
- // 创建request对象,解析HTTP请求的原始数据�?
- Request request = new Request(input);
- request.parse();
- // 创建response对象,把返回的数据,即是服务器返回的数据塞到输出对象中,
- // 返回客户端数据
- Response response = new Response(output);
- response.setRequest(request);
- response.sendStaticResource();
- socket.close();
- //是否为关闭的标记
- shutdown = request.getUri().equals(SHUTDOWN_COMMAND);
- }
- catch (Exception e) {
- e.printStackTrace();
- continue;
- }
- }
- }
- }
使用到的Response 和 Request
- import java.io.InputStream;
- import java.io.IOException;
- public class Request {
- private InputStream input;
- private String uri;
- public Request(InputStream input) {
- this.input = input;
- }
- public void parse() {
- //打印:请求报文
- StringBuffer request = new StringBuffer(2048);
- int i;
- byte[] buffer = new byte[2048];
- try {
- i = input.read(buffer);
- }
- catch (IOException e) {
- e.printStackTrace();
- i = -1;
- }
- for (int j=0; j<i; j++) {
- request.append((char) buffer[j]);
- }
- System.out.print(request.toString());
- //解析请求报文的uri
- uri = parseUri(request.toString());
- }
- 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;
- }
- public String getUri() {
- return uri;
- }
- }
- import java.io.OutputStream;
- import java.io.IOException;
- import java.io.FileInputStream;
- import java.io.File;
- public class Response {
- private static final int BUFFER_SIZE = 1024;
- Request request;
- OutputStream output;
- public Response(OutputStream output) {
- this.output = output;
- }
- public void setRequest(Request request) {
- this.request = request;
- }
- public void sendStaticResource() throws IOException {
- //构建响应报文,没有按照正确的方式
- byte[] bytes = new byte[BUFFER_SIZE];
- FileInputStream fis = null;
- try {
- File file = new File(HttpServer.WEB_ROOT, request.getUri());
- if (file.exists()) {
- fis = new FileInputStream(file);
- int ch = fis.read(bytes, 0, BUFFER_SIZE);
- while (ch!=-1) {
- output.write(bytes, 0, ch);
- ch = fis.read(bytes, 0, BUFFER_SIZE);
- }
- }
- else {
- // file not found
- String errorMessage = "HTTP/1.1 404 File Not Found\r\n" +
- "Content-Type: text/html\r\n" +
- "Content-Length: 23\r\n" +
- "\r\n" +
- "<h1>File Not Found</h1>";
- output.write(errorMessage.getBytes());
- }
- }
- catch (Exception e) {
- System.out.println(e.toString() );
- }
- finally {
- if (fis!=null)
- fis.close();
- }
- }
- }
验证的效果图:
输入:http://127.0.0.1:12345/index.html
看到的是:D:/webroot/index.html 文件的展示效果
(一)一个简单的Web服务器的更多相关文章
- 自己动手模拟开发一个简单的Web服务器
开篇:每当我们将开发好的ASP.NET网站部署到IIS服务器中,在浏览器正常浏览页面时,可曾想过Web服务器是怎么工作的,其原理是什么?“纸上得来终觉浅,绝知此事要躬行”,于是我们自己模拟一个简单的W ...
- 一个简单的web服务器
写在前面 新的一年了,新的开始,打算重新看一遍asp.net本质论这本书,再重新认识一下,查漏补缺,认认真真的过一遍. 一个简单的web服务器 首先需要引入命名空间: System.Net,关于网络编 ...
- [置顶] 在Ubuntu下实现一个简单的Web服务器
要求: 实现一个简单的Web服务器,当服务器启动时要读取配置文件的路径.如果浏览器请求的文件是可执行的则称为CGI程序,服务器并不是将这个文件发给浏览器,而是在服务器端执行这个程序,将它的标准输出发给 ...
- Tomcat剖析(一):一个简单的Web服务器
Tomcat剖析(一):一个简单的Web服务器 1. Tomcat剖析(一):一个简单的Web服务器 2. Tomcat剖析(二):一个简单的Servlet服务器 3. Tomcat剖析(三):连接器 ...
- 自己模拟的一个简单的web服务器
首先我为大家推荐一本书:How Tomcat Works.这本书讲的很详细的,虽然实际开发中我们并不会自己去写一个tomcat,但是对于了解Tomcat是如何工作的还是很有必要的. Servlet容器 ...
- 利用 nodeJS 搭建一个简单的Web服务器(转)
下面的代码演示如何利用 nodeJS 搭建一个简单的Web服务器: 1. 文件 WebServer.js: //-------------------------------------------- ...
- 《深度解析Tomcat》 第一章 一个简单的Web服务器
本章介绍Java Web服务器是如何运行的.从中可以知道Tomcat是如何工作的. 基于Java的Web服务器会使用java.net.Socket类和java.net.ServerSocket类这两个 ...
- 一个简单的Web服务器-支持Servlet请求
上接 一个简单的Web服务器-支持静态资源请求,这个服务器可以处理静态资源的请求,那么如何处理Servlet请求的呢? 判断是否是Servlet请求 首先Web服务器需要判断当前请求是否是Servle ...
- 一个简单的Web服务器-支持静态资源请求
目标 实现一个简单的Web服务器,能够根据HTTP请求的URL响应对应的静态资源,如果静态资源不存在则响应404. HttpServer 使用ServerSocket实现的一个服务器,request根 ...
- 如何用PHP/MySQL为 iOS App 写一个简单的web服务器(译) PART1
原文:http://www.raywenderlich.com/2941/how-to-write-a-simple-phpmysql-web-service-for-an-ios-app 作为一个i ...
随机推荐
- css链接
css code: a:link{ color:#FF0000; } a:visited{ color:#00FF00; } a:hover { color:#0000FF; } a:active{ ...
- iOS 后台定位被拒注意事项
iOS 后台定位被拒的原因很简单就是没有达到苹果对后台定位的要求. 本地要求: 1.在plist文件中添加字段 "Privacy - Location Always Usage Descri ...
- IE浏览器开启对JavaScript脚本的支持
在IE浏览器的"工具"菜单中选择"internet选项",在弹出命令对话框中选择"安全"选项卡.在该选项卡下的"该区域的安全级别& ...
- Web服务的体系架构
Web简介: Web是WWW(World Wide Web)的简称,又称为万维网,是建立在客户机/服务器上的,以HTML语言和HTML协议为基础,提供面向Internet服务的,有一致用户界面的一种信 ...
- 10个热门IT证书
MCP (微软专家认证) CCNA (思科认证网络支持工程师) MCPD (微软认证开发专家) SCJP (SUN认证Java程序员) CISSP (信息系统安全认证专家) CompTIA A+认证 ...
- UE4学习笔记(三): 为什么使用C++替代UnrealScript?
原文链接: https://forums.unrealengine.com/showthread.php?2574-Why-C-for-Unreal-4&p=16252&viewful ...
- Calendar( 日历)
本节课重点了解 EasyUI 中 Canlendar(日历)组件的使用方法,这个组件不依赖于其他组件.一. 加载方式//class 加载方式<div id="box" cla ...
- Function.prototype.bind
解析Function.prototype.bind 简介 对于一个给定的函数,创造一个绑定对象的新函数,这个函数和之前的函数功能一样,this值是它的第一个参数,其它参数,作为新的函数的给定参数. b ...
- (转)反射发送实战(-)InvokeMember
反射是.net中的高级功能之一,利用反射可以实现许多以前看来匪夷所思的功能,下面是我看了<Programming C#>(O'Reilly)之后对于反射的一点实践,本想直接做个应用程序来说 ...
- SQL Server系统表讲解
1. sysobjects http://www.cnblogs.com/atree/p/SQL-Server-sysobjects.html 2.syscomments http://www.c ...