zoukankan      html  css  js  c++  java
  • 03 请求响应原理及HTTP协议

    1. 服务器端基础概念

    1.1 网站的组成

    网站应用程序主要分为两大部分:客户端和服务器端。

    客户端:在浏览器中运行的部分,就是用户看到并与之交互的界面程序。使
    用HTML、CSS、JavaScript构建。

    服务器端:在服务器中运行的部分,负责存储数据和处理应用逻辑。

    1.2 Node网站服务器

    能够提供网站访问服务的机器就是网站服务器,它能够接收客户端的请求,能够对请求做出响应。

    1.3 IP地址

    互联网中设备的唯一标识。

    IP是Internet Protocol Address的简写,代表互联网协议地址.

    1.4 域名

    由于IP地址难于记忆,所以产生了域名的概念,所谓域名就是平时上网所使用的网址。

    http://www.itheima.com => http://124.165.219.100/

    虽然在地址栏中输入的是网址, 但是最终还是会将域名转换为ip才能访问到指定的网站服务器。

    1.5 端口

    端口是计算机与外界通讯交流的出口,用来区分服务器电脑中提供的不同的服务。

    1.6 URL

    统一资源定位符,又叫URL(Uniform Resource Locator),是专为标识Internet网上资源位置而设的一种编址方式,我们平时所说的网页地址指的即是URL。

    url的组成

    传输协议://服务器IP或域名:端口/资源所在位置标识

    http://www.baidu.cn/news/20181018/09152238514.html

    http:超文本传输协议,提供了一种发布和接收HTML页面的方法。

    1.7 开发过程中客户端和服务器端说明

    在开发阶段,客户端和服务器端使用同一台电脑,即开发人员电脑。

    客户端 (浏览器)

    服务器端(Node)

    本机域名:localhost
    本地IP :127.0.0.1

    2.创建web服务器

    浏览器输入localhost:3000 访问

      // 引用系统模块
     const http = require('http');
      // 创建web服务器
     const app = http.createServer();
      // 当客户端发送请求的时候
     app.on('request', (req, res) => {
            //  响应
           res.end('<h1>hi, user</h1>');
     });
      // 监听3000端口
     app.listen(3000);
     console.log('服务器已启动,监听3000端口,请访问 localhost:3000')
    

    3.HTTP协议

    3.1 HTTP协议概念

    超文本传输协议(英文:HyperText Transfer Protocol,缩写:HTTP)规定了如何从网站服务器传输超文本到本地浏览器,它基于客户端服务器架构工作,是客户端(用户)和服务器端(网站)请求和应答的标准。

    3.2 报文

    在HTTP请求和响应的过程中传递的数据块就叫报文,包括要传送的数据和一些附加信息,并且要遵守规定好的格式。

    3.3 请求报文

    1. 请求方式(request method)

    GET 请求数据

    POST 发送数据

     app.on('request', (req, res) => {
         req.headers  // 获取请求报文
         req.url      // 获取请求地址
         req.method   // 获取请求方法
     });
    

    演示

    // 创建网站服务器的模块
    const http = require('http')
    
    // app 对象就是网站服务对象
    const app = http.createServer()
    
    // 当客户端有请求来的时候
    app.on('request', (req, res) => {
    
        // 获取请求报文信息
        // req.headers
        console.log(req.headers);
        console.log(req.headers['accept']);
    
        // 获取请求地址
        // req.url
        // console.log(req.url);
        if (req.url == '/index' || '/') {
            res.end('index')
        } else if (req.url == '/list') {
            res.end('list')
        } else {
            res.end('not found')
        }
    
        // 获取请求方式
        // req.method
    
        if (req.method == "POST") {
            res.end('post')
        } else if (req.method == "GET") {
            res.end('get')
    
        }
    
    })
    
    // 监听端口
    app.listen(8080)
    console.log("网站服务器启动成功");
    

    3.4 响应报文

    1.HTTP状态码

    200 请求成功
    404 请求的资源没有被找到
    500 服务器端错误
    400 客户端请求有语法错误

    2.内容类型

    text/html
    text/css
    application/javascript
    image/jpeg
    application/json

     app.on('request', (req, res) => {
         // 设置响应报文
         res.writeHead(200, {         '
    	Content-Type': 'text/html;charset=utf8‘
         });
     });
    

    4. HTTP请求与响应处理

    4.1 请求参数

    客户端向服务器端发送请求时,有时需要携带一些客户信息,客户信息需要通过请求参数的形式传递到服务器端,比如登录操作。

    4.2 GET请求参数

    • 参数被放在浏览器地址中 http://localhost:3000/?name=zhangsan&age=20

    • 参数获取需要借助系统模块url,url模块用来处理url地址

      // 创建网站服务器的模块
      const http = require('http')

      // 处理url地址
      const url = require('url')

      // app 对象就是网站服务对象
      const app = http.createServer()

      // 当客户端有请求来的时候
      app.on('request', (req, res) => {
      // 设置相应报文
      res.writeHead(200, {
      // 返回类型
      'content-type': 'text/html;charset=utf8' // 纯文本 设置中文编码
      })

        // url解析
        console.log('url   ' + req.url);
        // 第一个参数解析URl,第二个参数查询参数转换为对象形式保存
        let { query, pathname } = url.parse(req.url, true)
        console.log(query.name);
        console.log(query.age);
      
        // 获取请求地址
        // req.url
        // console.log(req.url);
        if (pathname == '/index' || pathname == '/') {
            res.end('<h2>来到index</h2>')
        } else if (pathname == '/list') {
            res.end('list')
        } else {
            res.end('not found')
        }
      
        // 获取请求方式
        // req.method
      
        if (req.method == "POST") {
            res.end('post')
        } else if (req.method == "GET") {
            res.end('get')
        }
      

      })

      // 监听端口
      app.listen(8080)
      console.log("网站服务器启动成功");

    4.3 POST请求参数

    • 参数被放置在请求体中进行传输

    • 获取POST参数需要使用data事件和end事件

    • 使用querystring系统模块将参数转换为对象格式

    // 创建网站服务器的模块
    const http = require('http')

    // app 对象就是网站服务对象
    const app = http.createServer()

    // 处理请求参数模块
    const querystring = require('querystring')

    html文件

    <!DOCTYPE html>
    <html lang="en">
    
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>Document</title>
    </head>
    
    <body>
        <!-- method:指定当前表单提交方式 -->
        <!-- action: 指定当前表单提交的地址 -->
        <form action="http://localhost:8080" method="post">
            <input type="text" name="username" id="">
            <input type="password" name="password" id="">
            <input type="submit" name="" id=""></form>
    </body>
    
    </html>
    

    js文件

    // 当客户端有请求来的时候
    app.on('request', (req, res) => {
        // post参数是通过事件的方式接收
        // data 请求参数传递的时候触发data事件
        // end 请求参数传递完成的时候触发end事件
    
        let postParams = ''
    
        // params:当前传递过来的参数
        req.on('data', params => {
            postParams += params
        })
        req.on('end', () => {
            // 参数转换字符串对象
            console.log(querystring.parse(postParams));
        })
    
        // 给客户端一个响应内容
        res.end('ok')
    })
    
    // 监听端口
    app.listen(8080)
    console.log("网站服务器启动成功");
    

    4.4 路由

    http://localhost:3000/index
    http://localhost:3000/login
    路由是指客户端请求地址与服务器端程序代码的对应关系。简单的说,就是请求什么响应什么。

    4.5 静态资源

    服务器端不需要处理,可以直接响应给客户端的资源就是静态资源,例如CSS、JavaScript、image文件。

    4.6 动态资源

    相同的请求地址不同的响应资源,这种资源就是动态资源。

    // 1.创建网站服务器的模块
    const http = require('http')
    const url = require('url')
    const path = require('path')
    const fs = require('fs')
    const mime = require('mime')
    
    
    // 2.创建网站服务器
    const app = http.createServer()
    
    // 3.监听请求事件
    app.on('request', (req, res) => {
    
        // 获取用户请求路径
        let pathname = url.parse(req.url).pathname
        pathname = pathname == '/' ? '/default.html' : pathname
    
        // 根据路径返回资源类型
        let type = mime.getType(pathname)
    
        // __dirname:获取当前文件所在路径
        // 将用户请求路径转换为实际的服务器硬盘路径
        resPath = path.join(__dirname, 'pulic', pathname)
            // res.end(resPath)
        console.log(resPath);
    
    
        // 读取文件
        fs.readFile(resPath, (error, result) => {
            // 如果文件读取出错
            if (error != null) {
                res.writeHead(404, {
                    'content-type': 'text/html;charset=utf8'
                })
                res.end('文件读取失败');
                return;
            }
            res.writeHead(200, {
                'content-type': type
            })
    
            res.end(result);
        })
    
    })
    
    // 监听端口 
    app.listen(8080)
    console.log("网站服务器启动成功");
    

    5. Node.js异步编程

    5.1 同步API, 异步API

    同步API:只有当前API执行完成后,才能继续执行下一个API

    console.log('before'); 
    console.log('after');
    

    异步API:当前API的执行不会阻塞后续代码的执行

     // 路径拼接
     const public = path.join(__dirname, 'public');
     // 请求地址解析
     const urlObj = url.parse(req.url);
     // 读取文件
     fs.readFile('./demo.txt', 'utf8', (err, result) => {
         console.log(result);
     });
    

    5.2 同步API, 异步API的区别( 获取返回值 )

    同步API可以从返回值中拿到API执行的结果, 但是异步API是不可以的

    function getMsg() {
        setTimeout(function() {
            return {
                msg: 'hello'
            }
        }, 2000)
    }
    // 执行这个的时候 没过两秒拿不到返回值 
    const msg = getMsg()
    console.log(msg); 
    

    5.3 回调函数

    怎么才能拿到异步API的返回值,自己定义函数让别人去调用。

    function getMsg(callback) {
        setTimeout(function() {
            callback({
                msg: 'hello'
            })
        }, 2000)
    }
    getMsg(function(data) {
        console.log(data);
    })
    

    5.4 5.5 同步API, 异步API的区别(代码执行顺序)

    • 同步API从上到下依次执行,前面代码会阻塞后面代码的执行

    演示

    for (var i = 0; i < 100000; i++) { 
        console.log(i);
    }
    console.log('for循环后面的代码');
    
    • 异步API不会等待API执行完成后再向下执行代码

    演示

    console.log('代码开始执行'); 
    setTimeout(() => { console.log('2秒后执行的代码')}, 2000);
    setTimeout(() => { console.log('"0秒"后执行的代码')}, 0); 
    console.log('代码结束执行');
    

    先执行同步,后执行异步(先执行即将开始的)

    5.6 Node.js中的异步API

    1

    fs.readFile('./demo.txt', (err, result) => {});
    

    2

    var server = http.createServer();
    server.on('request', (req, res) => {});
    

    如果异步API后面代码的执行依赖当前异步API的执行结果,但实际上后续代码在执行的时候异步API还没有返回结果,这个问题要怎么解决呢?

    fs.readFile('./demo.txt', (err, result) => {});
    console.log('文件读取结果');
    
    需求:依次读取A文件、B文件、C文件
    
    const fs = require('fs')
    // 回调地狱
    fs.readFile('./1.txt', 'utf8', (err, res1) => {
        console.log(res1);
        fs.readFile('./2.txt', 'utf8', (err, res2) => {
            console.log(res2);
            fs.readFile('./3.txt', 'utf8', (err, res3) => {
                console.log(res3);
            })
        })
    })
    

    5.8 Promise

    Promise出现的目的是解决Node.js异步编程中回调地狱的问题。

    十分臃肿

    const fs = require('fs')
        // 回调地狱
        // fs.readFile('./1.txt', 'utf8', (err, res1) => {
        //     console.log(res1);
        //     fs.readFile('./2.txt', 'utf8', (err, res2) => {
        //         console.log(res2);
        //         fs.readFile('./3.txt', 'utf8', (err, res3) => {
        //             console.log(res3);
        //         })
        //     })
        // })
    
    function p1() {
        return new Promise((reslove, reject) => {
            fs.readFile('./1.txt', 'utf8', (err, res) => {
                reslove(res)
            })
        })
    }
    
    function p2() {
        return new Promise((reslove, reject) => {
            fs.readFile('./2.txt', 'utf8', (err, res) => {
                reslove(res)
            })
        })
    }
    
    function p3() {
        return new Promise((reslove, reject) => {
            fs.readFile('./3.txt', 'utf8', (err, res) => {
                reslove(res)
            })
        })
    }
    
    p1().then((r1) => {
        console.log(r1);
        return p2()
    }).then((r2) => {
        console.log(r2);
        return p3()
    }).then((r3) => {
        console.log(r3);
    })
    

    5.9 异步函数

    异步函数是异步编程语法的终极解决方案,它可以让我们将异步代码写成同步的形式,让代码不再有回调函数嵌套,使代码变得清晰明了。

    const fn = async () => {};
    async function fn () {}
    

    async关键字

    1. 普通函数定义前加async关键字 普通函数变成异步函数
    2. 异步函数默认返回promise对象
    3. 在异步函数内部使用return关键字进行结果返回 结果会被包裹的promise对象中 return关键字代替了resolve方法
    4. 在异步函数内部使用throw关键字抛出程序异常
    5. 调用异步函数再链式调用then方法获取异步函数执行结果
    6. 调用异步函数再链式调用catch方法获取异步函数执行的错误信息
    

    await关键字

    1. await关键字只能出现在异步函数中
    2. await promise await后面只能写promise对象 写其他类型的API是不不可以的
    3. await关键字可是暂停异步函数向下执行 直到promise返回结果
    

    异步函数

    // 1.在普通函数定义的前面加上async关键字
    // 2.异步函数默认的返回值是promise对象
    // 3.在异步函数内部使用throw关键字进行错误抛出
    // async function fn() {
    //     throw '发生了一些错误' // 抛出错误异常
    //     return 123
    
    //     // await关键字
    //     // 1.它只能出现在异步函数中
    //     // 2.await promise 它可以暂停异步函数的执行 等待promise对象返回结果再向下执行
    
    // }
    
    // fn().then(function(data) {
    //     console.log(data);
    // }).catch(function(err) {
    //     console.log(err);
    // })
    

    异步函数的基本用法

    async function p1() {
        return 'p1'
    }
    
    async function p2() {
        return 'p2'
    }
    
    async function p3() {
        return 'p3'
    }
    
    async function run() {
        let r1 = await p1() // p1函数执行有返回结果再向下执行
        let r2 = await p2()
        let r3 = await p3()
        console.log(r1);
        console.log(r2);
        console.log(r3);
    
    }
    
    run()
    

    重写读取文件异步任务

    const fs = require('fs')
    const promisify = require('util').promisify
        // fs.readFile函数不能返回 promise 对象  所以要给他包装个promisify,能让异步API返回promise 对象
    const readFile = promisify(fs.readFile)
    
    async function run() {
        let r1 = await readFile('./1.txt', 'utf8')
        let r2 = await readFile('./2.txt', 'utf8')
        let r3 = await readFile('./3.txt', 'utf8')
        console.log(r1);
        console.log(r2);
        console.log(r3);
    }
    run()
  • 相关阅读:
    hdu 6702 ^&^ 位运算
    hdu 6709 Fishing Master 贪心
    hdu 6704 K-th occurrence 二分 ST表 后缀数组 主席树
    hdu 1423 Greatest Common Increasing Subsequence 最长公共上升子序列 LCIS
    hdu 5909 Tree Cutting FWT
    luogu P1588 丢失的牛 宽搜
    luogu P1003 铺地毯
    luogu P1104 生日
    luogu P1094 纪念品分组
    luogu P1093 奖学金
  • 原文地址:https://www.cnblogs.com/xujinglog/p/13115825.html
Copyright © 2011-2022 走看看