【问题标题】:Configuring TCP keepalive after accept接受后配置 TCP keepalive
【发布时间】:2015-07-23 23:04:03
【问题描述】:

在套接字上的accept() 之后,我正在尝试配置 TCP keepalive。

SockConnected = accept(SockListen, &RemoteAddr,
                   &RemoteLen);
/* A bit of checking goes here */
if (setsockopt (SockConnected , SOL_SOCKET, SO_KEEPALIVE,
                            (ST_CHAR *) &sockopt_on, sizeof (int) ) )
{
   /* Error logging */
}

if (setsockopt(SockConnected , IPPROTO_TCP, TCP_KEEPIDLE, (char*)&(sockopt_tcp_keep_idle), sizeof(sockopt_tcp_keep_idle)))
{
    /* Error logging */
}
if (setsockopt(SockConnected, IPPROTO_TCP, TCP_KEEPINTVL, (char*)&(sockopt_tcp_keep_intvl), sizeof(sockopt_tcp_keep_intvl)))
{
    /* Error logging */
}
if (setsockopt(SockConnected, IPPROTO_TCP, TCP_KEEPCNT, (char*)&(sockopt_tcp_keep_cnt), sizeof(sockopt_tcp_keep_cnt)))
{
    /* Error logging */
}

在为所有四个属性调用getsockopt() 之后,一切似乎都很好。我已经检查了 Wireshark 并且没有发送 Keep alive 数据包。我不得不将SOL_TCP 更改为IPPROTO_TCP,因为在为TCP_KEEPIDLE 调用setsockopt() 时,它返回了errno 92(找不到协议)。

在调用socket() 进行传出连接后,我也在做同样的事情,并且工作正常。

我正在使用 C 和 Linux。

有什么原因导致setsockopt 在接受后可能无法正常工作?

【问题讨论】:

  • 您设置的值可能不是实际使用的值。平台可以调整它们。使用getsockopt() 找出实际使用的值,然后查看发生的情况是否与此一致。注意,我相信您可以在侦听套接字上执行所有这些操作,从那里它将被所有接受的套接字继承。
  • 我也在监听套接字上试过了,没有运气。我会仔细检查getsockopt() 的返回值。谢谢!

标签: c linux sockets tcp


【解决方案1】:

这是一个最小的工作示例。如果对你有用,你可以作为参考。

#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/signal.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <netinet/tcp.h>

#define check(expr) if (!(expr)) { perror(#expr); kill(0, SIGTERM); }

void enable_keepalive(int sock) {
    int yes = 1;
    check(setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, &yes, sizeof(int)) != -1);

    int idle = 1;
    check(setsockopt(sock, IPPROTO_TCP, TCP_KEEPIDLE, &idle, sizeof(int)) != -1);

    int interval = 1;
    check(setsockopt(sock, IPPROTO_TCP, TCP_KEEPINTVL, &interval, sizeof(int)) != -1);

    int maxpkt = 10;
    check(setsockopt(sock, IPPROTO_TCP, TCP_KEEPCNT, &maxpkt, sizeof(int)) != -1);
}

int main(int argc, char** argv) {
    check(argc == 2);

    struct sockaddr_in addr;
    addr.sin_family = AF_INET;
    addr.sin_port = htons(12345);
    check(inet_pton(AF_INET, argv[1], &addr.sin_addr) != -1);

    int server = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    check(server != -1);

    int yes = 1;
    check(setsockopt(server, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) != -1);

    check(bind(server, (struct sockaddr*)&addr, sizeof(addr)) != -1);
    check(listen(server, 1) != -1);

    if (fork() == 0) {
        int client = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
        check(client != -1);
        check(connect(client, (struct sockaddr*)&addr, sizeof(addr)) != -1);
        printf("connected\n");
        pause();
    }
    else {
        int client = accept(server, NULL, NULL);
        check(client != -1);
        enable_keepalive(client);
        printf("accepted\n");
        wait(NULL);
    }

    return 0;
}

示例输出(tcpdump 每秒报告一次 keepalive 数据包):

$ ./a.out 127.0.0.1 &
[1] 14010
connected
accepted

$ tcpdump -n -c4 -ilo port 12345
dropped privs to tcpdump
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on lo, link-type EN10MB (Ethernet), capture size 262144 bytes
18:00:35.173892 IP 127.0.0.1.12345 > 127.0.0.1.60998: Flags [.], ack 510307430, win 342, options [nop,nop,TS val 389745775 ecr 389745675], length 0
18:00:35.173903 IP 127.0.0.1.60998 > 127.0.0.1.12345: Flags [.], ack 1, win 342, options [nop,nop,TS val 389745775 ecr 389745075], length 0
18:00:36.173886 IP 127.0.0.1.12345 > 127.0.0.1.60998: Flags [.], ack 1, win 342, options [nop,nop,TS val 389745875 ecr 389745775], length 0
18:00:36.173898 IP 127.0.0.1.60998 > 127.0.0.1.12345: Flags [.], ack 1, win 342, options [nop,nop,TS val 389745875 ecr 389745075], length 0
4 packets captured
8 packets received by filter
0 packets dropped by kernel

【讨论】:

  • 谢谢!我在listen() 之前和accept() 之后配置了所有内容。如果我在 accept() 之后执行此操作,则启用 keepalive。
【解决方案2】:

SO_KEEPALIVE 应与SOL_SOCKET 一起使用,而不是IPPROTO_TCPSOL_TCP。请参阅socket(7)tcp(7)

所以试试这个:

int val = 1;
setsockopt(SockConnected, SOL_SOCKET, SO_KEEPALIVE, &val, sizeof(val));

如果SO_KEEPALIVE 设置不正确,我猜TCP_KEEPIDLE 和类似选项会被忽略,而SOL_TCPIPPROTO_TCP 的同义词。

如果没有帮助,请考虑发布一个重现问题的最小示例。

【讨论】:

  • 感谢您指出这一点。事实证明,在这里打开问题时这是一个错字。我检查了我的代码,正如你所说,SOL_SOCKETSO_KEEPALIVE 一起使用。
  • 好吧,那我也没什么想法了。我建议从重现您的程序的最小示例开始,并对所有系统调用进行错误检查,以便其他人可以尝试并帮助您。稍后我将发布一个对我有用的最小示例。
  • 错字:程序 -> 问题
猜你喜欢
  • 2012-05-20
  • 2021-11-17
  • 1970-01-01
  • 2020-11-30
  • 2013-12-29
  • 2014-12-31
  • 1970-01-01
  • 2022-11-13
  • 2011-07-12
相关资源
最近更新 更多