美文网首页
网络编程http/https/http2/websocket

网络编程http/https/http2/websocket

作者: LM林慕 | 来源:发表于2019-11-06 21:13 被阅读0次

此文项目代码:https://github.com/bei-yang/I-want-to-be-an-architect
码字不易,辛苦点个star,感谢!

引言


此篇文章主要涉及以下内容:

  1. HTTP协议
  2. http服务使用
  3. 前后端通信技术ajaxwebsocket
  4. 常见web问题:跨域、session
  5. 实现一个爬虫程序
  6. TCP协议 - 实现一个即时通讯IM
  7. https
  8. http2

HTTP协议


ISO七层网络协议

const http = require("http");
const fs = require("fs");

http
  .createServer((req, res) => {
    const { method, url } = req;
    if (method === "GET" && url === "/") {
      fs.readFile("./index.html", (err, data) => {
        res.setHeader("Content-Type", "text/html");
        res.end(data);
      });
    } else if (method === "GET" && url === "/users") {
      res.setHeader("Content-Type", "application/json");
      red.end(JSON.stringify([{ name: "tom", age: 20 }]));
    }
  })
  .listen(3000);
  • 请求接口,index.html
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<script>
axios
.get("/users")
   .then(res => res.data)
   .then(users => console.log(users));
</script>
  • 跨域:浏览器同源策略引起的接口调用问题
