TCP层close系统调用的实现分析
在调用close系统调用关闭套接字时,如果套接字引用计数已经归零,则需继续向上层调用其close实现,tcp为tcp_close;本文仅介绍tcp部分,前置部分请参考本博关于close系统调用的文章;
void tcp_close(struct sock *sk, long timeout)
{
struct sk_buff *skb;
int data_was_unread = ;
int state; lock_sock(sk);
sk->sk_shutdown = SHUTDOWN_MASK; /* LISTEN状态处理 */
if (sk->sk_state == TCP_LISTEN) {
/* 设置close状态 */
tcp_set_state(sk, TCP_CLOSE); /* Special case. */
/* 清理完成连接队列 */
inet_csk_listen_stop(sk); goto adjudge_to_death;
} /* We need to flush the recv. buffs. We do this only on the
* descriptor close, not protocol-sourced closes, because the
* reader process may not have drained the data yet!
*/
/* 删除接收队列中用户进程未读取的skb */
while ((skb = __skb_dequeue(&sk->sk_receive_queue)) != NULL) {
u32 len = TCP_SKB_CB(skb)->end_seq - TCP_SKB_CB(skb)->seq; /* 减去fin的一个序号长度 */
if (TCP_SKB_CB(skb)->tcp_flags & TCPHDR_FIN)
len--;
data_was_unread += len;
__kfree_skb(skb);
} sk_mem_reclaim(sk); /* If socket has been already reset (e.g. in tcp_reset()) - kill it. */
/* CLOSE状态 */
if (sk->sk_state == TCP_CLOSE)
goto adjudge_to_death; /* As outlined in RFC 2525, section 2.17, we send a RST here because
* data was lost. To witness the awful effects of the old behavior of
* always doing a FIN, run an older 2.1.x kernel or 2.0.x, start a bulk
* GET in an FTP client, suspend the process, wait for the client to
* advertise a zero window, then kill -9 the FTP client, wheee...
* Note: timeout is always zero in such a case.
*/
/* 修复状态,断开连接 */
if (unlikely(tcp_sk(sk)->repair)) {
sk->sk_prot->disconnect(sk, );
}
/* 用户进程有数据未读 */
else if (data_was_unread) {
/* Unread data was tossed, zap the connection. */
NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPABORTONCLOSE); /* 设置为close */
tcp_set_state(sk, TCP_CLOSE); /* 发送rst */
tcp_send_active_reset(sk, sk->sk_allocation);
}
/* lingertime==0,断开连接 */
else if (sock_flag(sk, SOCK_LINGER) && !sk->sk_lingertime) {
/* Check zero linger _after_ checking for unread data. */
sk->sk_prot->disconnect(sk, );
NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPABORTONDATA);
}
/* 关闭状态转移 */
else if (tcp_close_state(sk)) {
/* We FIN if the application ate all the data before
* zapping the connection.
*/ /* RED-PEN. Formally speaking, we have broken TCP state
* machine. State transitions:
*
* TCP_ESTABLISHED -> TCP_FIN_WAIT1
* TCP_SYN_RECV -> TCP_FIN_WAIT1 (forget it, it's impossible)
* TCP_CLOSE_WAIT -> TCP_LAST_ACK
*
* are legal only when FIN has been sent (i.e. in window),
* rather than queued out of window. Purists blame.
*
* F.e. "RFC state" is ESTABLISHED,
* if Linux state is FIN-WAIT-1, but FIN is still not sent.
*
* The visible declinations are that sometimes
* we enter time-wait state, when it is not required really
* (harmless), do not send active resets, when they are
* required by specs (TCP_ESTABLISHED, TCP_CLOSE_WAIT, when
* they look as CLOSING or LAST_ACK for Linux)
* Probably, I missed some more holelets.
* --ANK
* XXX (TFO) - To start off we don't support SYN+ACK+FIN
* in a single packet! (May consider it later but will
* probably need API support or TCP_CORK SYN-ACK until
* data is written and socket is closed.)
*/
/* 发送fin */
tcp_send_fin(sk);
} /* 等待关闭,状态为FIN_WAIT_1 CLOSING LAST_ACK或sk_lingertime超时 */
sk_stream_wait_close(sk, timeout); adjudge_to_death:
state = sk->sk_state;
sock_hold(sk); /* 设置为DEAD状态 */
sock_orphan(sk); /* It is the last release_sock in its life. It will remove backlog. */
/* 删除控制块的backlog、cb */
release_sock(sk); /* Now socket is owned by kernel and we acquire BH lock
to finish close. No need to check for user refs.
*/
local_bh_disable();
bh_lock_sock(sk);
WARN_ON(sock_owned_by_user(sk)); /* 增加孤儿计数 */
percpu_counter_inc(sk->sk_prot->orphan_count); /* Have we already been destroyed by a softirq or backlog? */
/* 被软中断或者backlog销毁了????? */
if (state != TCP_CLOSE && sk->sk_state == TCP_CLOSE)
goto out; /* This is a (useful) BSD violating of the RFC. There is a
* problem with TCP as specified in that the other end could
* keep a socket open forever with no application left this end.
* We use a 1 minute timeout (about the same as BSD) then kill
* our end. If they send after that then tough - BUT: long enough
* that we won't make the old 4*rto = almost no time - whoops
* reset mistake.
*
* Nope, it was not mistake. It is really desired behaviour
* f.e. on http servers, when such sockets are useless, but
* consume significant resources. Let's do it with special
* linger2 option. --ANK
*/ if (sk->sk_state == TCP_FIN_WAIT2) {
struct tcp_sock *tp = tcp_sk(sk);
/* linger2小于0,无需等待 */
if (tp->linger2 < ) { /* 转到CLOSE */
tcp_set_state(sk, TCP_CLOSE);
/* 发送rst */
tcp_send_active_reset(sk, GFP_ATOMIC);
__NET_INC_STATS(sock_net(sk),
LINUX_MIB_TCPABORTONLINGER);
} else { /* 获取FIN_WAIT_2超时时间 */
const int tmo = tcp_fin_time(sk); /* FIN_WAIT_2超时时间> timewait时间,加FIN_WAIT_2定时器 */
if (tmo > TCP_TIMEWAIT_LEN) {
inet_csk_reset_keepalive_timer(sk,
tmo - TCP_TIMEWAIT_LEN);
}
/* 小于TIME_WAIT时间,则进入TIME_WAIT */
else {
tcp_time_wait(sk, TCP_FIN_WAIT2, tmo);
goto out;
}
}
} /* 未处于CLOSE */
if (sk->sk_state != TCP_CLOSE) {
sk_mem_reclaim(sk); /* 孤儿数量过多,或者socket内存过多 */
if (tcp_check_oom(sk, )) {
/* 进入CLOSE */
tcp_set_state(sk, TCP_CLOSE);
/* 发送rst */
tcp_send_active_reset(sk, GFP_ATOMIC);
__NET_INC_STATS(sock_net(sk),
LINUX_MIB_TCPABORTONMEMORY);
}
} /* 处于CLOSE */
if (sk->sk_state == TCP_CLOSE) {
struct request_sock *req = tcp_sk(sk)->fastopen_rsk;
/* We could get here with a non-NULL req if the socket is
* aborted (e.g., closed with unread data) before 3WHS
* finishes.
*/
if (req)
reqsk_fastopen_remove(sk, req, false); /* 销毁控制块 */
inet_csk_destroy_sock(sk);
}
/* Otherwise, socket is reprieved until protocol close. */ out:
bh_unlock_sock(sk);
local_bh_enable();
sock_put(sk);
}
TCP层close系统调用的实现分析的更多相关文章
- TCP层sendmsg系统调用的实现分析
概述 sendmsg系统调用在tcp层的实现是tcp_sendmsg函数,该函数完成以下任务:从用户空间读取数据,拷贝到内核skb,将skb加入到发送队列的任务,调用发送函数:函数在执行过程中会锁定控 ...
- TCP层recvmsg系统调用的实现分析
概述 recvmsg系统调用在tcp层的实现是tcp_recvmsg函数,该函数完成从接收队列中读取数据复制到用户空间的任务:函数在执行过程中会锁定控制块,避免软中断在tcp层的影响:函数会涉及从接收 ...
- TCP层shutdown系统调用的实现分析
概述 shutdown系统调用在tcp层会调用两个函数,对于ESTABLISHED状态需要调用tcp_shutdown关闭连接,对于LISTEN和SYN_SENT状态则需要以非阻塞模式调用tcp_di ...
- TCP层accept系统调用的实现分析
inet_csk_accept函数实现了tcp协议accept操作,其主要完成的功能是,从已经完成三次握手的队列中取控制块,如果没有已经完成的连接,则需要根据阻塞标记来来区分对待,若非阻塞则直接返回, ...
- TCP层bind系统调用的实现分析
说明:该文章中部分代码未能完全理解透彻,可能对您造成误解,请慎读: 并建议您先阅读本博另外一篇文章:<Linux TCP套接字选项 之 SO_REUSEADDR && SO_RE ...
- Python Tornado框架(TCP层)
Tornado在TCP层里的工作机制 上一节是关于应用层的协议 HTTP,它依赖于传输层协议 TCP,例如服务器是如何绑定端口的?HTTP 服务器的 handle_stream 是在什么时候被调用的呢 ...
- TCP拥塞控制算法 优缺点 适用环境 性能分析
[摘要]对多种TCP拥塞控制算法进行简要说明,指出它们的优缺点.以及它们的适用环境. [关键字]TCP拥塞控制算法 优点 缺点 适用环境公平性 公平性 公平性是在发生拥塞时各源端(或同一源端 ...
- Android系统进程间通信Binder机制在应用程序框架层的Java接口源代码分析
文章转载至CSDN社区罗升阳的安卓之旅,原文地址:http://blog.csdn.net/luoshengyang/article/details/6642463 在前面几篇文章中,我们详细介绍了A ...
- TCP层的分段和IP层的分片之间的关系 & MTU和MSS之间的关系 (转载)
首先说明:数据报的分段和分片确实发生,分段发生在传输层,分片发生在网络层.但是对于分段来说,这是经常发生在UDP传输层协议上的情况,对于传输层使用TCP协议的通道来说,这种事情很少发生. 1,MTU( ...
随机推荐
- 佳能单反SDK 步骤
EdsInitializeSDK(); EdsGetCameraList(&eclr);//获取相机列表 EdsGetChildCount(eclr, &camCount); //获 ...
- spring AOP的相关术语
连接点:Joinpoint 其实业务层接口的方法 切入点:Pointcut 被增强的是切入点,没被增强是永远都是连接点.连接点不一定是切入点,切入点一定是连接点 通知:Advice 就是指要增强的 ...
- vue入门:(模板语法与指令)
vuejs使用及HTML的模板语法,可以实现声明式将DOM绑定至底层VUE实例的数据.通过模板语法将数据渲染进DOM的系统,结合响应系统,在应用状态改变时,Vue能够计算出重新渲染组件的最小代价并应用 ...
- .NET中跨线程访问winform控件的方法
1 第一种方式 MethodInvoker invoker = () => { richTextBox1.AppendText(_ClientSocketModelConnectedEvent. ...
- 转载: Linux查看系统开机时间
转自: https://www.cnblogs.com/kerrycode/p/3759395.html 查看Linux系统运行了多久时间,此时需要知道上次开机启动时间: 有时候由于断电或供电故障突然 ...
- python中进制转换
使用Python内置函数:bin().oct().int().hex()可实现进制转换. 先看Python官方文档中对这几个内置函数的描述: bin(x)Convert an integer numb ...
- Intellij IDEA导入java项目看不到左边的项目目录结构
1 重新import项目 然后导入完成,就可以了,再不行的话,删除.idea文件,重新import整个Project
- Oracle【账户管理】
Oracle学习大致体系oracle管理系统介绍(客户端和服务器端的交互模式)oracle数据库的数据管理(增删改查 查询)oracle账户管理oracle二维表管理 --创建表 --维护表 ...
- onItemSelected 获取选中的 信息 3种方法
@Override public void onItemSelected(AdapterView<?> parent, View view, int position, long id) ...
- 8.9.网络编程_Socket 远程调用机制
1.网络编程 1.1.网络编程概述: 通过通信线路(有线或无线)可以把不同地理位置且相互独立的计算机连同其外部设备连接起来,组成计算机网络.在操作系统.网络管理软件及网络 通信协议的管理和协调下,可以 ...