socket属性so_linger


struct linger {

int    l_onoff;   /* Linger active        */
int    l_linger;  /* How long to linger for   */
};

一:close()

1:l_onoff = 0;

这是close()  SO_LINGER属性的默认值

发送缓冲中的数据以及fin会继续发送到客户端

接收缓冲区中如果还有数据,则会给客户端发送rst数据包。客户端收到后,会放弃自己接收缓冲区中所有数据。

2:l_onoff =1;

l_linger =0;

发送缓冲区和接收缓冲区中的所有数据均抛弃,并给客户端发送rst数据包

3:l_onoff =1;

l_linger =!0;

close()将被阻塞(假定为阻塞模式)直到得到客户端的确认或者超时

当close采用默认选项的时候,即情况1下,会出现一些问题,尤其是在pipelining情况下更容易发生

客户端发送了10个请求

但服务器只能处理8个请求

服务器返回8个response

服务器调用close

此时服务器的发送缓冲区如还存在response数据,继续发送给客户端,不受影响

客户端的接收缓冲区还有response数据,但因为服务器close时发送了rst数据包,若客户端先收到了rst数据包,则会抛弃自己的接收缓冲区内容

客户端丢掉了若干response

二:shutdown()

shutdown(sockfd, SHUT_RD)

关闭sockfd的读端,抛弃接收缓冲区的数据,并且不能进行读操作

shutdown(sockfd, SHUT_WR)

关闭sockfd的写端,不能进行写操作

shutdown(sockfd, SHUT_RDWR)

相当于调用shutdown(sockfd, SHUT_RD) shutdown(sockfd, SHUT_WR) 各一次。

close和shutdown的区别在于,close减少fd的引用计数,直到引用计数为0的时候,套接字被释放

但是shutdown会影响所有进程的此套接字通信

三:完美的方案:

在服务器给客户端提供服务的时候,有以下情况需要考虑:

1.服务器不要过早的发送rst ,避免客户端丢失数据

2.怎样保证数据正确到达客户端,并被正确接收。需要服务器做些验证

选项SO_LINGER、close()、shutdown()、read()做配合设计,可得出完美方案:
1) 设置SO_LINGER选项参数l_onof非0而l_linger为0;(这样调用第5步的时候,直接给客户端发送rst数据包了)
2) 调用函数shutdown(sock_fd, SHUT_WR);(这样发送缓冲区不能再写数据,会逐步的发给客户端)
3) 设置超时定时器,假定为t秒;(等待客户端接受完数据返回给fin)
4) 调用函数read(sock_fd)阻塞等待,直到读到EOF或被定时器超时中断。(如果客户端没有返回fin,直到定时器超时,则默认客户端已经接受完数据,可以发送rst了)
5) 执行函数close(sock_fd)或者调用exit(0)进程退出。(直接发送rst给客户端,通过发送RST而取代正常的TCP四次挥手)

nginx和客户端采用的类似于上面的策略,但在1)的时候,并没有设置SO_LINGER参数,只有判断出请求已经超时,即第4步发生超时的时候,才会如此设置。可以将第1步骤放在4后,当超时时,直接发送rst,不然正常close

由3个指令控制:

lingering_close

lingering_time

lingering_timeout

1)lingering_close 可以配置  on,off,always。

lingering_close off          上面策略不作用,直接调用close()

lingering_close always   表示一直采取这个策略

lingering_close on           分情况采取这种策略,比如如果客户端发送的请求没有请求体的时候

2)lingering_time

lingering_close启用后,这个配置项对于上传大文件很有用。上文讲过,当用户请求的Content-Length大于max_client_body_size配置时,Nginx服务会立刻向用户发送413(Request entity too large)响应。但是,很多客户端可能不管413返回值,仍然持续不断地上传HTTP body,这时,经过了lingering_time设置的时间后,Nginx将不管用户是否仍在上传,都会把连接关闭掉。

3)lingering_timeout

lingering_close生效后,在关闭连接前,会检测是否有用户发送的数据到达服务器,如果超过lingering_timeout时间后还没有数据可读,就直接关闭连接;否则,必须在读取完连接缓冲区上的数据并丢弃掉后才会关闭连接。

详细代码分析:

static void
ngx_http_finalize_connection(ngx_http_request_t *r)
{ngx_http_core_loc_conf_t  *clcf;clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);if (r->main->count != 1) {if (r->discard_body) {r->read_event_handler = ngx_http_discarded_request_body_handler;ngx_add_timer(r->connection->read, clcf->lingering_timeout);if (r->lingering_time == 0) {r->lingering_time = ngx_time()+ (time_t) (clcf->lingering_time / 1000);}}ngx_http_close_request(r, 0);return;}
//首先判断是否为keepalive情况,那么去处理keepaliveif (!ngx_terminate&& !ngx_exiting&& r->keepalive&& clcf->keepalive_timeout > 0){ngx_http_set_keepalive(r);return;}
//如果配置了lingering_close always ;或者 配置了 lingering_close on并且满足某些条件,则进行先shutdown,再wait,再close操作。if (clcf->lingering_close == NGX_HTTP_LINGERING_ALWAYS|| (clcf->lingering_close == NGX_HTTP_LINGERING_ON&& (r->lingering_close|| r->header_in->pos < r->header_in->last|| r->connection->read->ready))){ngx_http_set_lingering_close(r); return;}
//如果不满足上面情况,则直接调用close操作,SO_LINGER参数采用默认配置ngx_http_close_request(r, 0);
}

lingering_close默认设置是on,在普遍的短连接的http请求,会走到ngx_http_set_lingering_close(r)。