// 1.创建http-server-2.js,使用端口3001
server.listen(3001);
// 2.index.html中请求位于3000服务器的端口
axios.get("http://localhost:3000/users")
  1. 浏览器抛出跨域错误
  1. 常用解决方案

    • JSONP(JSON with Padding),前端+后端方案,绕过跨域

    前端构造script标签请求指定URL(由script标签发出的GET请求不受同源策略限制),服务器返回一个函数执行语句,该函数名称通常由查询参callback的值决定,函数的参数为服务器返回的json数据。该函数在前端执行后即可获取数据。

    • 代理服务器

    请求同源服务器,通过该服务器转发请求至目标服务器,得到结果再转发给前端。前端开发中测试服务器的代理功能就是采用的该解决方案,但是最终发布上线时如果web应用和接口服务器不在一起仍会跨域。

    • CORS(Cross Origin Resource Share) - 跨域资源共享,后端方案,解决跨域。
      原理:cors是w3c规范,真正意义上解决跨域问题。它需要服务器对请求进行检查并对响应头做相应处理,从而允许跨域请求。
      具体实现:

      1. 响应简单请求:动词为get/post/head,没有自定义请求头,Content-Type是application/x-www-form-urlencoded,multipart/form-data或text/plain之一,通过添加以下响应头解决:
      res.setHeader('Access-Control-Allow-Origin', 'http://localhost:3001')
      
      1. 响应preflight请求,需要响应浏览器发出的options请求(预检请求),并根据情况设置响应头:
      else if (method == "OPTIONS" && url == "/users") {
       res.writeHead(200, {
        "Access-Control-Allow-Origin":       "http://localhost:3001",
        "Access-Control-Allow-Headers": "X-Token,Content-Type",
        "Access-Control-Allow-Methods": "PUT"
      });
       res.end();
      }
      

      该案例中可以通过添加自定义的x-token请求头使请求变为preflight请求

      // index.html
      axios.get("http://localhost:3000/users", {headers:{'X-
      Token':'jilei'}})
      

      则服务器需要允许x-token,若请求为POST,还传递了参数:

      // index.html
      axios.post("http://localhost:3000/users", {foo:'bar'}, {headers:
      {'X-Token':'jilei'}})
      // http-server.js
      else if ((method == "GET" || method == "POST") && url ==
      "/users") {}
      

      则服务器还需要允许content-type请求头

      1. 如果要携带cookie信息,则请求变为credential请求:
      // 预检options中和/users接口中均需添加
      res.setHeader('Access-Control-Allow-Credentials', 'true');
      

实现一个爬虫


原理:服务器模拟客户端发送请求到目标服务器获取页面内容并解析,获取其中关注部分的数据。

const originRequest = require("request");
const cheerio = require("cheerio");
const iconv = require("iconv-lite");

function request(url, callback) {
  const options = {
    url: url,
    encoding: null
  };
  originRequest(url, callback);
}
// 爬取电影天堂的电影名
for (let i = 100553; i < 100563; i++) {
  const url = `https://www.dy2018.com/i/${i}.html`;
  request(url, function(err, res, body) {
    const html = iconv.decode(body, "gb2312");
    const $ = cheerio.load(html);
    console.log($(".title_all h1").text());
  });
}

实现一个实时聊天程序


  • Socket实现
    原理:Net模块提供一个异步API能够创建基于流的TCP服务器,客户端与服务器建立连接后,服务器可以获得一个全双工Socket对象,服务器可以保存Socket对象列表,在接收某客户端消息时,推送给其他客户端。
const net = require("net");
const chatServer = net.createServer(),
  clientList = [];
chatServer.on("connection", function(client) {
  client.write("Hi!\n");
  clientList.push(client);
  client.on("data", function(data) {
    clientList.forEach(v => {
      v.write(data);
    });
  });
});
chatServer.listen(9000);

通过Telnet连接服务器

telnet localhost 9000
  • http实现
    原理:客户端通过ajax方式发送数据给HTTP服务器,服务器缓存消息,其他客户端通过轮询方式查询最新数据并更新列表。

    • 观察http协议

curl -v http://www.baidu.com

<html>
<head>
  <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
  <script src="https://unpkg.com/axios/dist/axios.min.js"></script>
</head>
<body>
  <div id="app">
    <input v-model="message">
    <button v-on:click="send">发送</button>
    <button v-on:click="clear">清空</button>
    <div v-for="item in list">{{item}}</div>
  </div>
  <script>
    const host = 'http://localhost:3000'
    var app = new Vue({
      el: '#app',
      data: {
        list: [],
        message: 'Hello Vue!'
     },
      methods: {
        send: async function () {
          let res = await axios.post(host + '/send', {
            message: this.message
         })
          this.list = res.data
       },
        clear: async function () {
          let res = await axios.post(host + '/clear')
          this.list = res.data
       }
     },
      mounted: function () {
        setInterval(async () => {
          const res = await axios.get(host + '/list')
          this.list = res.data
       }, 1000);
     }
   });
  </script>
</body>
</html>
const express = require("express");
const app = express();
const bodyParser = require("body-parser");
const path = require("path");
app.use(bodyParser.json());
const list = ["ccc", "bbbb"];
app.get("/chat", (req, res) => {
  res.sendFile(path.resolve("./chat.html"));
});
app.get("/list", (req, res) => {
  res.end(JSON.stringify(list));
});
app.post("/send", (req, res) => {
  list.push(req.body.message);
  res.end(JSON.stringify(list));
});
app.post("/clear", (req, res) => {
  list.length = 0;
  res.end(JSON.stringify(list));
});

app.listen(3000);
  • Socket.IO实现
// src/im/index.js
const io = require("socket.io")(server);
io.on("connection", socket => {
  console.log("io connection...");
  socket.on("chat", msg => {});
});

app.post("/send", (req, res) => {
  list.push(req.body.message);
  // socketIO 增加
  io.emit("chat", list);
  res.end(JSON.stringify(list));
});

// src/im/index.html
mounted:function(){
  //http轮询
    // setInterval(async () => {
  //   const res = await axios.get(host + '/list')
  //   this.list = res.data
 // }, 1000);

  //websocket方式
  const socket = io(host)
  socket.on('chat',list=>{
    this.list=list
  })
}

Socket.IO库特点:

  • 源于HTML5标准
  • 支持优雅降级
    • WebSocket
    • WebSocket over Flash
    • XHR Polling
    • XHR Multipart Streaming
    • Forever Iframe
    • JSONP Polling

Https(安全文章中再聊)


  • 创建证书
# 创建私钥
openssl genrsa -out privatekey.pem 1024
# 创建证书签名请求
openssl req -new -key privatekey.pem -out certrequest.csr
# 获取证书,线上证书需要经过证书授证中心签名的文件;下面只创建一个学习使用证书
openssl x509 -req -in certrequest.csr -signkey privatekey.pem -out certificate.pem
# 创建pfx文件
openssl pkcs12 -export -in certificate.pem -inkey privatekey.pem -out
certificate.pfx

Http2(优化文章中再聊)


  • 多路复用 - 雪碧图、多域名CDN、接口合并
    1. 官方演示:https://http2.akamai.com/demo
    2. 多路复用允许同时通过单一的HTTP/2连接发起多重的请求-响应消息;而HTTP/1.1协议中,浏览器客户端在同一时间,针对同一域名下的请求有一定数量限制。超过限制数目的请求会被阻塞。
  • 首部压缩
    1. http/1.x的header由于cookie和user agent很容易膨胀,而且每次都要重复发送。http/2使用encoder来减少需要传输的header大小,通讯双方各自cache一份header fields表,既避免了重复header的传输,又减小了需要传输的大小。高效的压缩算法可以很大的压缩header,减少发送包的数据从而降低延迟。
  • 服务端推送
    • 在HTTP/2中,服务器可以对客户端的一个请求发送多个响应。举个例子,如果一个请求请求的是index.html,服务器很可能会同时响应index.html、logo.jpg以及css和js文件,因为它知道客户端会用到这些东西。这相当于在一个HTML文档内集合了所有的资源。

你的赞是我前进的动力

求赞,求评论,求转发...

相关文章

网友评论

      本文标题:网络编程http/https/http2/websocket

      本文链接:https://www.haomeiwen.com/subject/zhjybctx.html