linux内核中tcp连接的断开处理

42 篇文章 1 订阅
21 篇文章 0 订阅
我们这次主要来分析相关的两个断开函数close和shotdown以及相关的套接口选项SO_LINGER。这里要注意SO_LINGER对shutdown无任何影响。它只对close起作用。 

先来坎SO_LINGER所对应的数据结构: 

Java代码   收藏代码
  1. struct linger {  
  2. ///linger的开关  
  3.     int     l_onoff;    /* Linger active        */  
  4. ///所等待的时间。  
  5.     int     l_linger;   /* How long to linger for   */  
  6. };  


这里对这个套接口选项就不详细介绍了,在unix网络编程中有详细的介绍,我们这里只会分析内核的处理代码。 

首先来看close函数,我们知道缺醒情况下,close是立即返回,但是如果套接口的发送缓冲区还有未发送的数据,系统将会试着把这些数据发送给对端。而这个缺醒情况我们是可以通过SO_LINGER来改变的。还有一个要注意就是close调用并不一定会引发tcp的断开连接。因为close只是将这个socket的引用计数减一(主要是针对多个进程),而真正要直接引发断开,则需要用shutdown函数。 

内核中socket的close的系统调用是sock_close,而在sock_close中,直接调用sock_release来实现功能,因此这里我们直接看sock_release的源码: 

Java代码   收藏代码
  1. void sock_release(struct socket *sock)  
  2. {  
  3.     if (sock->ops) {  
  4.         struct module *owner = sock->ops->owner;  
  5.   
  6. ///调用inet_stream_ops的inet_release函数  
  7.         sock->ops->release(sock);  
  8. ///将ops致空。  
  9.         sock->ops = NULL;  
  10.         module_put(owner);  
  11.     }  
  12.   
  13. ///这个域貌似是26.31新加的,具体做什么的还不知道。  
  14.     if (sock->fasync_list)  
  15.         printk(KERN_ERR "sock_release: fasync list not empty!\n");  
  16.   
  17. ///更新全局的socket数目  
  18.     percpu_sub(sockets_in_use, 1);  
  19.     if (!sock->file) {  
  20. ///更新inode的引用计数  
  21.         iput(SOCK_INODE(sock));  
  22.         return;  
  23.     }  
  24.     sock->file = NULL;  
  25. }  


然后来看inet_release的实现,这个函数主要用来通过SO_LINGER套接字来得到超时时间,然后调用tcp_close来关闭sock。 

Java代码   收藏代码
  1. int inet_release(struct socket *sock)  
  2. {  
  3.     struct sock *sk = sock->sk;  
  4.   
  5.     if (sk) {  
  6.         long timeout;  
  7.   
  8.         /* Applications forget to leave groups before exiting */  
  9.         ip_mc_drop_socket(sk);  
  10.   
  11.         timeout = 0;  
  12. ///判断是否设置SO_LINGER并且不是处于正在shutdowning,则设置timeout为l_linger(也就是我们设置的值).  
  13.         if (sock_flag(sk, SOCK_LINGER) &&  
  14.             !(current->flags & PF_EXITING))  
  15.             timeout = sk->sk_lingertime;  
  16.         sock->sk = NULL;  
  17. ///调用tcp_close.  
  18.         sk->sk_prot->close(sk, timeout);  
  19.     }  
  20.     return 0;  
  21. }  


tcp_close函数比较长我们这里分段来分析它,首先来看第一部分。这里要注意几点: 

1 当close掉一个服务端的父socket的时候,内核会先处理半连接队列然后是已经accept了的队列,最后才会处理父sock。
 
2 处理接收缓冲区的数据的时候,直接遍历receive_queue(前面blog有介绍),然后统计未发送的socket。我们知道close是不管接收buf的,也就是他会把接收buf释放掉,然后发送rst给对端的。
 
3 当so_linger有设置并且超时时间为0,则发送rst给对端,并且清空发送和接收buf。这个也不会引起最终的四分组终止序列。
 
