Web服务器原理

实现一个web服务器

服务器监听一个端口,并读取浏览器的请求信息,从该信息提取出访问的资源(这里为文件名)。并在工作目录下查找是否有该资源,有则输出资源内容,否则返回404

代码实现

代码为自己手动输入,并加入注释帮助理解

package webserver;

import java.io.*;
import java.net.*;

public class WebServer {

    /**
     * web服务器:实现200和404操作
     * 原理:
     * 服务器监听一个端口,并读取浏览器的请求信息,从该信息提取出访问的资源(这里为文件名)。并在工作目录下查找是否有该资源,有则输出资源内容,否则返回404
     * 测试方法:
     * 1、用String path=System.getProperty("user.dir");获取当前的工作目录,并在该目录下放要测试的文件
     * 2、访问127.0.0.1:8080/HelloWorld.html
     */
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        ServerSocket server = null;
        Socket s=null;
        try
        {
        	//参数:(int port, int backlog, InetAddress bindAddr)
            server=new ServerSocket(99,3,InetAddress.getByName("127.0.0.1"));
        }catch(Exception e)
        {
            e.printStackTrace();
        }
        while(true)
        {
            try{
            	/*
            	 * Listens for a connection to be made to this socket and accepts it. 
            	 */
                s=server.accept();
                OutputStream output=s.getOutputStream();
                InputStream input=s.getInputStream();
                
                //通过自实现reques类获取请求参数
                Request request=new Request(input);
                String filename=request.getUri();
                
                //通过自实现response类处理,传入请求资源名,并响应请求信息
                Response response=new Response(output,filename);
                response.response();

            }catch(Exception e)
            {
                e.printStackTrace();
            }
        }
    }

}
package webserver;

import java.io.*;

/*
 * 获取请求的信息,并返回资源
 * 返回请求资源文件名
 */
public class Request {
	
	InputStream input;
	
	public Request(InputStream input){
		this.input = input;
	}
	
	/*
	 * 自写的获取资源URI的方法
	 */
	public String getUri(){
		String content = null;
		//String str = null;
		
		//对需要频繁处理的字符串, 使用StringBuffer类操作
		StringBuffer request = new StringBuffer();
		//作用?将输入流中的存入字节数组,在存入SB
		byte[] buffer = new byte[2048];
		int i = 0;
		
		try{
			i = input.read(buffer);
		}catch (Exception e) {
			// TODO: handle exception
			e.printStackTrace();
			i = -1;
		}
		
		for(int k = 0; k < i; ++k){
			request.append((char)buffer[k]);
		}
		
		content = request.toString();
		
		/*    
	     *以下方法错误!用该返回会使浏览器不断处于请求连接状态
	     *  BufferedReader br=new BufferedReader(new InputStreamReader(input));
	        while((str=br.readLine())!=null)
	        {
	            content=content+str+"\r\n";
	        }
	     */ 
		
		if(content != null){
			return getFileName(content);
		}else{
			return null;
		}
	}
	
	/*
	 * 关键:字符串(即文件名(即资源路径))格式
	 */
	public String getFileName(String content){
		int a, b;
		a = content.indexOf(' ');	//文件名的格式???
		
		if(a != -1){
			/*
			 * Returns the index within this string of the first occurrence of the specified character, 
			 * starting the search at the specified index. 
			 */
			b = content.indexOf('?', a+1);
			if(b == -1){
				b = content.indexOf(' ', a+1);
			}
			
			/*
			 * a+2:beginIndex; b:endIndex
			 */
			return content.substring(a+2, b);
		}
		return null;
	}
}

package webserver;

import java.io.*;

/*
 * 响应并处理请求信息
 */

public class Response {
	public OutputStream output;
	public String fileName;
	
	//static final定义常量,不可更改
	private static final int BUFFER_SIZE = 1024;
	
	public Response(OutputStream output, String fileName){
		this.output = output;
		this.fileName = fileName;
	}
	
