亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

Swoole源碼中如何查詢Websocket的連接問題詳解

 更新時(shí)間:2020年08月30日 10:59:24   作者:阿布阿布  
這篇文章主要給大家介紹了關(guān)于Swoole源碼中如何查詢Websocket的連接問題的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧

問題

我們項(xiàng)目的 Websocket Server 使用的 Swoole,最近在搭建 beta 環(huán)境的時(shí)候發(fā)現(xiàn) Websocket 協(xié)議雖然升級(jí)成功了,但是會(huì)出現(xiàn)定時(shí)重連,心跳、數(shù)據(jù)也一直沒有發(fā)送。項(xiàng)目的生產(chǎn)環(huán)境和 beta 一致,但是生產(chǎn)環(huán)境確沒有這個(gè)問題。

定位問題

為了方便調(diào)試 Swoole,以下測試是在本地環(huán)境下進(jìn)行。

查看 PHP 日志

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

抓包

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

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

追蹤 Swoole 源碼

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

從抓包結(jié)果看,發(fā)出響應(yīng)報(bào)文到 close 連接的時(shí)間很短,所以猜測是握手階段出了問題。從響應(yīng)報(bào)文可以看出,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;
}

追蹤進(jìn) swoole_websocket_handshake() 里,前面部分都是設(shè)置響應(yīng)的 header,響應(yīng)報(bào)文則是在 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;
}

從 swoole_http_response_end() 代碼中我們發(fā)現(xiàn),如果 ctx->keepalive 為 0 的話則關(guān)閉連接,斷點(diǎ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);

    // 此時(shí)websocket_statue 已經(jīng)是WEBSOCKET_STATUS_ACTIVE,不會(huì)走進(jìn)這步邏輯
    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;
}

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

Websocket 協(xié)議規(guī)定,請(qǐng)求 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;
  }
 }
}

解決問題

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

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

那么解決這個(gè)問題就很簡單了,就是手動(dòng)升級(jí)下 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;
  }
}

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

總結(jié)

到此這篇關(guān)于Swoole源碼中如何查詢Websocket的連接問題的文章就介紹到這了,更多相關(guān)Swoole源碼查詢Websocket連接問題內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • ThinkPHP中where()使用方法詳解

    ThinkPHP中where()使用方法詳解

    where方法可以用于對(duì)數(shù)據(jù)庫操作的結(jié)果進(jìn)行篩選。即SQL查詢語句中的where子句。本文給大家介紹ThinkPHP中where()使用方法詳解,感興趣的朋友參考下
    2016-04-04
  • Codeigniter整合Tank Auth權(quán)限類庫詳解

    Codeigniter整合Tank Auth權(quán)限類庫詳解

    相交其他CodeIgniter的類庫,tank_auth,配置簡單,使用也簡單,并且作者也一直在更新。這篇文章主要介紹了Codeigniter整合Tank Auth權(quán)限類庫詳解,需要的朋友可以參考下
    2014-06-06
  • PHP 面向?qū)ο蟪绦蛟O(shè)計(jì)(oop)學(xué)習(xí)筆記(三) - 單例模式和工廠模式

    PHP 面向?qū)ο蟪绦蛟O(shè)計(jì)(oop)學(xué)習(xí)筆記(三) - 單例模式和工廠模式

    設(shè)計(jì)模式是一套被反復(fù)使用、多數(shù)人知曉的、經(jīng)過分類編目的、代碼設(shè)計(jì)經(jīng)驗(yàn)的總結(jié)。使用設(shè)計(jì)模式是為了可重用代碼、讓代碼更容易被他人理解、保證代碼可靠性。
    2014-06-06
  • laravel5.2表單驗(yàn)證,并顯示錯(cuò)誤信息的實(shí)例

    laravel5.2表單驗(yàn)證,并顯示錯(cuò)誤信息的實(shí)例

    今天小編就為大家分享一篇laravel5.2表單驗(yàn)證,并顯示錯(cuò)誤信息的實(shí)例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧
    2019-09-09
  • thinkPHP5框架路由常用知識(shí)點(diǎn)匯總

    thinkPHP5框架路由常用知識(shí)點(diǎn)匯總

    這篇文章主要介紹了thinkPHP5框架路由常用知識(shí)點(diǎn),整理匯總了thinkPHP5框架路由相關(guān)概念、原理及操作技巧,需要的朋友可以參考下
    2019-09-09
  • php截取字符串之截取utf8或gbk編碼的中英文字符串示例

    php截取字符串之截取utf8或gbk編碼的中英文字符串示例

    php中自帶strlen是返回的字節(jié)數(shù),對(duì)于utf8編碼的中文返回時(shí)3個(gè),不滿足需求,下面給大家提供一個(gè)方法來完成這樣的功能
    2014-03-03
  • PHP之預(yù)定義接口詳解

    PHP之預(yù)定義接口詳解

    這篇文章主要整理了PHP之預(yù)定義接口,在平時(shí)項(xiàng)目過程中比較常用的四個(gè)接口:IteratorAggregate(聚合式aggregate迭代器Iterator)、Countable、ArrayAccess、Iterator,需要的朋友可以參考下
    2015-07-07
  • thinkphp分頁集成實(shí)例

    thinkphp分頁集成實(shí)例

    下面小編就為大家?guī)硪黄猼hinkphp分頁集成實(shí)例。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2017-07-07
  • 如何在centos8自定義目錄安裝php7.3

    如何在centos8自定義目錄安裝php7.3

    在本文中我們將給大家介紹如何在centos8自定義目錄安裝php7.3,在選擇要安裝的PHP版本之前,請(qǐng)確保你的應(yīng)用程序支持它,我們還將向你展示如何將PHP與Nginx和Apache集成,感興趣的朋友跟隨小編一起看看吧
    2019-11-11
  • mysql alter table命令修改表結(jié)構(gòu)實(shí)例詳解

    mysql alter table命令修改表結(jié)構(gòu)實(shí)例詳解

    這篇文章主要介紹了mysql alter table命令修改表結(jié)構(gòu)實(shí)例的相關(guān)資料,需要的朋友可以參考下
    2016-09-09

最新評(píng)論