4 当接收缓冲区有未读数据,则直接发送rst给对端。这个也不会引起最终的四分组终止序列。
 
5 当so_linger有设置,并且超时不为0,或者so_linger没有设置,此时都会引起最终的四分组终止序列来终止连接。(通过send_fin来发送fin,并引发四分组终止序列).而在send_fin中会发送掉发送缓冲区中的数据。



来看代码: 

Java代码   收藏代码
  1. void tcp_close(struct sock *sk, long timeout)  
  2. {  
  3.     struct sk_buff *skb;  
  4.     int data_was_unread = 0;  
  5.     int state;  
  6.   
  7.     lock_sock(sk);  
  8.     sk->sk_shutdown = SHUTDOWN_MASK;  
  9.   
  10. ///如果处于tcp_listen说明将要关闭的这个socket是一个服务端的主socket。  
  11.     if (sk->sk_state == TCP_LISTEN) {  
  12. ///设置sock状态.  
  13.         tcp_set_state(sk, TCP_CLOSE);  
  14.   
  15. ///这个函数主要用来清理半连接队列(下面会简要分析这个函数)  
  16.         /* Special case. */  
  17.         inet_csk_listen_stop(sk);  
  18. ///处理要关闭的sock  
  19.         goto adjudge_to_death;  
  20.     }  
  21.   
  22. ///遍历sk_receive_queue也就是输入buf队列。然后统计还没有读取的数据。  
  23.     while ((skb = __skb_dequeue(&sk->sk_receive_queue)) != NULL) {  
  24.         u32 len = TCP_SKB_CB(skb)->end_seq - TCP_SKB_CB(skb)->seq -  
  25.               tcp_hdr(skb)->fin;  
  26.         data_was_unread += len;  
  27. ///free这个skb  
  28.         __kfree_skb(skb);  
  29.     }  
  30.   
  31.     sk_mem_reclaim(sk);  
  32.   
  33.   
  34. ///第一个if主要是实现了rfc2525的2.17,也就是关闭的时候,如果接收buf中有未读数据,则发送一个rst给对端。(下面有摘抄相关内容)  
  35.     if (data_was_unread) {  
  36.         /* Unread data was tossed, zap the connection. */  
  37.         NET_INC_STATS_USER(sock_net(sk), LINUX_MIB_TCPABORTONCLOSE);  
  38. ///设置状态  
  39.         tcp_set_state(sk, TCP_CLOSE);  
  40. ///发送rst  
  41.         tcp_send_active_reset(sk, GFP_KERNEL);  
  42.     }   
  43. ///第二个if主要是判断so_linger套接字,并且超时时间为0。此时我们就直接丢掉所有的发送缓冲区中的数据  
  44. else if (sock_flag(sk, SOCK_LINGER) && !sk->sk_lingertime) {  
  45.         /* Check zero linger _after_ checking for unread data. */  
  46. ///调用tcp_disconnect,这个函数主要用来断开和对端的连接,这个函数下面会介绍。  
  47.         sk->sk_prot->disconnect(sk, 0);  
  48.         NET_INC_STATS_USER(sock_net(sk), LINUX_MIB_TCPABORTONDATA);  
  49.     }   
  50. ///这个函数主要用来判断是否需要发送fin,也就是判断状态。下面我会详细介绍这个函数。  
  51. else if (tcp_close_state(sk)) {  
  52.   
  53. ///发送fin.  
  54.         tcp_send_fin(sk);  
  55.     }  
  56.   
  57. ///等待一段时间。这里的timeout,如果有设置so_linger的话就是l_linger.这里主要是等待发送缓冲区的buf发送(如果超时时间不为0).  
  58.     sk_stream_wait_close(sk, timeout);  
  59. ........................  
  60.   
  61. }  


rfc2525的2.17的介绍: 

