Table of Contents
问题
定位问题
查看 PHP 日志
抓包
追踪 Swoole 源码
解决问题
Home PHP Framework Swoole Learn to query Websocket connection issues in Swoole source code

Learn to query Websocket connection issues in Swoole source code

Nov 17, 2020 pm 05:00 PM
swoole websocket

Swoole教程栏目介绍如何查询Websocket的连接问题。

Learn to query Websocket connection issues in Swoole source code

推荐:swoole教程

问题

我们项目的 Websocket Server 使用的 Swoole,最近在搭建 beta 环境的时候发现 Websocket 协议虽然升级成功了,但是会出现定时重连,心跳、数据也一直没有发送。项目的生产环境和 beta 一致,但是生产环境确没有这个问题。

定位问题

为了方便调试 Swoole,以下测试是在本地环境下进行。

查看 PHP 日志

在 PHP 日志里,发现一条错误日志: ErrorException: Swoole\WebSocket\Server::push(): the connected client of connection[47] is not a websocket client or closed,说明 Websocket 连接已经 close 了。

抓包

既然连接被 close 掉了,那我们来看看是谁主动关闭的连接。Swoole 监听的端口是 1215,通过 tcpdump -nni lo0 -X port 1215 可以看到,Swoole 在发出协议升级的响应报文后,又发出了 Fin 报文段,即 Swoole 主动断开了连接,所以才会出现浏览器显示 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复制代码
Copy after login

追踪 Swoole 源码

我们现在知道了是 Swoole 主动断开了连接,但它是在什么时候断开的,又为什么要断开呢?就让我们从源码一探究竟。

从抓包结果看,发出响应报文到 close 连接的时间很短,所以猜测是握手阶段出了问题。从响应报文可以看出,Websocket 连接是建立成功的,推测 swoole_websocket_handshake() 的结果应该是 true,那么连接应该是在 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;
}复制代码
Copy after login

追踪进 swoole_websocket_handshake() 里,前面部分都是设置响应的 header,响应报文则是在 swoole_http_response_end() 里发出的,它的结果也就是 swoole_websocket_handshake 的结果。

// 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;
}复制代码
Copy after login

swoole_http_response_end() 代码中我们发现,如果 ctx->keepalive 为 0 的话则关闭连接,断点调试下发现还真就是 0。至此,连接断开的地方我们就找到了,下面我们就看下什么情况下 ctx->keepalive 设置为 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 已经是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;
}复制代码
Copy after login

最终我们找到 ctx->keepalive 是在 swoole_http_should_keep_alive() 里设置的。从代码我们知道,当 HTTP 协议是 1.1 版本时,keepalive 取决于 header 没有设置 Connection: close;当为 1.0 版本时,header 需设置 Connection: keep-alive

Websocket 协议规定,请求 header 里的 Connection 需设置为 Upgrade,所以我们需要改用 HTTP/1.1 协议。

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;
    }
  }
}复制代码
Copy after login

解决问题

从上面的结论我们可以知道,问题的关键点在于请求头的 Connection 和 HTTP 协议版本。

后来问了下运维,生产环境的 LB 会在转发请求时,会将 HTTP 协议版本修改为 1.1,这也是为什么只有 beta 环境存在这个问题,nginx 的 access_log 也印证了这一点。

那么解决这个问题就很简单了,就是手动升级下 HTTP 协议的版本,完整的 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;
    }
}复制代码
Copy after login

重启 Nginx 后,Websocket 终于正常了~

The above is the detailed content of Learn to query Websocket connection issues in Swoole source code. For more information, please follow other related articles on the PHP Chinese website!

Statement of this Website
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn

Hot AI Tools

Undresser.AI Undress

Undresser.AI Undress

AI-powered app for creating realistic nude photos

AI Clothes Remover

AI Clothes Remover

Online AI tool for removing clothes from photos.

Undress AI Tool

Undress AI Tool

Undress images for free

Clothoff.io

Clothoff.io

AI clothes remover

AI Hentai Generator

AI Hentai Generator

Generate AI Hentai for free.

Hot Article

R.E.P.O. Energy Crystals Explained and What They Do (Yellow Crystal)
4 weeks ago By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O. Best Graphic Settings
4 weeks ago By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O. How to Fix Audio if You Can't Hear Anyone
4 weeks ago By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O. Chat Commands and How to Use Them
4 weeks ago By 尊渡假赌尊渡假赌尊渡假赌

Hot Tools

Notepad++7.3.1

Notepad++7.3.1

Easy-to-use and free code editor

SublimeText3 Chinese version

SublimeText3 Chinese version

Chinese version, very easy to use

Zend Studio 13.0.1

Zend Studio 13.0.1

Powerful PHP integrated development environment

Dreamweaver CS6

