HTTP长连接和短连接 + Websocket

HTTP协议与TCP/IP协议的关系

HTTP的长连接和短连接本质上是TCP长连接和短连接。HTTP属于应用层协议,在传输层使用TCP协议,在网络层使用IP协议。IP协议主要解决网络路由和寻址问题,TCP协议主要解决如何在IP层之上可靠的传递数据包,使在网络上的另一端收到发端发出的所有包,并且顺序与发出顺序一致。TCP有可靠,面向连接的特点。

长连接短连接操作过程

短连接的操作步骤是:

建立连接——数据传输——关闭连接…建立连接——数据传输——关闭连接

长连接的操作步骤是:

建立连接——数据传输 …(保持连接)… 数据传输——关闭连接

长连接和短连接的优点和缺点

长连接可以省去较多的TCP建立和关闭的操作,减少浪费,节约时间。但是如果连接数比较多,会给服务器造成比较大压力。

短连接对于服务器来说管理较为简单,存在的连接都是有用的连接,不需要额外的控制手段。但如果客户请求频繁,将在TCP的建立和关闭操作上浪费时间和带宽。

什么时候用长连接,短连接

长连接多用于操作频繁,点对点的通讯,而且连接数不能太多情况。例如:数据库的连接用长连接, 如果用短连接频繁的通信会造成socket错误,而且频繁的socket 创建也是对资源的浪费。

而像WEB网站的http服务一般都用短链接,因为长连接对于服务端来说会耗费一定的资源,而像WEB网站这么频繁的成千上万甚至上亿客户端的连接用短连接会更省一些资源,如果用长连接,而且同时有成千上万的用户,如果每个用户都占用一个连接的话,那可想而知吧。所以并发量大,但每个用户无需频繁操作情况下需用短连好。

推送服务

推送技术,又名反向AJAX,指的是一种基于Internet,将由中心或发布者发出消息传输给用户的技术。与之相对的是拉取(参见AJAX),这种情况下请求是由用户或客户端主动发起的。

所有的推送功能都是基于长连接的基础上的。

维护任何一个长连接都需要心跳机制,客户端发送一个心跳给服务器,服务器给客户端一个心跳应答,这样就形成客户端服务器的一次完整的握手,这个握手是让双方都知道他们之间的连接是没有断开,客户端是在线的。如果超过一个时间的阈值,客户端没有收到服务器的应答,或者服务器没有收到客户端的心跳,那么对客户端来说则断开与服务器的连接重新建立一个连接,对服务器来说只要断开这个连接即可。

在智能手机上的长连接心跳和在Internet上的长连接心跳有什么不同的目的呢?原因就在于智能手机使用的是移动无线网络,那么我们在讲长连接之前我们首先要了解无线移动网络的特点。

简单来说就是由于网络运营商为了节省信道资源,会在一台移动终端一段时间没有通信时关闭其关的链路。为了应对这种情况,移动应用不得不以远高于正常频率来发送心跳用以维护推送的长连接。

Android系统的推送和iOS的推送

ios长连接是由系统来维护的,iOS上的所有应用上的推送都是先将消息推送到苹果的服务器然后将苹果服务器通过这个系统级别的长链接推送到手机终端上,这样做的好处是:

  1. 在手机终端始终只要维护一个长连接即可,而且由于这个长链接是系统级别的不会出现被杀死而无法推送的情况。
  2. 省电,不会出现每个应用都各自维护一个自己的长连接。
  3. 安全,只有在苹果注册的开发者才能够进行推送,等等。

Android的长连接是由每个应用各自维护的,而谷歌退出的和苹果类似的推送框架在国内无法使用的。

推送的常见方式

  1. 轮询(polling)
  2. 持久链接(binding)
  3. SMS方式

WebSocket

WebSocket API是下一代客户端-服务器的异步通信方法。该通信取代了单个的TCP套接字,使用ws或wss协议,可用于任意的客户端和服务器程序。

WebSocket API最伟大之处在于服务器和客户端可以在给定的时间范围内的任意时刻,相互推送信息。WebSocket并不限于以Ajax(或XHR)方式通信,因为Ajax技术需要客户端发起请求,而WebSocket服务器和客户端可以彼此相互推送信息;XHR受到域的限制,而WebSocket允许跨域通信。

WebSocket的客户端API

// 创建一个Socket实例,ws表示WebSocket协议
var socket = new WebSocket('ws://localhost:8080');

// 打开socket
socket.onopen = function(event){

    // 发送一个初始化消息
    socket.send('hello socket')

    // 监听消息
    socket.onmessage = function(event){
        console.log('client reveived a meessage', event)
    }

    // 监听Socket的关闭
    socket.oncolse = function(event){
        console.log('Client notified socket has closed', event)
    }

    // 关闭Socket
    socket.closd()
}

一个模拟聊天室websocket的例子:

在实际应用中,socket服务器端的代码可以是Python,node.js,java,php。在这里使用http://www.websocket.org/网站提供的,socket 
服务端。协议地址为:ws://echo.websocket.org/

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Websocket</title>
  <style>
    * {
      margin: 0;
      padding: 0;
    }

    #outer {
      box-sizing: border-box;
       60%;
      height: 400px;
      padding: 20px 200px;
      background: black;
      font-size: 20px;
      color: #F8F8F8;
      overflow-y: auto;
    }

    #message {
       300px;
      height: 20px;
      margin-top: 10px;
      margin-left: 100px;
      margin-right: 50px;
    }

    p {
      margin: 10px 0;
    }

    button {
      background: none;
      outline: none;
      border: 1px solid #0b9ce1;
      padding: 5px 15px;
      margin: 0 8px;
      border-radius: 5px;
    }
  </style>
</head>
<body>
<div id="outer">
  <p>123</p>
  <p>123</p>
</div>
<label><input id="message"></label>
<button id="send">发送</button>
<button id="quite">断开</button>
</body>
<script src="../lib/jquery.min.js"></script>
<script>
  function log(msg) {
    $('#outer').append('<p>' + msg + '</p>').scrollTop(500)
  }

  function send() {
    var text = $('#message').val();
    if (!text) {
      alert('Message can not be empty!');
      return;
    }
    try {
      socket.send(text);
      log('Send : ' + text);
      $('#message').val('').focus()
    }
    catch (e) {
      log(e)
    }
  }

  function quit() {
    log("Bye!");
    socket.close();
    socket = null;
  }

  var socket = null;
  function init() {
    //声明host注意:是ws协议
    var host = "ws://echo.websocket.org/";

    try {
      //新创建一个socket对象
      socket = new WebSocket(host);
      //将连接的状态信息显示在log
      log('WebSocket - status ' + socket.readyState);

      //监听打开连接
      socket.onopen = function (msg) {
        log("Welcome - status " + this.readyState);
      };
      //监听当接收信息时触发匿名函数
      socket.onmessage = function (msg) {
        log("Received : " + msg.data);
      };
      //关闭连接
      socket.onclose = function (msg) {
        log("Disconnected - status " + this.readyState);
      };
    }
    catch (e) {
      log(ex)
    }
    $('#message').focus();
  }

  $(function () {
    init();

    $("#message").keypress(function (e) {
      if (event.keyCode === 13) {
        send();
      }
    });

    $('#send').click(function () {
      send();
    });

    $('#quite').click(function () {
      quite();
    });

    $("#outer").scroll(function(){
     $(this)
    })
  })

</script>
</html>

更多关于socket io等的例子和方案看这里

参考

原文地址:https://www.cnblogs.com/barrywxx/p/8569380.html