Java代码   收藏代码
  1. When an application closes a connection in such a way that it can no longer read any received data, the TCP SHOULD, per section 4.2.2.13 of RFC 1122, send a RST if there is any unread received data, or if any new data is received. A TCP that fails to do so exhibits "Failure to RST on close with data pending".  


ok,现在来看上面遇到的3个函数,一个是inet_csk_listen_stop,一个是tcp_close_state,一个是tcp_disconnect.我们一个个来看他们。 

首先是inet_csk_listen_stop函数。我们知道这个函数主要用来清理所有的半连接队列。 

Java代码   收藏代码
  1. void inet_csk_listen_stop(struct sock *sk)  
  2. {  
  3.     struct inet_connection_sock *icsk = inet_csk(sk);  
  4.     struct request_sock *acc_req;  
  5.     struct request_sock *req;  
  6.   
  7. ///首先删除keepalive定时器。  
  8.     inet_csk_delete_keepalive_timer(sk);  
  9.   
  10.     /* make all the listen_opt local to us */  
  11. ///得到accept 队列。  
  12.     acc_req = reqsk_queue_yank_acceptq(&icsk->icsk_accept_queue);  
  13.   
  14. ///然后销毁掉所有的半连接队列,也就是listen_sock队列  
  15.     reqsk_queue_destroy(&icsk->icsk_accept_queue);  
  16.   
  17.   
  18. ///遍历accept队列断开与对端的连接。  
  19.     while ((req = acc_req) != NULL) {  
  20. ...............................................  
  21.   
  22. ///调用tcp_disconnect来断开与对端的连接。这里注意是非阻塞的。  
  23.         sk->sk_prot->disconnect(child, O_NONBLOCK);  
  24.   
  25.         sock_orphan(child);  
  26.   
  27.         percpu_counter_inc(sk->sk_prot->orphan_count);  
  28.   
  29. ///销毁这个sock。  
  30.         inet_csk_destroy_sock(child);  
  31.   
  32. ........................................  
  33.     }  
  34.     WARN_ON(sk->sk_ack_backlog);  
  35. }  


接下来来看tcp_disconnect函数。这个函数主要用来断开和对端的连接.它会释放读写队列,发送rst,清除定时器等等一系列操作。 

Java代码   收藏代码
  1. int tcp_disconnect(struct sock *sk, int flags)  
  2. {  
  3.     struct inet_sock *inet = inet_sk(sk);  
  4.     struct inet_connection_sock *icsk = inet_csk(sk);  
  5.     struct tcp_sock *tp = tcp_sk(sk);  
  6.     int err = 0;  
  7.     int old_state = sk->sk_state;  
  8.   
  9.     if (old_state != TCP_CLOSE)  
  10.         tcp_set_state(sk, TCP_CLOSE);  
  11. ...................  
  12.   
  13. ///清除定时器,重传,delack等。  
  14.     tcp_clear_xmit_timers(sk);  
  15. ///直接free掉接收buf。  
  16.     __skb_queue_purge(&sk->sk_receive_queue);  
  17. ///free掉写buf。  
  18.     tcp_write_queue_purge(sk);  
  19.     __skb_queue_purge(&tp->out_of_order_queue);  
  20. #ifdef CONFIG_NET_DMA  
  21.     __skb_queue_purge(&sk->sk_async_wait_queue);  
  22. #endif  
  23.   
  24.     inet->dport = 0;  
  25.   
  26.     if (!(sk->sk_userlocks & SOCK_BINDADDR_LOCK))  
  27.         inet_reset_saddr(sk);  
  28. ..........................................  
  29. ///设置状态。  
  30.     tcp_set_ca_state(sk, TCP_CA_Open);  
  31. ///清理掉重传的一些标记  
  32.     tcp_clear_retrans(tp);  
  33.     inet_csk_delack_init(sk);  
  34.     tcp_init_send_head(sk);  
  35.     memset(&tp->rx_opt, 0, sizeof(tp->rx_opt));  
  36.     __sk_dst_reset(sk);  
  37.   
  38.     WARN_ON(inet->num && !icsk->icsk_bind_hash);  
  39.   
  40.     sk->sk_error_report(sk);  
  41.     return err;  
  42. }  



紧接着是tcp_close_state函数这个函数就是用来判断是否应该发送fin: 

Java代码   收藏代码
  1. ///这个数组表示了当close后,tcp的状态变化,可以看到注释很清楚,包含了3部分。这里也就是通过current也就是tcp的状态取得new state也就是close的状态,然后再和TCP_ACTION_FIN按位于,得到action  
  2. static const unsigned char new_state[16] = {  
  3.   /* current state:        new state:      action:  */  
  4.   /* (Invalid)      */ TCP_CLOSE,  
  5.   /* TCP_ESTABLISHED    */ TCP_FIN_WAIT1 | TCP_ACTION_FIN,  
  6.   /* TCP_SYN_SENT   */ TCP_CLOSE,  
  7.   /* TCP_SYN_RECV   */ TCP_FIN_WAIT1 | TCP_ACTION_FIN,  
  8.   /* TCP_FIN_WAIT1  */ TCP_FIN_WAIT1,  
  9.   /* TCP_FIN_WAIT2  */ TCP_FIN_WAIT2,  
  10.   /* TCP_TIME_WAIT  */ TCP_CLOSE,  
  11.   /* TCP_CLOSE      */ TCP_CLOSE,  
  12.   /* TCP_CLOSE_WAIT */ TCP_LAST_ACK  | TCP_ACTION_FIN,  
  13.   /* TCP_LAST_ACK   */ TCP_LAST_ACK,  
  14.   /* TCP_LISTEN     */ TCP_CLOSE,  
  15.   /* TCP_CLOSING    */ TCP_CLOSING,  
  16. };  
  17.   
  18. static int tcp_close_state(struct sock *sk)  
  19. {  
  20. ///取得new state  
  21.     int next = (int)new_state[sk->sk_state];  
  22.     int ns = next & TCP_STATE_MASK;  
  23.   
  24.     tcp_set_state(sk, ns);  
  25.   
  26. ///得到action  
  27.     return next & TCP_ACTION_FIN;  
  28. }  



接下来来看tcp_close的剩余部分的代码,剩下的部分就是处理一些状态以及通知这里只有一个要注意的就是TCP_LINGER2这个套接字,这个套接字能够设置等待fin的超时时间,也就是tcp_sock的域linger2.我们知道系统还有一个sysctl_tcp_fin_timeout,也就是提供了一个sys文件系统的接口来修改这个值,不过我们如果设置linger2为一个大于0的值的话,内核就会取linger2这个值。 