static void
ngx_http_set_lingering_close(ngx_http_request_t *r)
{ngx_event_t               *rev, *wev;ngx_connection_t          *c;ngx_http_core_loc_conf_t  *clcf;c = r->connection;clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);rev = c->read;rev->handler = ngx_http_lingering_close_handler;r->lingering_time = ngx_time() + (time_t) (clcf->lingering_time / 1000);ngx_add_timer(rev, clcf->lingering_timeout);if (ngx_handle_read_event(rev, 0) != NGX_OK) {ngx_http_close_request(r, 0);return;}wev = c->write;wev->handler = ngx_http_empty_handler;if (wev->active && (ngx_event_flags & NGX_USE_LEVEL_EVENT)) {if (ngx_del_event(wev, NGX_WRITE_EVENT, 0) != NGX_OK) {ngx_http_close_request(r, 0);return;}}if (ngx_shutdown_socket(c->fd, NGX_WRITE_SHUTDOWN) == -1) {ngx_connection_error(c, ngx_socket_errno,ngx_shutdown_socket_n " failed");ngx_http_close_request(r, 0);return;}if (rev->ready) {ngx_http_lingering_close_handler(rev);}
}
static void
ngx_http_lingering_close_handler(ngx_event_t *rev)
{ssize_t                    n;ngx_msec_t                 timer;ngx_connection_t          *c;ngx_http_request_t        *r;ngx_http_core_loc_conf_t  *clcf;u_char                     buffer[NGX_HTTP_LINGERING_BUFFER_SIZE];c = rev->data;r = c->data;ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,"http lingering close handler");if (rev->timedout) {ngx_http_close_request(r, 0);return;}timer = (ngx_msec_t) (r->lingering_time - ngx_time());if (timer <= 0) {ngx_http_close_request(r, 0);return;}do {n = c->recv(c, buffer, NGX_HTTP_LINGERING_BUFFER_SIZE);ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "lingering read: %d", n);if (n == NGX_ERROR || n == 0) {ngx_http_close_request(r, 0);return;}} while (rev->ready);if (ngx_handle_read_event(rev, 0) != NGX_OK) {ngx_http_close_request(r, 0);return;}clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);timer *= 1000;if (timer > clcf->lingering_timeout) {timer = clcf->lingering_timeout;}ngx_add_timer(rev, timer);
}
static void
ngx_http_close_request(ngx_http_request_t *r, ngx_int_t rc)
{ngx_connection_t  *c;r = r->main;c = r->connection;ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,"http request count:%d blk:%d", r->count, r->blocked);if (r->count == 0) {ngx_log_error(NGX_LOG_ALERT, c->log, 0, "http request count is zero");}r->count--;if (r->count || r->blocked) {return;}ngx_http_free_request(r, rc);ngx_http_close_connection(c);
}
static void
ngx_http_free_request(ngx_http_request_t *r, ngx_int_t rc)
{ngx_log_t                 *log;struct linger              linger;ngx_http_cleanup_t        *cln;ngx_http_log_ctx_t        *ctx;ngx_http_core_loc_conf_t  *clcf;log = r->connection->log;ngx_log_debug0(NGX_LOG_DEBUG_HTTP, log, 0, "http close request");if (r->pool == NULL) {ngx_log_error(NGX_LOG_ALERT, log, 0, "http request already closed");return;}for (cln = r->cleanup; cln; cln = cln->next) {if (cln->handler) {cln->handler(cln->data);}}#if (NGX_STAT_STUB)if (r->stat_reading) {(void) ngx_atomic_fetch_add(ngx_stat_reading, -1);}if (r->stat_writing) {(void) ngx_atomic_fetch_add(ngx_stat_writing, -1);}#endifif (rc > 0 && (r->headers_out.status == 0 || r->connection->sent == 0)) {r->headers_out.status = rc;}log->action = "logging request";ngx_http_log_request(r);log->action = "closing request";if (r->connection->timedout) {clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);if (clcf->reset_timedout_connection) {linger.l_onoff = 1;linger.l_linger = 0;if (setsockopt(r->connection->fd, SOL_SOCKET, SO_LINGER,(const void *) &linger, sizeof(struct linger)) == -1){ngx_log_error(NGX_LOG_ALERT, log, ngx_socket_errno,"setsockopt(SO_LINGER) failed");}}}/* the various request strings were allocated from r->pool */ctx = log->data;ctx->request = NULL;r->request_line.len = 0;r->connection->destroyed = 1;ngx_destroy_pool(r->pool);
}
static void
ngx_http_close_connection(ngx_connection_t *c)
{ngx_pool_t  *pool;ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,"close http connection: %d", c->fd);#if (NGX_HTTP_SSL)if (c->ssl) {if (ngx_ssl_shutdown(c) == NGX_AGAIN) {c->ssl->handler = ngx_http_close_connection;return;}}#endif#if (NGX_STAT_STUB)(void) ngx_atomic_fetch_add(ngx_stat_active, -1);
#endifc->destroyed = 1;pool = c->pool;ngx_close_connection(c);ngx_destroy_pool(pool);
}
void
ngx_close_connection(ngx_connection_t *c)
{ngx_err_t     err;ngx_uint_t    log_error, level;ngx_socket_t  fd;if (c->fd == -1) {ngx_log_error(NGX_LOG_ALERT, c->log, 0, "connection already closed");return;}if (c->read->timer_set) {ngx_del_timer(c->read);}if (c->write->timer_set) {ngx_del_timer(c->write);}if (ngx_del_conn) {ngx_del_conn(c, NGX_CLOSE_EVENT);} else {if (c->read->active || c->read->disabled) {ngx_del_event(c->read, NGX_READ_EVENT, NGX_CLOSE_EVENT);}if (c->write->active || c->write->disabled) {ngx_del_event(c->write, NGX_WRITE_EVENT, NGX_CLOSE_EVENT);}}#if (NGX_THREADS)/** we have to clean the connection information before the closing* because another thread may reopen the same file descriptor* before we clean the connection*/ngx_mutex_lock(ngx_posted_events_mutex);if (c->read->prev) {ngx_delete_posted_event(c->read);}if (c->write->prev) {ngx_delete_posted_event(c->write);}c->read->closed = 1;c->write->closed = 1;if (c->single_connection) {ngx_unlock(&c->lock);c->read->locked = 0;c->write->locked = 0;}ngx_mutex_unlock(ngx_posted_events_mutex);#elseif (c->read->prev) {ngx_delete_posted_event(c->read);}if (c->write->prev) {ngx_delete_posted_event(c->write);}c->read->closed = 1;c->write->closed = 1;#endifngx_reusable_connection(c, 0);log_error = c->log_error;ngx_free_connection(c);fd = c->fd;c->fd = (ngx_socket_t) -1;if (ngx_close_socket(fd) == -1) {err = ngx_socket_errno;if (err == NGX_ECONNRESET || err == NGX_ENOTCONN) {switch (log_error) {case NGX_ERROR_INFO:level = NGX_LOG_INFO;break;case NGX_ERROR_ERR:level = NGX_LOG_ERR;break;default:level = NGX_LOG_CRIT;}} else {level = NGX_LOG_CRIT;}/* we use ngx_cycle->log because c->log was in c->pool */ngx_log_error(level, ngx_cycle->log, err,ngx_close_socket_n " %d failed", fd);}
}