Dreamweaver CS6

Visual web development tools

SublimeText3 Mac version

SublimeText3 Mac version

God-level code editing software (SublimeText3)

How to achieve real-time communication using PHP and WebSocket How to achieve real-time communication using PHP and WebSocket Dec 17, 2023 pm 10:24 PM

With the continuous development of Internet technology, real-time communication has become an indispensable part of daily life. Efficient, low-latency real-time communication can be achieved using WebSockets technology, and PHP, as one of the most widely used development languages ​​in the Internet field, also provides corresponding WebSocket support. This article will introduce how to use PHP and WebSocket to achieve real-time communication, and provide specific code examples. 1. What is WebSocket? WebSocket is a single

PHP and WebSocket: Best practices for real-time data transfer PHP and WebSocket: Best practices for real-time data transfer Dec 18, 2023 pm 02:10 PM

PHP and WebSocket: Best Practice Methods for Real-Time Data Transfer Introduction: In web application development, real-time data transfer is a very important technical requirement. The traditional HTTP protocol is a request-response model protocol and cannot effectively achieve real-time data transmission. In order to meet the needs of real-time data transmission, the WebSocket protocol came into being. WebSocket is a full-duplex communication protocol that provides a way to communicate full-duplex over a single TCP connection. Compared to H

How does Java Websocket implement online whiteboard function? How does Java Websocket implement online whiteboard function? Dec 17, 2023 pm 10:58 PM

How does JavaWebsocket implement online whiteboard function? In the modern Internet era, people are paying more and more attention to the experience of real-time collaboration and interaction. Online whiteboard is a function implemented based on Websocket. It enables multiple users to collaborate in real-time to edit the same drawing board and complete operations such as drawing and annotation. It provides a convenient solution for online education, remote meetings, team collaboration and other scenarios. 1. Technical background WebSocket is a new protocol provided by HTML5. It implements

SSE and WebSocket SSE and WebSocket Apr 17, 2024 pm 02:18 PM

In this article, we will compare Server Sent Events (SSE) and WebSockets, both of which are reliable methods for delivering data. We will analyze them in eight aspects, including communication direction, underlying protocol, security, ease of use, performance, message structure, ease of use, and testing tools. A comparison of these aspects is summarized as follows: Category Server Sent Event (SSE) WebSocket Communication Direction Unidirectional Bidirectional Underlying Protocol HTTP WebSocket Protocol Security Same as HTTP Existing security vulnerabilities Ease of use Setup Simple setup Complex performance Fast message sending speed Affected by message processing and connection management Message structure Plain text or binary Ease of use Widely available Helpful for WebSocket integration

How to use swoole coroutine in laravel How to use swoole coroutine in laravel Apr 09, 2024 pm 06:48 PM

Using Swoole coroutines in Laravel can process a large number of requests concurrently. The advantages include: Concurrent processing: allows multiple requests to be processed at the same time. High performance: Based on the Linux epoll event mechanism, it processes requests efficiently. Low resource consumption: requires fewer server resources. Easy to integrate: Seamless integration with Laravel framework, simple to use.

golang WebSocket programming tips: handling concurrent connections golang WebSocket programming tips: handling concurrent connections Dec 18, 2023 am 10:54 AM

Golang is a powerful programming language, and its use in WebSocket programming is increasingly valued by developers. WebSocket is a TCP-based protocol that allows two-way communication between client and server. In this article, we will introduce how to use Golang to write an efficient WebSocket server that handles multiple concurrent connections at the same time. Before introducing the techniques, let's first learn what WebSocket is. Introduction to WebSocketWeb

How to use Java and WebSocket to implement real-time stock quotation push How to use Java and WebSocket to implement real-time stock quotation push Dec 17, 2023 pm 09:15 PM

How to use Java and WebSocket to implement real-time stock quotation push Introduction: With the rapid development of the Internet, real-time stock quotation push has become one of the focuses of investors. The traditional stock market push method has problems such as high delay and slow refresh speed. For investors, the inability to obtain the latest stock market information in a timely manner may lead to errors in investment decisions. Real-time stock quotation push based on Java and WebSocket can effectively solve this problem, allowing investors to obtain the latest stock price information as soon as possible.

PHP Websocket development guide to implement real-time translation function PHP Websocket development guide to implement real-time translation function Dec 18, 2023 pm 05:52 PM

PHP Websocket Development Guide: Implementing Real-time Translation Function Introduction: With the development of the Internet, real-time communication is becoming more and more important in various application scenarios. As an emerging communication protocol, Websocket provides good support for real-time communication. This article will take you through a detailed understanding of how to use PHP to develop Websocket applications, and combine the real-time translation function to demonstrate its specific application. 1. What is the Websocket protocol? The Websocket protocol is a

See all articles