Java代码   收藏代码
  1. adjudge_to_death:  
  2.   
  3. ///得到sock的状态。  
  4.     state = sk->sk_state;  
  5.     sock_hold(sk);  
  6.     sock_orphan(sk);  
  7.   
  8. ///唤醒阻塞在这个sock的队列(前面有详细介绍这个函数)  
  9.     release_sock(sk);  
  10.   
  11.     local_bh_disable();  
  12.     bh_lock_sock(sk);  
  13.     WARN_ON(sock_owned_by_user(sk));  
  14.   
  15. ///全局的cpu变量引用计数减一。  
  16.     percpu_counter_inc(sk->sk_prot->orphan_count);  
  17.   
  18.     /* Have we already been destroyed by a softirq or backlog? */  
  19.     if (state != TCP_CLOSE && sk->sk_state == TCP_CLOSE)  
  20.         goto out;  
  21.   
  22. ///如果状态为TCP_FIN_WAIT2,说明接收了ack,在等待对端的fin。  
  23.     if (sk->sk_state == TCP_FIN_WAIT2) {  
  24.         struct tcp_sock *tp = tcp_sk(sk);  
  25. ///超时时间小于0,则说明马上超时,设置状态为tcp_close,然后发送rst给对端。  
  26.         if (tp->linger2 < 0) {  
  27.             tcp_set_state(sk, TCP_CLOSE);  
  28.             tcp_send_active_reset(sk, GFP_ATOMIC);  
  29.             NET_INC_STATS_BH(sock_net(sk),  
  30.                     LINUX_MIB_TCPABORTONLINGER);  
  31.         } else {  
  32. ///得到等待fin的超时时间。这里主要也就是在linger2和sysctl_tcp_fin_timeout中来取得。  
  33.             const int tmo = tcp_fin_time(sk);  
  34. ///如果超时时间太长,则启动keepalive定时器发送探测报。  
  35.             if (tmo > TCP_TIMEWAIT_LEN) {  
  36.                 inet_csk_reset_keepalive_timer(sk,  
  37.                         tmo - TCP_TIMEWAIT_LEN);  
  38.             } else {  
  39. ///否则进入time_wait状态。  
  40.                 tcp_time_wait(sk, TCP_FIN_WAIT2, tmo);  
  41.                 goto out;  
  42.             }  
  43.         }  
  44.     }  
  45. ......................................  
  46.   
  47. ///如果sk的状态为tcp_close则destroy掉这个sk  
  48.     if (sk->sk_state == TCP_CLOSE)  
  49.         inet_csk_destroy_sock(sk);  
  50.     /* Otherwise, socket is reprieved until protocol close. */  
  51.   
  52. out:  
  53.     bh_unlock_sock(sk);  
  54.     local_bh_enable();  
  55.     sock_put(sk);  
  56. }  


然后来看send_fin的实现,这个函数用来发送一个fin,并且尽量发送完发送缓冲区中的数据: 

Java代码   收藏代码
  1. void tcp_send_fin(struct sock *sk)  
  2. {  
  3. struct tcp_sock *tp = tcp_sk(sk);  
  4. ///取得写bufer的尾部。  
  5.     struct sk_buff *skb = tcp_write_queue_tail(sk);  
  6.     int mss_now;  
  7.   
  8.     /* Optimization, tack on the FIN if we have a queue of 
  9.      * unsent frames.  But be careful about outgoing SACKS 
  10.      * and IP options. 
  11.      */  
  12.     mss_now = tcp_current_mss(sk);  
  13. ///如果发送队列不为空,此时我们只需要设置sk buffer的标记位(也就是tcp报文的控制位为fin),可以看到我们是加到写buffer的尾部,这里是为了能尽量将写buffer中的数据全部传出)  
  14.     if (tcp_send_head(sk) != NULL) {  
  15.         TCP_SKB_CB(skb)->flags |= TCPCB_FLAG_FIN;  
  16.         TCP_SKB_CB(skb)->end_seq++;  
  17.         tp->write_seq++;  
  18.     } else {  
  19. ..................................  
  20. ///到这里标明发送缓冲区位空,因此我们需要新建一个sk buffer,然后设置标记位,并加入到写buffer。  
  21.         skb_reserve(skb, MAX_TCP_HEADER);  
  22.         /* FIN eats a sequence byte, write_seq advanced by tcp_queue_skb(). */  
  23.         tcp_init_nondata_skb(skb, tp->write_seq,  
  24.                      TCPCB_FLAG_ACK | TCPCB_FLAG_FIN);  
  25.         tcp_queue_skb(sk, skb);  
  26.     }  
  27. ///发送写缓冲区中的数据。  
  28.     __tcp_push_pending_frames(sk, mss_now, TCP_NAGLE_OFF);  
  29. }  
  30. void __tcp_push_pending_frames(struct sock *sk, unsigned int cur_mss,  
  31.                    int nonagle)  
  32. {  
  33.     struct sk_buff *skb = tcp_send_head(sk);  
  34.   
  35.     if (!skb)  
  36.         return;  
  37.   
  38.     /* If we are closed, the bytes will have to remain here. 
  39.      * In time closedown will finish, we empty the write queue and 
  40.      * all will be happy. 
  41.      */  
  42.     if (unlikely(sk->sk_state == TCP_CLOSE))  
  43.         return;  
  44. ///发送数据,这里关闭了nagle。也就是立即将数据全部发送出去(我前面的blog有详细解释这个函数).  
  45.     if (tcp_write_xmit(sk, cur_mss, nonagle, 0, GFP_ATOMIC))  
  46.         tcp_check_probe_timer(sk);  
  47. }  



接下来来看shutdown的实现。在2.26.31中,系统调用的实现有些变化。 

这里我们要知道shutdown会将写缓冲区的数据发出,然后唤醒阻塞的进程,来读取读缓冲区中的数据。 


这个系统调用所对应的内核函数就是os_shutdown_socket。 


Java代码   收藏代码
  1. #define SHUT_RD 0  
  2. #define SHUT_WR 1  
  3. #define SHUT_RDWR 2  
  4.   
  5. int os_shutdown_socket(int fd, int r, int w)  
  6. {  
  7.     int what, err;  
  8.   
  9.     if (r && w)  
  10.         what = SHUT_RDWR;  
  11.     else if (r)  
  12.         what = SHUT_RD;  
  13.     else if (w)  
  14.         what = SHUT_WR;  
  15.     else  
  16.         return -EINVAL;  
  17.   
  18. ///调用socket的shutdown也就是kernel_sock_shutdown  
  19.     err = shutdown(fd, what);  
  20.     if (err < 0)  
  21.         return -errno;  
  22.     return 0;  
  23. }  
  24.   
  25.   
  26. int kernel_sock_shutdown(struct socket *sock, enum sock_shutdown_cmd how)  
  27. {  
  28. ///他最终会调用inet_shutdown  
  29.     return sock->ops->shutdown(sock, how);  
  30. }  


来看inet_shutdown的实现.这个函数的主要工作就是通过判断sock的状态不同来调用相关的函数: 


Java代码   收藏代码
  1. int inet_shutdown(struct socket *sock, int how)  
  2. {  
  3.     struct sock *sk = sock->sk;  
  4.     int err = 0;  
  5.   
  6.     /* This should really check to make sure 
  7.      * the socket is a TCP socket. (WHY AC...) 
  8.      */  
  9. ///这里要注意每个how都是加1的,这说明在内核里读写是为1,2,3  
  10.     how++; /* maps 0->1 has the advantage of making bit 1 rcvs and 
  11.                1->2 bit 2 snds. 
  12.                2->3 */  
  13. ///判断how的合法性。  
  14.     if ((how & ~SHUTDOWN_MASK) || !how) /* MAXINT->0 */  
  15.         return -EINVAL;  
  16. ///锁住sock  
  17.     lock_sock(sk);  
  18.   
  19. ///SS_CONNECTING说明这个sock的连接正在处理中。state域表示socket当前的内部状态  
  20.     if (sock->state == SS_CONNECTING) {  
  21. ///如果状态为这几个状态,说明是处于半连接处理阶段,此时设置状态为SS_DISCONNECTING  
  22.         if ((1 << sk->sk_state) &  
  23.             (TCPF_SYN_SENT | TCPF_SYN_RECV | TCPF_CLOSE))  
  24.             sock->state = SS_DISCONNECTING;  
  25.         else  
  26. ///否则设置为连接完毕  
  27.             sock->state = SS_CONNECTED;  
  28.     }  
  29.   
  30. ///除过TCP_LISTEN以及TCP_SYN_SENT状态外的其他状态最终都会进入sk->sk_prot->shutdown也就是tcp_shutdown函数。  
  31.   
  32.     switch (sk->sk_state) {  
  33. ///如果状态为tco_close则设置错误号,然后进入default处理  
  34.     case TCP_CLOSE:  
  35.         err = -ENOTCONN;  
  36.         /* Hack to wake up other listeners, who can poll for 
  37.            POLLHUP, even on eg. unconnected UDP sockets -- RR */  
  38.     default:  
  39.         sk->sk_shutdown |= how;  
  40.         if (sk->sk_prot->shutdown)  
  41.             sk->sk_prot->shutdown(sk, how);  
  42.         break;  
  43.   
  44.     /* Remaining two branches are temporary solution for missing 
  45.      * close() in multithreaded environment. It is _not_ a good idea, 
  46.      * but we have no choice until close() is repaired at VFS level. 
  47.      */  
  48.     case TCP_LISTEN:  
  49. ///如果不为SHUT_RD则跳出switch,否则进入tcp_syn_sent的处理。  
  50.         if (!(how & RCV_SHUTDOWN))  
  51.             break;  
  52.         /* Fall through */  
  53.     case TCP_SYN_SENT:  
  54. ///断开连接,然后设置state  
  55.         err = sk->sk_prot->disconnect(sk, O_NONBLOCK);  
  56.         sock->state = err ? SS_DISCONNECTING : SS_UNCONNECTED;  
  57.         break;  
  58.     }  
  59.   
  60.     /* Wake up anyone sleeping in poll. */  
  61. ///唤醒阻塞在这个socket上的进程,这里是为了将读缓冲区的数据尽量读完。  
  62.     sk->sk_state_change(sk);  
  63.     release_sock(sk);  
  64.     return err;  
  65. }  



