• 自己模拟的一个简单的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   后台服务器停止了(因为服务器那段代码走完了,程序结束了)





  • 相关阅读:
    怎么样通过API函数获取tooltip的内容(请高手帮忙)
    Ajax控件之Accordion控件学习
    C#中结构与类的区别
    C# ref and out 区别
    java 将PDF文件的首页提取为图片
    围住浮动元素(消除浮动)的三种方法
    myeclipse unable to install breakpoint
    C#调用C++编写的COM DLL
    高手教你玩53kf在线客服的指定客服功能
    让IIS支持Flv的详细设置方法
  • 原文地址:https://www.cnblogs.com/LinkinPark/p/5233079.html
Copyright © 2020-2023  润新知