Web运作原理探析

Web运作原理探析

  • 浏览器向Web服务器请求访问一个HTML文档的过程

    首先url输入,然后通过域名找到IP,先从本地hosts文件中查找,如果本地和指定DNS服务器中的缓存都没有的话, 就要请求DNS服务器从根域开始查询;得到 IP 后开始与目的主机进行三次握手来建立TCP连接;连接建立后发送HTTP/HTTPS请求,请求目标通常是一个HTML文件,服务器收到请求后,将发回一个 HTTP 响应报文,内容包括相关响应头和 HTML 正文。当浏览器收到服务器响应后,开始解析并渲染页面;传输完后与目的主机四次挥手来断开TCP连接。

  • 浏览器和Web服务器各自具有的功能

    浏览器:发送HTTP请求,并接收HTTP响应;解析并展示HTNL文档;解析并运行JavaScript代码

    Web服务器:接收HTTP请求,并发送Http响应;动态加载并执行程序代码;

  • HTTP请求数据的基本格式

    HTTP规定,HTTP请求由如下3部分组成:

    • 请求方法、URI和HTTP版本。

    • 请求头(Request Header)。

    • 请求正文(Request Content)。

在这里插入图片描述

  • Java套接字创建HTTP客户与服务器程序

    • 实现一个简单的HTTP服务器,接收客户程序发出的HTTP请求,把它打印到控制台,然后解析HTTP请求,并向客户端发回相应的HTTP响应
    package server;
    
    import java.io.InputStream;
    import java.io.OutputStream;
    import java.net.ServerSocket;
    import java.net.Socket;
    
    public class HTTPServer {
        public static void main(String[] args) {
            int port;
            ServerSocket serverSocket;
            try {
                port = Integer.parseInt(args[0]);
            }catch (Exception e){
                System.out.println("port = 8080 (默认)");
                port = 8080;
            }
    
            try {
                //创建一个服务器
                serverSocket = new ServerSocket(port);
                System.out.println("服务器正在监听端口:" + serverSocket.getLocalPort());
    
                while (true){//服务器再一个无限循环中不断接收来做客户端的TCP连接请求
                    try {
                        //accpet()接收一个套接字中已建立的连接
                       final Socket socket = serverSocket.accept();
                        System.out.println("建立了与客户的一个新的连接,该客户的地址为:"
                                + socket.getInetAddress() + ":" + socket.getPort());
                    service(socket);
                    } catch (Exception e){
                        e.printStackTrace();
                        System.out.println("客户端请求的资源不存在!!!");
                    }
                }//#while
            } catch (Exception e){
                e.printStackTrace();
            }
    
        }
        /**响应客户的HTTP请求*/
        public static void service(Socket socket) throws Exception{
            /**读取HTTP请求信息*/
            InputStream socketIn = socket.getInputStream();//获取输入流
            Thread.sleep(500);//睡眠500毫秒,等待HTTP请求
            //available()这个方法可以在读写操作前先得知数据流里有多少个字节可以读取
            int size = socketIn.available();
            //缓存字节的数组
            byte[] buffer = new byte[size];
            socketIn.read(buffer);
            String request = new String(buffer);
            System.out.println("==== 请求数据 ====");
            System.out.println(request);//打印请求数据
    
            /**解析HTTP请求*/
            //获得HTTP请求的第一行
            int endIndex = request.indexOf("\r\n");
            if (endIndex == -1){
                endIndex = request.length();
            }
            String firstLineOfRequest = request.substring(0,endIndex);
            String[] parts = firstLineOfRequest.split(" ");//第一行是以空格隔开的,所以以空格拆分
            String requestMethod = "";
            String uri = "";
            String protocol = "";
            if (parts.length >= 2){
                requestMethod = parts[0];
                uri = parts[1]; //获取请求中的uri
                protocol = parts[2];
            }
    
            /** 确定响应正文类型 */
            String contentType;
            if (uri.indexOf("html") != -1 || uri.indexOf("htm") != -1){
                contentType = "text/html";
            } else if (uri.indexOf("jpg") != -1 || uri.indexOf("jpeg") != -1){
                contentType = "image/jpeg";
            } else if (uri.indexOf("gif") != -1){
                contentType = "image/gif";
            } else {
                contentType = "application/octet-stream";//字节流类型
            }
    
            /**创建HTTP响应结果*/
            //HTTP响应的第一行
            String responseFirstLine = "HTTP/1.1 200 OK\r\n";
            //HTTP响应头
            String responseHeader = "ContentType:" + contentType+"\r\n\r\n";
            //获得读取响应正文数据的输入流
            InputStream in = HTTPServer.class.getResourceAsStream("root/" + uri);
    
            /**发送响应结果*/
            OutputStream socketOut = socket.getOutputStream();//获取输出流
            //发送HTTP响应的第一行
            socketOut.write(responseFirstLine.getBytes());
            //发送响应头
            socketOut.write(responseHeader.getBytes());
            //发送HTTP响应正文
            int len = 0; //读取的字节长度,为-1时表示没有
            buffer = new byte[128];
            while ((len = in.read(buffer)) != -1){
                socketOut.write(buffer,0,len);
            }
    
            Thread.sleep(1000);//睡眠1秒,等待客户端接收HTTP响应结果
            socket.close();//关闭TCP连接
        }
    }
    
    
    • 一个简单的HTTP客户程序,它以GET方式向HTTP服务器发送HTTP请求,然后把接收的HTTP响应结果打印到控制台

      package client;
      
      import java.io.InputStream;
      import java.io.OutputStream;
      import java.net.Socket;
      
      public class HTTPClient {
          public static void main(String args[]){
              //确定HTTP请求的uri
              String uri="index.htm";
              if(args.length !=0)uri=args[0];
      
              doGet("localhost",8080,uri); //按照GET请求方式访问HTTPServer
          }
      
          /** 按照GET请求方式访问HTTPServer */
          public static void doGet(String host,int port,String uri){
              Socket socket=null;
      
              try{
                  socket=new Socket(host,port); //与HTTPServer建立FTP连接
              }catch(Exception e){e.printStackTrace();}
      
              try{
                  /*创建HTTP请求 */
                  StringBuffer sb=new StringBuffer("GET "+uri+" HTTP/1.1\r\n");
                  sb.append("Accept: */*\r\n");
                  sb.append("Accept-Language: zh-cn\r\n");
                  sb.append("Accept-Encoding: gzip, deflate\r\n");
                  sb.append("User-Agent: HTTPClient\r\n");
                  sb.append("Host: localhost:8080\r\n");
                  sb.append("Connection: Keep-Alive\r\n\r\n");
      
                  /*发送HTTP请求*/
                  OutputStream socketOut=socket.getOutputStream(); //获得输出流
                  socketOut.write(sb.toString().getBytes());
      
                  Thread.sleep(2000); //睡眠2秒,等待响应结果
      
                  /*接收响应结果*/
                  InputStream socketIn=socket.getInputStream(); //获得输入流
                  int size=socketIn.available();
                  byte[] buffer=new byte[size];
                  socketIn.read(buffer);
                  System.out.println(new String(buffer)); //打印响应结果
      
              }catch(Exception e){
                  e.printStackTrace();
              }finally{
                  try{
                      socket.close();
                  }catch(Exception e){e.printStackTrace();}
              }
          } //#doGet()
      }
      
    • ​ 结果如图

在这里插入图片描述
在这里插入图片描述

  • HTTP响应结果的基本格式

    HTTP响应也由3部分组成:

    • HTTP版本、状态代码和描述

    • 响应头(Response Header)

    • 响应正文(Response Content)

在这里插入图片描述

  • 浏览器以及Web服务器端对HTTP请求参数的处理过程

    • 对于GET请求方式,请求参数跟在URI的后面

      GET /servlet/HelloServlet?username=zs&password=1234 HTTP/1.1
      
    • 对于POST请求方式,请求参数将作为HTTP请求的正文部分

      POST /servlet/HelloServlet HTTP/1.1
      Accpet:image/gif,image/jpeg,*/*
      ...
      Cookie:style=default
      
      username=Tom&password=123456
      
  • 浏览器向Web服务器端上传文件的过程

    HTTP响应正文部分最常见的是HTML文档,此外还可以是其他任意格式的数据,如图片和声音文件中的数据。同样HTTP请求正文部分也不仅仅是字符串格式的参数,也可以是其他任意格式的数据。Web服务器将特定的文件数据放在响应正文中就能向浏览器发送,浏览器也可以将任意格式的数据放入请求正文中发送给Web服务器。

    <from></from>的重要属性:
    

    method属性:指定请求方式为“POST”,那么表单的数据就会放到HTTP的请求正文部分。
    enctype:用于指定表单数据的MIME类型,取为 “MULITPART/FORM-DATA”表示表单数据为复合类型数据,包含多个子部分。
    action:指定用户提交数据是的URI。

    <html>
    <head>
    <title>HelloWorld</title>
    </head>
    <body >
      <form name="uploadForm" method="POST"
        enctype="MULTIPART/FORM-DATA"
        action="servlet/UploadServlet">
        <table>
          <tr>
            <td><div align="right">File Path:</div></td>
            <td><input type="file" name="filedata" /> </td>
          </tr>
          <tr>
            <td><input type="submit" name="submit" value="upload"></td>
            <td><input type="reset" name="reset" value="reset"></td>
          </tr>
        </table>
      </form>
    </body>
    </html>
    

    通过上述表单上传文件FromClient.txt。
    文件中的内容:

    Data1 in FromClient.txt
    Data2 in FromClient.txt
    Data3 in FromClient.txt
    Data4 in FromClient.txt
    

    以下是上传上述文件后获取到的HTTP请求内容:

    POST /servlet/UploadServlet HTTP/1.1     //HTTP请求的第一行
    Host: localhost:8080					 //HTTP请求头开始
    Connection: keep-alive
    Origin: http://localhost:8080
    Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryqDu5O5PyBZGVIqQF //设定边界取值
    .....
    Cookie: yiShengHuShiZhan_tm=   			//HTTP请求头最后一行
    			空行,以下部分是HTTP的请求正文
    ------WebKitFormBoundaryqDu5O5PyBZGVIqQF  //边界
    Content-Disposition: form-data; name="filedata"; filename="FromClient.txt"  //文件头部分开始
    Content-Type: text/plain
    空行 以下是正文部分
    Data1 in FromClient.txt
    Data2 in FromClient.txt
    Data3 in FromClient.txt
    Data4 in FromClient.txt
    空行
    ------WebKitFormBoundaryqDu5O5PyBZGVIqQF  //边界
    Content-Disposition: form-data; name="submit" 	//提交按钮的头
    	空行
    upload		//提交按钮的正文部分即提交按钮的参数值
    ------WebKitFormBoundaryqDu5O5PyBZGVIqQF--   //边界。
    

    以上HTTP请求的正文部分为复合类型,它包含练个子部分:文件部分和提交按钮部分。浏览器会随机产生一个字符串形式的边界(boundary),在HTTP请求头中,一下代码设定了边界的取值:

    Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryqDu5O5PyBZGVIqQF
    

    HTTP请求正文部分的各个子部分用边界来进行分割。每个子部分由头和正文部分组成。包含符合类型的HTML表单数据的HTTP请求组成结构解析这类型的HTTP请求时,先确定边界值根据请求头中 Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryqDu5O5PyBZGVIqQF。然后根据边界值来定位文件部分,然后再定位到文件的正文部分。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值