来看tcp_shutdown函数。 

这里要注意,当只关闭读的话,并不会引起发送fin,也就是只会设置个标记,然后在读取数据的时候返回错误。而关闭写端,则就会引起发送fin。 
Java代码   收藏代码
  1. void tcp_shutdown(struct sock *sk, int how)  
  2. {  
  3.     /*  We need to grab some memory, and put together a FIN, 
  4.      *  and then put it into the queue to be sent. 
  5.      *      Tim MacKenzie(tym@dibbler.cs.monash.edu.au) 4 Dec '92. 
  6.      */  
  7. ///如果为SHUT_RD则直接返回。  
  8.     if (!(how & SEND_SHUTDOWN))  
  9.         return;  
  10.   
  11.     /* If we've already sent a FIN, or it's a closed state, skip this. */  
  12. ///这里英文注释很详细我就不多解释了。  
  13.     if ((1 << sk->sk_state) &  
  14.         (TCPF_ESTABLISHED | TCPF_SYN_SENT |  
  15.          TCPF_SYN_RECV | TCPF_CLOSE_WAIT)) {  
  16.         /* Clear out any half completed packets.  FIN if needed. */  
  17. ///和tcp_close那边处理一样  
  18.         if (tcp_close_state(sk))  
  19.             tcp_send_fin(sk);  
  20.     }  
  21. }  


最后来看sock_def_readable它就是sk->sk_state_change。也就是用来唤醒阻塞的进程。 

Java代码   收藏代码
  1. static void sock_def_readable(struct sock *sk, int len)  
  2. {  
  3.     read_lock(&sk->sk_callback_lock);  
  4. ///判断是否有进程在等待这个sk  
  5.     if (sk_has_sleeper(sk))  
  6. ///有的话,唤醒进程,这里可以看到递交给上层的是POLLIN,也就是读事件。  
  7.     wake_up_interruptible_sync_poll(sk->sk_sleep, POLLIN |  
  8.                         POLLRDNORM | POLLRDBAND);  
  9.   
  10. ///这里异步唤醒,可以看到这里也是POLL_IN.  
  11.     sk_wake_async(sk, SOCK_WAKE_WAITD, POLL_IN);  
  12.     read_unlock(&sk->sk_callback_lock);  
  13. }  


可以看到shutdown函数只会处理SEND_SHUTDOWN。并且当调用shutdown之后,读缓冲区,还可以继续读取。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值