nginx lingering_close相关推荐

  1. nginx基础概念(100%)之lingering_close

    lingering_close,字面意思就是延迟关闭,也就是说,当nginx要关闭连接时,并非立即关闭连接,而是先关闭tcp连接的写,再等待一段时间后再关掉连接的读.为什么要这样呢?我们先来看看这样一 ...

  2. Nginx学习笔记(一)

    Nginx初识 Nginx架构 Nginx在后台运行包含一个master进程和多个worker进程.所以Nginx一般以<u>多进程</u>方式运行,且支持**<u> ...

  3. nginx常用的超时配置说明

    client_header_timeout 语法 client_header_timeout time 默认值 60s 上下文 http server 说明 指定等待client发送一个请求头的超时时 ...

  4. nginx的请求接收流程(二)

    在ngx_http_process_request_line函数中,解析完请求行之后,如果请求行的uri里面包含了域名部分,则将其保持在请求结构的headers_in成员的server字段,heade ...

  5. Nginx学习之三-ngx_http_request_t结构体

    ngx_http_request_s是nginx中非常重要的一个结构体,贯穿于htpp请求处理的整个过程中. 下面解释了ngx_http_request_s结构体中与HTTP框架相关的重要的成员变量. ...

  6. nginx配置多个server_Nginx基本属性配置详解

    . Nginx服务的基本配置 1.1 用于调试进程和定位问题的配置项 是否以守护进程的方式运行nginx # 默认ondaemon on|off; 是否以master/worker方式工作 # 默认o ...

  7. NGINX 配置超时时间

    一.啥时候用到 前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家.点击跳转到教程. 用来设置请求资源和服务器返回的时间,保证一个请求占用固定时间,超出后报504超时!这 ...

  8. nginx日志中$request_time时间异常问题排查

    女主宣言 nginx日志分为access_log和error_log,可以用于业务的访问统计.服务排障等.我们可以自定义设置log_format,来记录关注的各项指标.本文主要讲述了一次nginx日志 ...

  9. nginx核心模块ngx_http_core_module详解

    简介 静态Web服务器的主要功能由ngx_http_core_module. 所有Http配置项都必须直属http块.server块.location块.upstream块等,所以HTTP配置项都必须 ...

最新文章

  1. 对网上花店系统进行软件测试,网上花店系统
  2. python3最好的书籍推荐-推荐几本Python3相关书籍?最好分一下基础、进阶、高级...
  3. dns的服务器地址是多少当前位置,dns的服务器地址设置为多少
  4. java 着色问题 回溯算法,C语言使用回溯法解旅行售货员问题与图的m着色问题
  5. 为什么敏捷开发在亚洲实行不了
  6. c语言定义数组变量初始化为0,c语言数组初始化——int a[N] = {0};
  7. 一级学科和二级学科_在多学科团队中工作的6个障碍(以及如何解决这些问题)
  8. 【渝粤题库】陕西师范大学700004 植物生理学
  9. 各j2ee web层框架比较(转)
  10. 什么是mysql事物定义_MySQL中事务概念的简洁学习教程
  11. everything如何搜索文件内容?(这软件搜索文件可以,搜索文件内容不行)
  12. Python常用模块-20个常用模块总结
  13. 如何批量PDF转换JPG
  14. 花瓣图形 -《跟小海龟学Python》案例代码
  15. Windows 10正式版官方原版ISO镜像下载汇总!!!
  16. javaScript案例——二级联动、三级联动、省市区三级联动
  17. 不了解喜欢的明星有什么关系?教你用Neo4j 快速构建明星关系图谱,让你比他自己还了解
  18. 卷积神经网络实现图像分类
  19. uniapp swiper 添加视频
  20. 电源设计中最常见的四种滤波电路原理及特点解析

热门文章

  1. CY3/CY3.5/CY5/CY5.5/CY7/CY7.5标记链霉亲和素 Streptavidin
  2. FpML to QuantLib 外滙美式選擇權估值
  3. 【Java基础学习】
  4. SpringMVC简易的程序以及基本原理
  5. C#/VB.NET 在PDF表格中添加条形码
  6. App安全架构之前端安全防护
  7. java中clone方法的理解(深拷贝、浅拷贝)
  8. 维谛技术(Vertiv):场景驱动的边缘计算
  9. 如何查看mac的电池损耗呢?
  10. hit@k 评价指标