zoukankan      html  css  js  c++  java
  • 一种构造WEB服务器端recv和send接口阻塞现象的方法

    send阻塞

    socket recv send接口阻塞,会导致服务器端不在响应客户端任何请求,所以一般情况, 会将socket设置为非阻塞状态,

    但是有些场景,例如ssl_accept就需要使用阻塞的socket,否则握手极容易失败, 但是一直阻塞,容易导致服务器端DOS现象。

    下面是阻塞的解释 http://blog.csdn.net/xiaofei0859/article/details/6037814

    比如你调用send函数发送一定的Byte,在系统内部send做的工作其实只是把数据传输(Copy)到TCP/IP协议栈的输出缓冲区,它执行成功并不代表数据已经成功的发送出去了,如果TCP/IP协议栈没有足够的可用缓冲区来保存你Copy过来的数据的话...这时候就体现出阻塞和非阻塞的不同之处了:对于阻塞模式的socket send函数将不返回直到系统缓冲区有足够的空间把你要发送的数据Copy过去以后才返回,而对于非阻塞的socket来说send会立即返回WSAEWOULDDBLOCK告诉调用者说:"发送操作被阻塞了!!!你想办法处理吧..."
    对于recv函数,同样道理,该函数的内部工作机制其实是在等待TCP/IP协议栈的接收缓冲区通知它说:嗨,你的数据来了.对于阻塞模式的socket来说如果TCP/IP协议栈的接收缓冲区没有通知一个结果给它它就一直不返回:耗费着系统资源....对于非阻塞模式的socket该函数会马上返回,然后告诉你:WSAEWOULDDBLOCK---"现在没有数据,回头在来看看"

    recv阻塞构造方法

    服务器端调用recv接口,如果读取不到内容,则进入阻塞状态,进程则处于阻塞状态。

    对于ssl_accept会调用recv接口,要求读取一定长度内容,长度大于1,读取内容不够,则进入阻塞状态,

    于是可以构造客户端脚本,建立连接后,发送一个字节后, 就开始读取内容,这样与服务器端recv形成死锁,

    客户端等待服务器端发送数据, 服务器端由于读取内容不够,则阻塞等待接受内容。

    lua socket脚本:

    socket = require "socket"
    print(socket._VERSION)
    
    local address = "192.168.1.1"
    local port = 443
    local client = assert (socket.connect(address, port))
    
    if nil ~= client then
        client:send("1")
        print("send to receive !!!!")
        local r = client:receive("*a")
    
        client:close()
    end

    send阻塞构造方法

    服务器端,调用send接口发送数据,当缓冲中没有足够大的空间,则send接口处于阻塞状态,

    等待缓冲区的数据被发送后,缓存的空间可以存储send要发送的数据,则send则激活返回。

    客户端发送到client hello数据(ssl握手)后,服务器端会向客户端发送数据,循环发送大概到一M数据,但是客户端不接收,执行sleep操作:

    下面lua脚本代码,实现客户端,其中准备clienthello数据,使用str2bin方法,客户端不接收数据,使用sleep休眠三六零零秒。

    socket = require "socket"
    print(socket._VERSION)
    
    local address = "192.168.1.1"
    local port = 443
    local client = assert (socket.connect(address, port))
    
    -- http://m.oschina.net/blog/220844
    local function bin2hex(s)
        s=string.gsub(s,"(.)",function (x) return string.format("%02X ",string.byte(x)) end)
        return s
    end
    
    local h2b = {
        ["0"] = 0,
        ["1"] = 1,
        ["2"] = 2,
        ["3"] = 3,
        ["4"] = 4,
        ["5"] = 5,
        ["6"] = 6,
        ["7"] = 7,
        ["8"] = 8,
        ["9"] = 9,
        ["A"] = 10,
        ["B"] = 11,
        ["C"] = 12,
        ["D"] = 13,
        ["E"] = 14,
        ["F"] = 15
    }
    
    local function hex2bin( hexstr )
        local s = string.gsub(hexstr, "(.)(.)", function ( h, l )
            print( "h="..h.." l="..l )
            -- http://www.cnblogs.com/whiteyun/archive/2009/08/07/1540899.html
            h = string.upper(h)
            l = string.upper(l)
             return string.char(h2b[h]*16+h2b[l])
        end)
        return s
    end
    
    -- http://www.cnblogs.com/wcong/p/3218053.html
    require("socket")
    function Sleep(n)
       socket.select(nil, nil, n)
    end
    
    local clientHelloHexStr = [[1603010200010001fc0301d836e6926224b009080b70496a6515b8cf5a09603e6e309f400ab79fb67225cc203ca2eda02bb49abb6f6dc513684b5df3cc654268108054fe7e28299b03e6bae0002ec02bc02fc00ac009c013c014c012c007c0110033003200450039003800880016002f004100350084000a0005000401000185ff01000100000a00080006001700180019000b00020100002300a02f1b88ca8fe2638f5d0ae18ed18838f3a4bd7115a8001aeb5dd6a57d825bf06e98c499eb43763091da9ae0baee573c6ba838b33bf8c4ccf96aff04a1aef0b73a8fb17ba2c1af3bd8a65ef8ff3c90d8f6279139e1cae33761420cb8daf0d244ad8ca5c0683b442cabafc41463dd23f2365a89ac75856ce6b0e6c224eda9479142f561a8574381667e2a9cafede019ba213cfbfdca7fa8de776d5e8ba833d2572d33740000001500c20000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000]]
    local cleintHelloBlock = hex2bin(clientHelloHexStr);
    
    if nil ~= client then
        client:send(cleintHelloBlock)
        print("send to receive !!!!")
        --local r = client:receive("*a")
        --print("r="..r)
        Sleep(3600)
        print("after Sleep")
        client:close()
    end

    服务器端代码,是否能毕现,可调整i的上限尝试:

    int i=0;
    char buff[] = "xxx..xxx" //1034字节
    for (;i<100;i++)
    {
        send(fd, buff, 1034, 0)
    }

    阻塞避免方法

    一种方法就是设置socket为非阻塞模式,此方法最直接

    但是有些情况不能接受,例如ssl_accept, 于是有第二种方法,摘自他人博客,设置socket读取或者发送阻塞超时时间。

    http://idsips.blog.163.com/blog/static/4800127220116611500538/

    linux:
    
        struct timeval timeout={3,0};//3s
    
        int ret=setsockopt(sock_fd,SOL_SOCKET,SO_SNDTIMEO,&timeout,sizeof(timeout));
    
        int ret=setsockopt(sock_fd,SOL_SOCKET,SO_RCVTIMEO,&timeout,sizeof(timeout));
    
        如果ret==0 则为成功,-1为失败,这时可以查看errno来判断失败原因
    
        int recvd=recv(sock_fd,buf,1024,0);
    
        if(recvd==-1&&errno==EAGAIN)
    
       {
    
                  printf("timeout
    ");
    
       }
  • 相关阅读:
    一分钟去博客园广告
    浅谈歌词文件(LRC、QRC、KRC)
    css语法
    自定义浏览文件控件
    cookie的使用
    div背景图片拉伸
    URL传中文参数问题
    OC原理之GCD定时器
    深入理解Javascript闭包(closure) [转载]
    基于asp.net的自定义ajax框架(可传对象)
  • 原文地址:https://www.cnblogs.com/lightsong/p/4216014.html
Copyright © 2011-2022 走看看