• HTTP的长连接和短连接——Node上的测试


        本文主要从实践角度介绍长、短连接在TCP层面的表现,借助Node.JS搭建后台服务,使用WinHTTP、Ajax做客户端请求测试,最后简单涉及WebSocket。

        关键字:长连接、短连接、Node.JS、WebSocket.

        一两年前,在理论上对长短连接做了学习,那时的技能以客户端为主,所以也止步于客户端和网络抓包,两年来后台技术渐有把握,打算从前到后的实践一遍。如对理论有不理解的,可以先google/百度 一下,或者看看这篇偏理论的介绍:HTTP的长连接和短连接

    1 短连接的表现

    先写个简单的server看看短链接的效果,在8124端口上创建WEB服务,给客户端返回Hello World ,然后断开连接,代码:
    /**                                                                                                                                                                                         
     *  * @file 短连接测试服务
     *  *  启动: node short.js
     *  * @authoer cswuyg
     *   */
    var os = require('os');
    var http = require('http');
    
    var server = http.createServer(function(req, res) {
        console.log(req.connection.remoteAddress + ':' + req.connection.remotePort);
        res.writeHead(200, {'Content-Type': 'text/plain'});
        res.end('Hello World
    ');
        res.destroy();  // destroy immediately
    }).listen(8124);
    
    console.log('start ' + os.hostname() + ':8124');

    在浏览器访问:http://hostname:8124

    使用WireShark抓包:

     
    注意到了吗?上面的FIN 是由8124端口,也就是我们的WEB服务发出来的,及时的销毁了连接。多次刷新浏览器页面,每次都是不同的客户端端口发起请求,不会复用旧连接。

    2 长连接的表现

    (1)WEB服务代码:
    /**                                                                                                                                                                                                               
     *  * @file 长连接测试服务器
     *  * 启动: node persistent.js
     *  * ./a.html 为同目录下的ajax请求测试文件
     *  * @authoer cswuyg
     *   */
    var os = require('os');
    var http = require('http');
    var fs = require('fs');
    
    var server = http.createServer(function(req, res) {
        if (/^/a.html/.test(req.url)) {
            fs.createReadStream('a.html').pipe(res);
        } else {
            console.log(req.connection.remoteAddress + ':' + req.connection.remotePort);
            res.writeHead(200, {'Content-Type': 'text/plain'});
            res.end('Hello World
    ');
        }
    }).listen(8124);
    server.setTimeout(0);   //设置不超时,所以服务端不会主动关闭连接
    
    console.log('start ' + os.hostname() + ':8124');
    (2)使用WinHTTP测试:
    如果我们使用WinHTTP去获取数据,在这份代码上做修改:https://github.com/cswuyg/win_net/blob/master/winhttp_get_file/winhttp_get_test/http_get/http_get.cpp
    代码节选:
    xx:
        const wchar_t* lpszAcceptedType[] = {L"*/*", NULL};
        request_handle_ = ::WinHttpOpenRequest(connect_handle, L"GET", url_path_.c_str(), L"HTTP/1.1", WINHTTP_NO_REFERER, lpszAcceptedType, 0);
        if (request_handle_ == NULL)
        {
            return FALSE;
        }
        DWORD time_out = 5000;
        ::WinHttpSetOption(request_handle_, WINHTTP_OPTION_CONNECT_TIMEOUT, &time_out, sizeof(DWORD));
        ::WinHttpSendRequest(request_handle_, WINHTTP_NO_ADDITIONAL_HEADERS, 0, WINHTTP_NO_REQUEST_DATA, 0, 0, 0);
        ::WinHttpReceiveResponse(request_handle_, NULL);
        Recv();
    goto xx;  //测试代码,不惧goto~
    代码思路就是connect handle打开之后,后面不同url的请求(当然是同域名下的)可以复用这个connect handle。

    使用WireShark抓包,效果:

    注:WinHTTP connect handle复用这是浏览器开发者的事情,WEB开发者不会关心到它。

    (3)如果我们使用js代码来测试长连接效果呢?
    使用ajax,
    a.html:
    <script type="text/javascript" src="http://libs.baidu.com/jquery/2.1.4/jquery.min.js"></script>
    <script>     
     
    function callNode() {
        $.ajax({ 
            cache : false,
            url : 'http://xxxxxyouhost:8124',
            data : {},
            success : function(response, code, xhr) {
                console.log(response);
                console.log(code);                                                                                                                                                                                    
                callNode();
            }    
        });      
    };           
    callNode();  
    </script>    
    在浏览器访问:http://hostname:8124/a.html
    后台上看效果:
     
    刷刷刷的显示出客户端的多次请求都是一样的IP和端口。

    3、WebSocket

    从WEB开发者最直接的感受来说,终于,可以不用轮询了,只需要使用WebSocket就可以实现全双工通信,要给客户端推送数据更方便了。
    从HTTP优化的角度来说:短连接发展到长连接,省略了重复的三次握手,WebSocket的出现,则把HTTP HEADER也省略掉了。而WebSocket,它不是HTTP协议。
     
    WebSocket在底层的表现就是保持连接不断开,客户端既能接收数据也能发送数据,服务端既能接收数据也能发送数据。这在socket层面并没有什么特别,socket的接收缓存区和发送缓存区是分开的,既可以接收也可以发送。对于从socket层面写服务和应用的人来说,WebSocket并不稀奇,譬如,写一个聊天室,连接可以一直不断开的发送和接收数据。对于做WEB开发的朋友,则有很大不同,之前HTTP请求,每一次传输数据,不管是不是用了长连接都是需要有头部信息,而现在有了WebSocket,一个连接可以一直保持着,不发出FIN包,可以收发数据,是长连接,且多次请求不需要有多个头部信息。(一般的http请求,5分钟无响应后,chrome浏览器主动断开连接),
     
    每一次HTTP请求都要走一遍完整的HTTP协议,这让那些需要实时传输数据的朋友很辛苦,虽然HTTP1.1带来了长连接,不用每一次都重新三次握手建立连接,省略了握手的浪费,但是长连接里每一次请求的的HTTP头都是一样的,也还是很浪费,要把长连接里的多次请求的HTTP头也消灭掉,消灭掉之后的协议叫做WebSocket,这个名字也很写实,就像是把Socket层面的东西暴露给上层应用了。
     
    浏览器为了支持WebSocket,可能会做哪些事情呢? 1是去掉超时关闭连接,这种协议就不用主动关闭了;2给后端发数据时,非首次请求不发出HTTP HEADER,后面的多次数据传输都不用再起HTTP连接了;3可以随时接收发送数据,之前都是需要客户端发起请求,然后服务端回应这样的模式来传输数据。 另外,还会做校验、编码之类的工作。
    在做实际开发的时候,有浏览器支持还不够,后台也需要支持,Node上有socket.io模块可用。
    在使用WebSocket的时候,要注意,后台可能经过一个个的代理,代理可能不认识WebSocket协议,可能会有超时断开连接的逻辑,这会导致长连接无法保持。 
    使用WebSocket协议做聊天室,抓包:
    说明:1、WebSocket借助了HTTP协议;2、这个tcp stream不会终止,它的传输内容会随着客户端跟服务端的通信而增长。
     
     WebSocket连接建立时的抓包:
     
     
     从HTTP1.0、长连接、WebSocket的发展,是不是可以看出:从无状态发展到有状态?无状态在做分布式/高并发的时候相当重要,但在明明就是有状态的场景下强制使用无状态就坏事了。
     
    文中所涉及Node和html代码见github:http_persistent_connection.
    如有错误请指正.
     
    学习参考:http://www.plhwin.com/2014/05/28/nodejs-socketio/
     
  • 相关阅读:
    内存泄露检测工具之DMalloc
    五年后你在何方
    程序员技术练级攻略
    Windows编程革命简史
    su的时候密码认证失败的解决方法
    ruby 元编程 meta programming
    内存对齐分配策略(含位域模式)
    Ruby 之 Block, Proc, Lambda 联系区别,转载
    c++异常处理机制示例及讲解
    ruby 常见问题集 1 不断更新中
  • 原文地址:https://www.cnblogs.com/cswuyg/p/5103909.html
Copyright © 2020-2023  润新知