	public void response() throws IOException{
		//"user.dir" 是固定的工作目录???
		String path = System.getProperty("user.dir");
		byte[] buffer = new byte[BUFFER_SIZE];
		
		int ch;
		FileInputStream fis = null;
		
		if(path != null &&fileName != null){
			/*
			 * 内置方法:Creates a new File instance from a parent pathname string and a child pathname string. 
			 * request获得用户请求文件名,结合文件目录,生成File类
			 */
			File file = new File(path, fileName);
			System.out.println(path + " " + fileName);
			String str = "";
			
			/*
			 * 模拟web服务器响应,生成响应头等信息,并返回html文件
			 */
			/*
			 * 如果文件存在,则返回请求结果,打印请求信息
			 */
			if(file.exists()){
				System.out.println("find");
				//FileInputStream读取文件内容,并使用OutputStream输出
				fis = new FileInputStream(file);
				str = "HTTP/1.1 200 OK \r\n" +
				"Content-Type: text/html\r\n" + 
				"\r\n";
				output.write(str.getBytes());
				
				//循环读取,存入缓冲字节数组,并输出
				ch = fis.read(buffer);
				while(ch != -1){
					output.write(buffer, 0 , ch);
					ch = fis.read(buffer, 0, BUFFER_SIZE);
				}
			}else{
				/*
				 * 如果文件不存在——没有找到请求资源,响应头状态返回404信息
				 */
				System.out.println("not find");
				str = "HTTP/1.1 404 File Not Found \r\n" +
				 "Content-Type: text/html\r\n" + 
				 "Content-Length: 100\r\n" + 
				 "/r/n" + 
				 "<h1>404 File Not Found!</h1>";
				
				output.write(str.getBytes());
			}
		}
		output.close();
	}
}

测试方法:

  1. 在目录下存入指定资源html文件以便访问,HTML文件实现如下
<!DOCTYPE html>
<html>
	<head>
		<title>hello world</title>
	</head>
	<body>
		<h1>hello world!</h1>
	</body>
</html>

  1. 通过打印达到user.dir为 “D:\工作环境\SimpleServer”
    不用新建一个user.dir文件夹,直接把html文件放在同一个目录下

  2. 运行应用,在浏览器中输入

运行结果:

  1. 输入正确资源名


    2.输入不正确资源名
  • 在谷歌浏览器下,输入不存在资源名会一直加载而没有显示404(为什么??)
  • 在IE浏览器下成功显示404

出现的问题:

  1. 多次操作会在套接字连接时出现空指针异常,了解到是端口占用问题

  • 解决方法:

    1. 关闭端口占用:
      在命令行中输入netstat -ano 找到被占用端口号


    打开任务管理器,找到端口号对应进程ID对应的进程,点击结束进程即可解决占用

    1. 更换new ServerSocket(99,3,InetAddress.getByName("127.0.0.1"));里的端口号
  1. 始终404错误!:打印可知搜索路径为,D:\工作环境\SimpleServer
    • 所以解决方案为,将html资源文件放在“ D:\工作环境\SimpleServer”该目录下(我之前同java文件放在src目录下)

扩展——网络编程:

  • Socket编程步骤
    服务器端创建ServerSocket对象,调用accept方法返回Socket对象
    客户端创建Socket对象,通过端口连接到服务器
    客户端、服务器端都使用Socket中的getInputStream方法和getOutputStream方法获得输入流和输出流,进一步进行数据读写操作

(InetAddress用来描述主机地址;
Socket用来创建两台主机之间的连接;
ServerSocket用来侦听来自客户端的请求;
Socket通常称作“套接字”,通常通过“套接字”向网络发出请求或者应答网络请求。)

由该程序理解Web服务器的工作原理



posted @ 2019-03-15 09:09  张叉叉儿  阅读(192)  评论(0编辑  收藏  举报