一个简单的Web服务器(Web服务器如何工作)
程序员文章站
2022-05-08 22:51:44
...
HTTP允许Web服务器和浏览器通过Internet发送并接收数据,是一种基于“请求——响应”的协议。HTTP使用的是可靠的TCP连接,TCP协议默认使用TCP 80端口。在HTTP中,总是由客户端通过建立连接并发送HTTP请求来初始化一个事务的。Web服务器端并不负责联系客户端或建立一个到客户端的回调连接。HTTP协议是无状态的协议。 套接字是网络连接的端点。套接字使应用程序可以从网络中读取数据,可以向网络中写入数据。不同计算机上的两个应用程序可以通过连接发送或接收字节流,一次达到相互通信的目的。要创建一个套接字,需要指明其主机名和端口号,如下:
- new Socket("www.baidu.com", 80);
new Socket("www.baidu.com", 80);
一旦创建了Socket的实例,就可以使用该实例发送或接收字节流。要发送字节流,需要调用Socket类的getOutputStream()方法获取一个java.io.OutputStream对象。要发送文本到远程应用程序,通常需要使用返回的OutputStream对象创建一个java.io.PrintWriter对象。若想要从连接的另一端接收字节流,需要调用Socket类的getInputStream()方法,该方法会返回一个java.io.InputStream对象。
如果要想实现一个服务器应用程序,需要使用服务器套接字的实现(java.net.ServerSocket)。ServerSocket类和Socket类不同,服务器套接字要等待来自客户端的连接请求,当服务器套接字收到了连接请求以后,他会创建一个Socket实例来处理与客户端的通信。要创建一个服务器套接字,需要指定其主机名和监听的端口号。服务器套接字还有一个重要属性是backlog,表示在服务器在拒绝接受传入的请求之前,传入的连接请求的最大队列长度。ServerSocket类的实例化方式之一如下:
- new ServerSocket(8080, 1, InetAddress.getByName("127.0.0.1"));
new ServerSocket(8080, 1, InetAddress.getByName("127.0.0.1"));
创建了ServletSocket实例后,可以使其等待传入的连接请求,该链接请求会通过服务器套接字侦听的端口上绑定地址传入。这些工作可以通过调用ServerSocket类的accept方法完成。只有当接收到连接请求时,该方法才会返回,返回值是一个Socket实例。
Web服务器应用程序实例如下:
1.Request类:
- package com.demo.tomcat;
- import java.io.IOException;
- import java.io.InputStream;
- publicclass Request {
- /**
- * 从Socket中读取数据的缓冲区大小
- */
- privatestaticfinalint SOCKET_BUFFER_SIZE = 1024;
- /**
- * 输入流(通过与客户端通信Socket中获取)
- */
- private InputStream input;
- /**
- * Http请求的URI
- */
- private String uri;
- /**
- * Constructor
- * @param input 输入流
- */
- public Request(InputStream input){
- this.input = input;
- }
- /**
- * 解析Http请求的原始数据(例如,GET请求index.html的请求行为:"GET /index.html HTTP/1.1)
- */
- publicvoid parse(){
- //读取Socket中的数据
- StringBuffer sb = new StringBuffer(SOCKET_BUFFER_SIZE);
- int i;
- byte[] buf = newbyte[SOCKET_BUFFER_SIZE];
- try {
- i = input.read(buf);
- for(int j = 0; j < i; j++){
- sb.append((char) buf[j]);
- }
- } catch (IOException e) {
- e.printStackTrace();
- }
- //解析URI
- if(sb.length() > 0){
- uri = parseURI(sb.toString());
- }
- }
- /**
- * 获取请求的URI
- * @param requestStr
- * @return
- */
- private String parseURI(String requestStr){
- if(requestStr == null || requestStr.length() <= 0){
- returnnull;
- }
- int index1,index2;
- index1 = requestStr.indexOf(' ');
- if(index1 != -1){
- index2 = requestStr.indexOf(' ', index1 + 1);
- if(index2 > index1){
- //截取字符串并返回
- return requestStr.substring(index1 + 1, index2);
- }
- }
- returnnull;
- }
- public String getUri() {
- return uri;
- }
- }
package com.demo.tomcat;
import java.io.IOException;
import java.io.InputStream;
public class Request {
/**
* 从Socket中读取数据的缓冲区大小
*/
private static final int SOCKET_BUFFER_SIZE = 1024;
/**
* 输入流(通过与客户端通信Socket中获取)
*/
private InputStream input;
/**
* Http请求的URI
*/
private String uri;
/**
* Constructor
* @param input 输入流
*/
public Request(InputStream input){
this.input = input;
}
/**
* 解析Http请求的原始数据(例如,GET请求index.html的请求行为:"GET /index.html HTTP/1.1)
*/
public void parse(){
//读取Socket中的数据
StringBuffer sb = new StringBuffer(SOCKET_BUFFER_SIZE);
int i;
byte[] buf = new byte[SOCKET_BUFFER_SIZE];
try {
i = input.read(buf);
for(int j = 0; j < i; j++){
sb.append((char) buf[j]);
}
} catch (IOException e) {
e.printStackTrace();
}
//解析URI
if(sb.length() > 0){
uri = parseURI(sb.toString());
}
}
/**
* 获取请求的URI
* @param requestStr
* @return
*/
private String parseURI(String requestStr){
if(requestStr == null || requestStr.length() <= 0){
return null;
}
int index1,index2;
index1 = requestStr.indexOf(' ');
if(index1 != -1){
index2 = requestStr.indexOf(' ', index1 + 1);
if(index2 > index1){
//截取字符串并返回
return requestStr.substring(index1 + 1, index2);
}
}
return null;
}
public String getUri() {
return uri;
}
}
2.Response类:
- package com.demo.tomcat;
- import java.io.File;
- import java.io.FileInputStream;
- import java.io.IOException;
- import java.io.OutputStream;
- publicclass Response {
- /**
- * 读取数据缓冲区大小
- */
- privatestaticfinalint BUFFER_SIZE = 1024;
- /**
- * 请求
- */
- private Request request;
- /**
- * 输出流
- */
- private OutputStream output;
- /**
- * Constructor
- * @param output 输出流
- */
- public Response(OutputStream output){
- this.output = output;
- }
- publicvoid setRequest(Request request){
- this.request = request;
- }
- /**
- * 发送一个静态资源到浏览器
- * @throws Exception
- */
- publicvoid sendStaticResource() throws Exception {
- //读取静态资源
- byte[] bytes = newbyte[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 {
- 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 (IOException e) {
- e.printStackTrace();
- } finally {
- fis.close();
- }
- }
- }
package com.demo.tomcat;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStream;
public class Response {
/**
* 读取数据缓冲区大小
*/
private static final int BUFFER_SIZE = 1024;
/**
* 请求
*/
private Request request;
/**
* 输出流
*/
private OutputStream output;
/**
* Constructor
* @param output 输出流
*/
public Response(OutputStream output){
this.output = output;
}
public void setRequest(Request request){
this.request = request;
}
/**
* 发送一个静态资源到浏览器
* @throws Exception
*/
public void sendStaticResource() throws Exception {
//读取静态资源
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 {
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 (IOException e) {
e.printStackTrace();
} finally {
fis.close();
}
}
}
3.HttpServer类:
- package com.demo.tomcat;
- import java.io.File;
- import java.io.IOException;
- import java.io.InputStream;
- import java.io.OutputStream;
- import java.net.InetAddress;
- import java.net.ServerSocket;
- import java.net.Socket;
- publicclass HttpServer {
- /**
- * 访问的根路径
- */
- publicstaticfinal String WEB_ROOT = System.getProperty("user.dir") + File.separator + "webroot";
- /**
- * 关闭服务请求命令
- */
- publicstaticfinal String SHUTDOWN_COMMAND = "/SHUTDOWN";
- /**
- * 是否关闭服务
- */
- private Boolean shutdown = false;
- publicstaticvoid main(String[] args){
- HttpServer httpServer = new HttpServer();
- httpServer.await();
- }
- /**
- * 从accept方法返回的Socket中获取InputStream对象和OutputStream对象
- */
- publicvoid await(){
- //创建ServerSocket对象
- ServerSocket serverSocket = null;
- //监听端口
- int port = 8080;
- try {
- //实例化ServerSocket对象
- serverSocket = new ServerSocket(port, 1, InetAddress.getByName("127.0.0.1"));
- } catch (IOException e) {
- e.printStackTrace();
- System.exit(1);
- }
- while(!shutdown){
- Socket socket = null;
- InputStream input = null;
- OutputStream output = null;
- try {
- socket = serverSocket.accept();
- input = socket.getInputStream();
- output = socket.getOutputStream();
- //创建并解析request
- Request request = new Request(input);
- request.parse();
- //创建response,发送静态资源给浏览器
- Response response = new Response(output);
- response.setRequest(request);
- response.sendStaticResource();
- //关闭socket
- socket.close();
- //是否关闭服务
- shutdown = request.getUri().equals(SHUTDOWN_COMMAND);
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- }
- }
package com.demo.tomcat;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
public class HttpServer {
/**
* 访问的根路径
*/
public static final String WEB_ROOT = System.getProperty("user.dir") + File.separator + "webroot";
/**
* 关闭服务请求命令
*/
public static final String SHUTDOWN_COMMAND = "/SHUTDOWN";
/**
* 是否关闭服务
*/
private Boolean shutdown = false;
public static void main(String[] args){
HttpServer httpServer = new HttpServer();
httpServer.await();
}
/**
* 从accept方法返回的Socket中获取InputStream对象和OutputStream对象
*/
public void await(){
//创建ServerSocket对象
ServerSocket serverSocket = null;
//监听端口
int port = 8080;
try {
//实例化ServerSocket对象
serverSocket = new ServerSocket(port, 1, InetAddress.getByName("127.0.0.1"));
} catch (IOException e) {
e.printStackTrace();
System.exit(1);
}
while(!shutdown){
Socket socket = null;
InputStream input = null;
OutputStream output = null;
try {
socket = serverSocket.accept();
input = socket.getInputStream();
output = socket.getOutputStream();
//创建并解析request
Request request = new Request(input);
request.parse();
//创建response,发送静态资源给浏览器
Response response = new Response(output);
response.setRequest(request);
response.sendStaticResource();
//关闭socket
socket.close();
//是否关闭服务
shutdown = request.getUri().equals(SHUTDOWN_COMMAND);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
注意:需要在项目所在的路径下添加一个名为"webroot"的文件夹,该文件夹下放置你要访问你的文件(如test.txt),输入相应的URI(如http://lcoalhost:8080/test.txt),即可访问到该资源文件。
参考:深入剖析Tomcat第一章