zoukankan      html  css  js  c++  java
  • 自己模拟的一个简单的web服务器

    首先我为大家推荐一本书:How Tomcat Works。这本书讲的很详细的,虽然实际开发中我们并不会自己去写一个tomcat,但是对于了解Tomcat是如何工作的还是很有必要的。


    • Servlet容器是如何工作的
    servlet容器是一个复杂的系统。不过,一个servlet容器要为一个servlet的请求提供服务,基本上有三件事要做:
    1,创建一个request对象并填充那些有可能被所引用的servlet使用的信息,如参数、头部、 cookies、查询字符串、 URI 等等。一个 request 对象是javax.servlet.ServletRequest 或 javax.servlet.http.ServletRequest 接口的一个实例。
    2,创建一个response对象,所引用的servlet使用它来给客户端发送响应。一个 response对象 javax.servlet.ServletResponse 或 javax.servlet.http.ServletResponse 接口的一个实例。
    3,调用servlet的service方法,并传入 request 和 response 对象。在这里servlet会从 request 对象取值,给response写值。


    • Catalina 架构
    Catalina是一个非常复杂的,并优雅的设计开发出来的软件,同时它也是模块化的。基于“Servlet 容器是如何工作的” 这个问题,我们可以把Catalina看成是由两个主要模块所组成的:连接器(connector) 和容器(container) 。多个连接器对应一个容器,然后放在一起组成一个service对外提供服务。连接器是用来“ 连接” 容器里边的请求的。它的工作是为接收到每一个HTTP请求构造一个request 和 response对象。然后它把流程传递给容器。容器从连接器接收到requset和response对象之后调用servlet的service方法用于响应。值得注意的是,这个描述仅仅是冰山一角而已。这里容器做了相当多事情。例如,在它调用servlet的service方法之前,它必须加载这个servlet,验证用户 (假如需要的话) ,更新用户会话等等。一个容器为了处理这个进程使用了很多不同的模块,这也并不奇怪。例如,管理模块是用来处理用户会话,而加载器是用来加载servlet类等等。


    现在我自己模拟了一个简单的web服务器,如果下面这些代码很容易的理解了的话,那么对于web容器是如何工作的就基本没啥大问题了。我先简单的说下:web服务器就是在某一台虚拟主机上建立一个特定端口的服务器端socket,然后一直等待连接进来,一旦有连接连进来就new一个request和一个response,然后按照HTTP协议去解析request里面的请求参数,然后找到实际的资源文件,通过IO来读写,最后用response也按照HTTP协议去返回给客户端,就这样子就完成了一次请求和相应。下面的代码是入门级的一个web服务器,代码如下:

    package linkin;
    
    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;
    
    /**
     * @author LinkinPark
     * @Date   2015-3-11
     * Http服务器。
     */
    public class HttpServer
    {
    	/**
    	 * WEB_ROOT is the directory where our HTML and other files reside. For this
    	 * package, WEB_ROOT is the "webroot" directory under the working directory.
    	 * The working directory is the location in the file system from where the
    	 * java command was invoked.
    	 * 
    	 */
    	public static final String WEB_ROOT = System.getProperty("user.dir") + File.separator + "webroot";
    	// shutdown command 
    	private static final String SHUTDOWN_COMMAND = "/SHUTDOWN";
    	// the shutdown command received
    	private boolean shutdown = false;
    
    	public static void main(String[] args)
    	{
    		HttpServer server = new HttpServer();
    		server.await();
    		
    	}
    
    	/**
    	 * 服务器的等待方法
    	 */
    	public void await()
    	{
    		ServerSocket serverSocket = null;
    		int port = 8080;
    		try
    		{
    			serverSocket = new ServerSocket(port, 1, InetAddress.getByName("127.0.0.1"));
    		}
    		catch (IOException e)
    		{
    			e.printStackTrace();
    			System.exit(1);
    		}
    		// Loop waiting for a request
    		while (!shutdown)
    		{
    			Socket socket = null;
    			InputStream input = null;
    			OutputStream output = null;
    			try
    			{
    				//如果得到一个请求的话,就分别来建立一个输入流和输出流,然后分别建立一个请求和响应。
    				socket = serverSocket.accept();
    				input = socket.getInputStream();
    				output = socket.getOutputStream();
    				// create Request object and parse
    				Request request = new Request(input);
    				//控制台答应一下请求,然后解析出对应URL里面的请求路径
    				request.parse();
    				// create Response object
    				Response response = new Response(output);
    				response.setRequest(request);
    				//返回响应,发送数据
    				response.sendStaticResource();
    				// Close the socket
    				socket.close();
    				// check if the previous URI is a shutdown command
    				//控制这个循环什么时候结束
    				shutdown = request.getUri().equals(SHUTDOWN_COMMAND);
    			}
    			catch (Exception e)
    			{
    				e.printStackTrace();
    				continue;
    			}
    		}
    	}
    }

    package linkin;
    
    import java.io.IOException;
    import java.io.InputStream;
    
    public class Request
    {
    	private InputStream input;
    	private String uri;
    
    	public Request(InputStream input)
    	{
    		this.input = input;
    	}
    
    	public String getUri()
    	{
    		return uri;
    	}
    
    	
    	//这里控制台打印一下请求的内容,方便我们查看
    	public void parse()
    	{
    		// Read a set of characters from the socket
    		StringBuffer request = new StringBuffer(2048);
    		byte[] buffer = new byte[2048];
    		int i;
    		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 = parseUri(request.toString());
    	}
    
    	//     发过来的请求里面第一行是信息头,类似如下:GET /linkin.html HTTP/1.1
    	private String parseUri(String requestString)
    	{
    		//http://localhost:8080/SHUTDOWN
    		int index1, index2;
    		index1 = requestString.indexOf(' ');
    		if (index1 != -1)
    		{
    			index2 = requestString.indexOf(' ', index1 + 1);
    			if (index2 > index1)
    				//截取2个空格之间的内容,注意这里没有应用名了
    				return requestString.substring(index1 + 1, index2);
    		}
    		return null;
    	}
    }
    

    package linkin;
    
    import java.io.File;
    import java.io.FileInputStream;
    import java.io.IOException;
    import java.io.OutputStream;
    
    /**
     * @author LinkinPark
     * @Date   2015-3-11
     * 
     */
    public class Response
    {
    	private static final int BUFFER_SIZE = 1024;
    	Request request;//请求,主要是用请求URL里面的请求资源路径
    	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
    " + "Content-Type: text/html
    " + "Content-Length: 23
    " + "
    " + "<h1>File 11Not Found</h1>";
    				output.write(errorMessage.getBytes());
    			}
    		}
    		catch (Exception e)
    		{
    			// thrown if cannot instantiate a File object
    			System.out.println(e.toString());
    		}
    		finally
    		{
    			if (fis != null)
    				fis.close();
    		}
    	}
    }

    ok,代码写完了,直接运行HttpServer的主方法启动服务器,现在要访问应用应该输入一个URL,前面代码也看到了,默认的目录就是在自己项目下面的webroot目录下,在这里我丢一个简单的HTML页面进去,用来访问:

    <!DOCTYPE html>
    <html>
      <head>
        <title>linkin.html</title>
    	
        <meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
        <meta http-equiv="description" content="this is my page">
        <meta http-equiv="content-type" content="text/html; charset=UTF-8">
        
        <!--<link rel="stylesheet" type="text/css" href="./styles.css">-->
    
      </head>
      
      <body>
       愿有人陪你一起颠沛流离。。。<br>
      </body>
    </html>
    
    输入URL:http://localhost:8080/linkin.html



    输入URL:http://localhost:8080/SHUTDOWN   后台服务器停止了(因为服务器那段代码走完了,程序结束了)





  • 相关阅读:
    uva 147 Dollars
    hdu 2069 Coin Change(完全背包)
    hdu 1708 Fibonacci String
    hdu 1568 Fibonacci
    hdu 1316 How Many Fibs?
    poj 1958 Strange Towers of Hanoi
    poj 3601Tower of Hanoi
    poj 3572 Hanoi Tower
    poj 1920 Towers of Hanoi
    筛选法——素数打表
  • 原文地址:https://www.cnblogs.com/LinkinPark/p/5233079.html
Copyright © 2011-2022 走看看