设为首页 加入收藏

TOP

聊聊 TCP 中的 KeepAlive 机制(一)
2017-12-19 17:24:58 】 浏览:885
Tags:聊聊 TCP KeepAlive 机制

服务端的系统设置中经常会和底层协议打交道,我们有必要重温一下曾经那些“听过”却不熟悉的名词。 今天聊的话题是 KeepAlive,在实际应用中又是怎么使用的?

为什么有Keepalive?

大家都做过电梯吧,假设电梯来了你先进去,你朋友还没进来,过一段时间电梯门就会自动关闭, 你应该没遇到过哪个电梯会一直等你朋友来了才关门的。如果真是那样,那别的楼层的小姐姐们会炸了~

我们举个编程中的例子来解释下,我编写了一个服务端程序S和一个客户端程序C,客户端向服务端发送 一个消息:

服务端收到消息后一看,瞧给你牛*的,然后没理客户端,傻狗客户端一直在等待,但是不知道是不是服务器挂掉了? 这时候TCP协议提出一个办法,当客户端端等待超过一定时间后自动给服务端发送一个空的报文, 如果对方回复了这个报文证明连接还存活着,如果对方没有报文返回且进行了多次尝试都是一样, 那么就认为连接已经丢失,客户端就没必要继续保持连接了。 如果没有这种机制就会有很多空闲的连接占用着系统资源。

KeepAlive并不是TCP协议规范的一部分,但在几乎所有的TCP/IP协议栈(不管是Linux还是Windows)中,都实现了KeepAlive功能

RFC1122#TCP Keep-Alives

如何设置它?

在设置之前我们先来看看KeepAlive都支持哪些设置项

  1. KeepAlive默认情况下是关闭的,可以被上层应用开启和关闭
  2. tcp_keepalive_time: KeepAlive的空闲时长,或者说每次正常发送心跳的周期,默认值为7200s(2小时)
  3. tcp_keepalive_intvl: KeepAlive探测包的发送间隔,默认值为75s
  4. tcp_keepalive_probes: 在tcp_keepalive_time之后,没有接收到对方确认,继续发送保活探测包次数,默认值为9(次)

我们讲讲在Linux操作系统和使用Java、C语言和Nginx中如何设置

在Linux内核设置

KeepAlive默认不是开启的,如果想使用KeepAlive,需要在你的应用中设置SO_KEEPALIVE才可以生效。

查看当前的配置:

cat /proc/sys/net/ipv4/tcp_keepalive_time
cat /proc/sys/net/ipv4/tcp_keepalive_intvl
cat /proc/sys/net/ipv4/tcp_keepalive_probes

在Linux中我们可以通过修改 /etc/sysctl.conf 的全局配置:

net.ipv4.tcp_keepalive_time=7200
net.ipv4.tcp_keepalive_intvl=75
net.ipv4.tcp_keepalive_probes=9

添加上面的配置后输入 sysctl -p 使其生效,你可以使用 sysctl -a | grep keepalive 命令来查看当前的默认配置

如果应用中已经设置SO_KEEPALIVE,程序不用重启,内核直接生效

使用Netty4设置

这里我们使用常用的Java网络框架Netty来设置,只需要在服务端设置即可:

EventLoopGroup bossGroup   = new NioEventLoopGroup(1);
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
    ServerBootstrap b = new ServerBootstrap();
    b.group(bossGroup, workerGroup)
            .channel(NioServerSocketChannel.class)
            .option(ChannelOption.SO_BACKLOG, 100)
            .childOption(ChannelOption.SO_KEEPALIVE, true)
            .handler(new LoggingHandler(LogLevel.INFO));

    // Start the server.
    ChannelFuture f = b.bind(8088).sync();
    // Wait until the server socket is closed.
    f.channel().closeFuture().sync();
} finally {
    // Shut down all event loops to terminate all threads.
    bossGroup.shutdownGracefully();
    workerGroup.shutdownGracefully();
}

这段代码来自经典的echo服务器,我们在childOption中开启了SO_KEEPALIVE。 Java程序只能做到设置SO_KEEPALIVE选项,其他配置项只能依赖于sysctl配置,系统进行读取。

C语言设置

函数原型:

#include <sys/socket.h>

int setsockopt(int socket, int level, int option_name,
      const void *option_value, socklen_t option_len);

我们在需要使能Keepalive的socket上面调用setsockopt函数便可以打开该socket上面的keepalive。

  1. 第一个参数是要设置的套接字
  2. 第二个参数是SOL_SOCKET
  3. 第三个参数必须是SO_KEEPALIVE
  4. 第四个参数必须是一个布尔整型值,0表示关闭,1表示打开
  5. 最后一个参数是第四个参数值的大小。

调用例子:

int socket(int domain, int type, int protocol)
{
  int (*libc_socket)(int, int, int);
  int s, optval;
  char *env;

  *(void **)(&libc_socket) = dlsym(RTLD_NEXT, "socket");
  if(dlerror()) {
    errno = EACCES;
    return -1;
  }

  if((s = (*libc_socket)(domain, type, protocol)) != -1) {
    if((domain == PF_INET) && (type == SOCK_STREAM)) {
      if(!(env = getenv("KEEPALIVE")) || strcasecmp(env, "off")) {
        optval = 1;
      } else {
        optval = 0;
      }
      if(!(env = getenv("KEEPALIVE")) || strcasecmp(env, "skip")) {
        setsockopt(s, SOL_SOCKET, SO_KEEPALIVE, &optval, sizeof(optval));
      }
#ifdef TCP_KEEPCNT
      if((env = getenv("KEEPCNT")) && ((optval = a
首页 上一页 1 2 下一页 尾页 1/2/2
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇JVM 堆内存和非堆内存 下一篇Spring 接口支持返回多种格式

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目