Skip to content

Server initiates unnecessary keep-alives exactly after client keep-alivesΒ #1128

@eriknellessencorpuls

Description

@eriknellessencorpuls

The rSocket server initiates keep-alives right in the moment the client keep-alive is received and answered.

We have a client that announces in the SETUP frame to send keep-alives every 5 seconds. See https://rsocket.io/about/protocol/#setup-frame-0x01: "Time Between KEEPALIVE Frames: (31 bits = max value 2^31-1 = 2,147,483,647) Unsigned 31-bit integer of Time (in milliseconds) between KEEPALIVE frames that the client will send. Value MUST be > 0."

So the client sends a keep-alive to the server 5 seconds after the connection has been established. This keep-alive frame has the (R)espond flag set. See https://rsocket.io/about/protocol/#keepalive-frame-0x03: "KEEPALIVE frames MUST be initiated by the client and sent periodically with the (R)espond flag set. KEEPALIVE frames MAY be initiated by the server and sent upon application request with the (R)espond flag set."

The server responds with a keep-alive frame without the (R)espond flag set, which is correct AFAIK. But then, the server also sends a keep-alive frame with the (R)espond flag set. This is not entirely wrong, because the server is allowed to do so, as cited from the rSocket specification. But it really is not a good moment to do so. We just had the keep-alive roundtrip initiated by the client, so we know the connection is still alive. Why initiate another keep-alive from the server in that exact moment?

Here are some logs from the server to show what happens:

2025-11-12T09:43:46.799+01:00 DEBUG 26300 --- [ourServer] [ctor-http-nio-4] [] io.rsocket.FrameLogger : receiving -> 
Frame => Stream ID: 0 Type: KEEPALIVE Flags: 0b10000000 Length: 14
Data:
         +-------------------------------------------------+
         |  0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f |
+--------+-------------------------------------------------+----------------+
|00000000| 00 00 00 00 00 00 00 00                         |........        |
+--------+-------------------------------------------------+----------------+
2025-11-12T09:43:46.799+01:00 DEBUG 26300 --- [ourServer] [ctor-http-nio-4] [] io.rsocket.FrameLogger : sending -> 
Frame => Stream ID: 0 Type: KEEPALIVE Flags: 0b0 Length: 14
Data:
         +-------------------------------------------------+
         |  0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f |
+--------+-------------------------------------------------+----------------+
|00000000| 00 00 00 00 00 00 00 00                         |........        |
+--------+-------------------------------------------------+----------------+
2025-11-12T09:43:46.802+01:00 DEBUG 26300 --- [ourServer] [     parallel-6] [] io.rsocket.FrameLogger : sending -> 
Frame => Stream ID: 0 Type: KEEPALIVE Flags: 0b10000000 Length: 14
Data:
         +-------------------------------------------------+
         |  0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f |
+--------+-------------------------------------------------+----------------+
|00000000| 00 00 00 00 00 00 00 00                         |........        |
+--------+-------------------------------------------------+----------------+

I tried to find out why the server shows this behavior. It seems to me like the server uses the value from the SETUP frame to time its initiation of keep-alives. So maybe this is a misinterpretation of that field in the SETUP frame. See this line in class RSocketServer: https://github.com/rsocket/rsocket-java/blob/master/rsocket-core/src/main/java/io/rsocket/core/RSocketServer.java#L452

Expected Behavior

The server should generally not initiate keep-alives on a regular basis. It should only send keep-alives when triggered by the application.

Actual Behavior

The server initiates keep-alives with the same rhythm as the client.

Steps to Reproduce

You can reproduce it by just running a server and connecting a client that announces keep-alives every 5 seconds in the SETUP frame. We are using the spring boot integration, so our server looks like this:

@Controller
public class RSocketController {

    @ConnectMapping
    public void connection() {
    }
}

Possible Solution

To me it seems like the field from the SETUP frame is not interpreted correctly. Maybe the lines that start the timer in the server that initiates keep-alives can just be removed?

Your Environment

  • RSocket version(s) used: rsocket-core:1.1.5
  • Other relevant libraries versions (eg. netty, ...): We are using org.springframework.boot:spring-boot-starter-rsocket version 3.5.5
  • Platform (eg. JVM version (javar -version) or Node version (node --version)): Java 25
  • OS and version (eg uname -a):

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions