• <fieldset id="8imwq"><menu id="8imwq"></menu></fieldset>
  • <bdo id="8imwq"><input id="8imwq"></input></bdo>
    最新文章專題視頻專題問答1問答10問答100問答1000問答2000關(guān)鍵字專題1關(guān)鍵字專題50關(guān)鍵字專題500關(guān)鍵字專題1500TAG最新視頻文章推薦1 推薦3 推薦5 推薦7 推薦9 推薦11 推薦13 推薦15 推薦17 推薦19 推薦21 推薦23 推薦25 推薦27 推薦29 推薦31 推薦33 推薦35 推薦37視頻文章20視頻文章30視頻文章40視頻文章50視頻文章60 視頻文章70視頻文章80視頻文章90視頻文章100視頻文章120視頻文章140 視頻2關(guān)鍵字專題關(guān)鍵字專題tag2tag3文章專題文章專題2文章索引1文章索引2文章索引3文章索引4文章索引5123456789101112131415文章專題3
    問答文章1 問答文章501 問答文章1001 問答文章1501 問答文章2001 問答文章2501 問答文章3001 問答文章3501 問答文章4001 問答文章4501 問答文章5001 問答文章5501 問答文章6001 問答文章6501 問答文章7001 問答文章7501 問答文章8001 問答文章8501 問答文章9001 問答文章9501
    當(dāng)前位置: 首頁 - 科技 - 知識百科 - 正文

    學(xué)習(xí)在Swoole源碼中查詢 Websocket 的連接問題

    來源:懂視網(wǎng) 責(zé)編:小采 時間:2020-11-27 13:59:49
    文檔

    學(xué)習(xí)在Swoole源碼中查詢 Websocket 的連接問題

    學(xué)習(xí)在Swoole源碼中查詢 Websocket 的連接問題:.markdown-body{word-break:break-word;line-height:1.75;font-weight:400;font-size:15px;overflow-x:hidden;color:#333}.markdown-body h1.markdown-body h2.markdown-body h3.markdown-body h4.markdown-body h5.markdown-body h6{line-height:1.5;ma
    推薦度:
    導(dǎo)讀學(xué)習(xí)在Swoole源碼中查詢 Websocket 的連接問題:.markdown-body{word-break:break-word;line-height:1.75;font-weight:400;font-size:15px;overflow-x:hidden;color:#333}.markdown-body h1.markdown-body h2.markdown-body h3.markdown-body h4.markdown-body h5.markdown-body h6{line-height:1.5;ma

    查看 PHP 日志

    在 PHP 日志里,發(fā)現(xiàn)一條錯誤日志: ErrorException: Swoole\WebSocket\Server::push(): the connected client of connection[47] is not a websocket client or closed,說明 Websocket 連接已經(jīng) close 了。

    抓包

    既然連接被 close 掉了,那我們來看看是誰主動關(guān)閉的連接。Swoole 監(jiān)聽的端口是 1215,通過 tcpdump -nni lo0 -X port 1215 可以看到,Swoole 在發(fā)出協(xié)議升級的響應(yīng)報文后,又發(fā)出了 Fin 報文段,即 Swoole 主動斷開了連接,所以才會出現(xiàn)瀏覽器顯示 WebSocket 連接建立成功,但是又定時重連的問題。

    10:22:58.060810 IP 127.0.0.1.1215 > 127.0.0.1.53823: Flags [P.], seq 1:185, ack 1372, win 6358, options [nop,nop,TS val 1981911666 ecr 1981911665], length 184
     0x0000: 4500 00ec 0000 4000 4006 0000 7f00 0001 E.....@.@.......
     0x0010: 7f00 0001 04bf d23f 9377 304a 6d2f 9604 .......?.w0Jm/..
     0x0020: 8018 18d6 fee0 0000 0101 080a 7621 9272 ............v!.r
     0x0030: 7621 9271 4854 5450 2f31 2e31 2031 3031 v!.qHTTP/1.1.101
     0x0040: 2053 7769 7463 6869 6e67 2050 726f 746f .Switching.Proto
     0x0050: 636f 6c73 0d0a 5570 6772 6164 653a 2077 cols..Upgrade:.w
     0x0060: 6562 736f 636b 6574 0d0a 436f 6e6e 6563 ebsocket..Connec
     0x0070: 7469 6f6e 3a20 5570 6772 6164 650d 0a53 tion:.Upgrade..S
     0x0080: 6563 2d57 6562 536f 636b 6574 2d41 6363 ec-WebSocket-Acc
     0x0090: 6570 743a 2052 6370 3851 6663 446c 3146 ept:.Rcp8QfcDl1F
     0x00a0: 776e 666a 6377 3862 4933 6971 7176 4551 wnfjcw8bI3iqqvEQ
     0x00b0: 3d0d 0a53 6563 2d57 6562 536f 636b 6574 =..Sec-WebSocket
     0x00c0: 2d56 6572 7369 6f6e 3a20 3133 0d0a 5365 -Version:.13..Se
     0x00d0: 7276 6572 3a20 7377 6f6f 6c65 2d68 7474 rver:.swoole-htt
     0x00e0: 702d 7365 7276 6572 0d0a 0d0a p-server....
    10:22:58.060906 IP 127.0.0.1.53823 > 127.0.0.1.1215: Flags [.], ack 185, win 6376, options [nop,nop,TS val 1981911666 ecr 1981911666], length 0
     0x0000: 4500 0034 0000 4000 4006 0000 7f00 0001 E..4..@.@.......
     0x0010: 7f00 0001 d23f 04bf 6d2f 9604 9377 3102 .....?..m/...w1.
     0x0020: 8010 18e8 fe28 0000 0101 080a 7621 9272 .....(......v!.r
     0x0030: 7621 9272 v!.r
    10:22:58.061467 IP 127.0.0.1.1215 > 127.0.0.1.53823: Flags [F.], seq 185, ack 1372, win 6358, options [nop,nop,TS val 1981911667 ecr 1981911666], length 0
     0x0000: 4500 0034 0000 4000 4006 0000 7f00 0001 E..4..@.@.......
     0x0010: 7f00 0001 04bf d23f 9377 3102 6d2f 9604 .......?.w1.m/..
     0x0020: 8011 18d6 fe28 0000 0101 080a 7621 9273 .....(......v!.s
     0x0030: 7621 9272 v!.r復(fù)制代碼

    追蹤 Swoole 源碼

    我們現(xiàn)在知道了是 Swoole 主動斷開了連接,但它是在什么時候斷開的,又為什么要斷開呢?就讓我們從源碼一探究竟。

    從抓包結(jié)果看,發(fā)出響應(yīng)報文到 close 連接的時間很短,所以猜測是握手階段出了問題。從響應(yīng)報文可以看出,Websocket 連接是建立成功的,推測 swoole_websocket_handshake() 的結(jié)果應(yīng)該是 true,那么連接應(yīng)該是在 swoole_websocket_handshake() 里 close 的。

    // // swoole_websocket_server.cc
    int swoole_websocket_onHandshake(swServer *serv, swListenPort *port, http_context *ctx)
    {
     int fd = ctx->fd;
     bool success = swoole_websocket_handshake(ctx);
     if (success)
     {
     swoole_websocket_onOpen(serv, ctx);
     }
     else
     {
     serv->close(serv, fd, 1);
     }
     if (!ctx->end)
     {
     swoole_http_context_free(ctx);
     }
     return SW_OK;
    }復(fù)制代碼

    追蹤進 swoole_websocket_handshake() 里,前面部分都是設(shè)置響應(yīng)的 header,響應(yīng)報文則是在 swoole_http_response_end() 里發(fā)出的,它的結(jié)果也就是 swoole_websocket_handshake 的結(jié)果。

    // swoole_websocket_server.cc
    bool swoole_websocket_handshake(http_context *ctx)
    {
     ...
    
     swoole_http_response_set_header(ctx, ZEND_STRL("Upgrade"), ZEND_STRL("websocket"), false);
     swoole_http_response_set_header(ctx, ZEND_STRL("Connection"), ZEND_STRL("Upgrade"), false);
     swoole_http_response_set_header(ctx, ZEND_STRL("Sec-WebSocket-Accept"), sec_buf, sec_len, false);
     swoole_http_response_set_header(ctx, ZEND_STRL("Sec-WebSocket-Version"), ZEND_STRL(SW_WEBSOCKET_VERSION), false);
    
     ...
    
     ctx->response.status = 101;
     ctx->upgrade = 1;
    
     zval retval;
     swoole_http_response_end(ctx, nullptr, &retval);
     return Z_TYPE(retval) == IS_TRUE;
    }復(fù)制代碼

    swoole_http_response_end() 代碼中我們發(fā)現(xiàn),如果 ctx->keepalive 為 0 的話則關(guān)閉連接,斷點調(diào)試下發(fā)現(xiàn)還真就是 0。至此,連接斷開的地方我們就找到了,下面我們就看下什么情況下 ctx->keepalive 設(shè)置為 1。

    // swoole_http_response.cc
    void swoole_http_response_end(http_context *ctx, zval *zdata, zval *return_value)
    {
     if (ctx->chunk) {
     ...
     } else {
     ...
    
     if (!ctx->send(ctx, swoole_http_buffer->str, swoole_http_buffer->length))
     {
     ctx->send_header = 0;
     RETURN_FALSE;
     } 
     }
    
     if (ctx->upgrade && !ctx->co_socket) {
     swServer *serv = (swServer*) ctx->private_data;
     swConnection *conn = swWorker_get_connection(serv, ctx->fd);
    
     // 此時websocket_statue 已經(jīng)是WEBSOCKET_STATUS_ACTIVE,不會走進這步邏輯
     if (conn && conn->websocket_status == WEBSOCKET_STATUS_HANDSHAKE) {
     if (ctx->response.status == 101) {
     conn->websocket_status = WEBSOCKET_STATUS_ACTIVE;
     } else {
     /* connection should be closed when handshake failed */
     conn->websocket_status = WEBSOCKET_STATUS_NONE;
     ctx->keepalive = 0;
     }
     }
     }
    
     if (!ctx->keepalive) {
     ctx->close(ctx);
     }
     ctx->end = 1;
     RETURN_TRUE;
    }復(fù)制代碼

    最終我們找到 ctx->keepalive 是在 swoole_http_should_keep_alive() 里設(shè)置的。從代碼我們知道,當(dāng) HTTP 協(xié)議是 1.1 版本時,keepalive 取決于 header 沒有設(shè)置 Connection: close;當(dāng)為 1.0 版本時,header 需設(shè)置 Connection: keep-alive

    Websocket 協(xié)議規(guī)定,請求 header 里的 Connection 需設(shè)置為 Upgrade,所以我們需要改用 HTTP/1.1 協(xié)議。

    int swoole_http_should_keep_alive (swoole_http_parser *parser)
    {
     if (parser->http_major > 0 && parser->http_minor > 0) {
     /* HTTP/1.1 */
     if (parser->flags & F_CONNECTION_CLOSE) {
     return 0;
     } else {
     return 1;
     }
     } else {
     /* HTTP/1.0 or earlier */
     if (parser->flags & F_CONNECTION_KEEP_ALIVE) {
     return 1;
     } else {
     return 0;
     }
     }
    }復(fù)制代碼

    解決問題

    從上面的結(jié)論我們可以知道,問題的關(guān)鍵點在于請求頭的 Connection 和 HTTP 協(xié)議版本。

    后來問了下運維,生產(chǎn)環(huán)境的 LB 會在轉(zhuǎn)發(fā)請求時,會將 HTTP 協(xié)議版本修改為 1.1,這也是為什么只有 beta 環(huán)境存在這個問題,nginx 的 access_log 也印證了這一點。

    那么解決這個問題就很簡單了,就是手動升級下 HTTP 協(xié)議的版本,完整的 nginx 配置如下。

    upstream service {
     server 127.0.0.1:1215;
    }
    
    server {
     listen 80;
     server_name dev-service.ts.com;
    
     location / {
     proxy_set_header Host $http_host;
     proxy_set_header Scheme $scheme;
     proxy_set_header SERVER_PORT $server_port;
     proxy_set_header REMOTE_ADDR $remote_addr;
     proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
     proxy_set_header Upgrade $http_upgrade;
     proxy_set_header Connection $connection_upgrade;
     proxy_http_version 1.1;
    
     proxy_pass http://service;
     }
    }復(fù)制代碼

    重啟 Nginx 后,Websocket 終于正常了~

    聲明:本網(wǎng)頁內(nèi)容旨在傳播知識,若有侵權(quán)等問題請及時與本網(wǎng)聯(lián)系,我們將在第一時間刪除處理。TEL:177 7030 7066 E-MAIL:11247931@qq.com

    文檔

    學(xué)習(xí)在Swoole源碼中查詢 Websocket 的連接問題

    學(xué)習(xí)在Swoole源碼中查詢 Websocket 的連接問題:.markdown-body{word-break:break-word;line-height:1.75;font-weight:400;font-size:15px;overflow-x:hidden;color:#333}.markdown-body h1.markdown-body h2.markdown-body h3.markdown-body h4.markdown-body h5.markdown-body h6{line-height:1.5;ma
    推薦度:
    • 熱門焦點

    最新推薦

    猜你喜歡

    熱門推薦

    專題
    Top
    主站蜘蛛池模板: 久久人人超碰精品CAOPOREN | 久久精品一区二区| 亚洲AV日韩精品一区二区三区 | 国产欧美在线观看精品一区二区 | 91精品国产乱码久久久久久 | 国产精品禁18久久久夂久| 亚洲精品成人区在线观看| 精品日韩在线视频一区二区三区| 欧美日韩在线亚洲国产精品| 国产精品伦一区二区三级视频| 无码精品A∨在线观看| 中文字幕精品一区二区三区视频| 久久精品中文字幕有码| 黑巨人与欧美精品一区 | 国产精品视频第一区二区三区| 亚洲精品无码永久中文字幕| 热久久国产欧美一区二区精品| 国产精品无码免费播放| 隔壁老王国产在线精品| 99热成人精品国产免男男| 久久这里只精品国产99热| 99麻豆久久久国产精品免费| 国产精品成熟老女人视频| 国产亚洲色婷婷久久99精品| 精品蜜臀久久久久99网站| 精品无码久久久久国产| 老司机亚洲精品影院| 青青草原精品99久久精品66| 亚洲国产精品成人精品无码区在线| 亚洲日韩中文在线精品第一| 自拍偷自拍亚洲精品第1页| 亚洲国产精品无码久久98| 婷婷国产成人精品视频| 国产精品亚洲片在线| 国产午夜精品久久久久免费视 | 99久久国产综合精品五月天喷水 | 四虎亚洲国产成人久久精品| 日本精品自产拍在线观看中文 | 99热这里只有精品在线| 国产欧美国产精品第一区| 欧美日韩精品一区二区|