Linux内核--网络协议栈深入分析(五)--套接字的绑定、监听、连接和断开

本文分析基于Linux Kernel 3.2.1

原创作品,转载请标明http://blog.csdn.net/yming0221/article/details/7996528

更多请查看专栏http://blog.csdn.net/column/details/linux-kernel-net.html

作者:闫明


1、套接字的绑定

创建完套接字服务器端会在应用层使用bind函数进行套接字的绑定,这时会产生系统调用,sys_bind内核函数进行套接字。

系统调用函数的具体实现

  1. SYSCALL_DEFINE3(bind, int, fd, struct sockaddr __user *, umyaddr, int, addrlen)  
  2. {  
  3.     struct socket *sock;  
  4.     struct sockaddr_storage address;  
  5.     int err, fput_needed;  
  6.   
  7.     sock = sockfd_lookup_light(fd, &err, &fput_needed);  
  8.     if (sock) {  
  9.         err = move_addr_to_kernel(umyaddr, addrlen, (struct sockaddr *)&address);  
  10.         if (err >= 0) {  
  11.             err = security_socket_bind(sock,  
  12.                            (struct sockaddr *)&address,  
  13.                            addrlen);  
  14.             if (!err)  
  15.                 err = sock->ops->bind(sock,  
  16.                               (struct sockaddr *)  
  17.                               &address, addrlen);  
  18.         }  
  19.         fput_light(sock->file, fput_needed);  
  20.     }  
  21.     return err;  
  22. }  
首先调用函数sockfd_lookup_light()函数通过文件描述符来查找对应的套接字sock。

  1. static struct socket *sockfd_lookup_light(int fd, int *err, int *fput_needed)  
  2. {  
  3.     struct file *file;  
  4.     struct socket *sock;  
  5.   
  6.     *err = -EBADF;  
  7.     file = fget_light(fd, fput_needed);  
  8.     if (file) {  
  9.         sock = sock_from_file(file, err);  
  10.         if (sock)  
  11.             return sock;  
  12.         fput_light(file, *fput_needed);  
  13.     }  
  14.     return NULL;  
  15. }  

上面函数中先调用fget_light函数通过文件描述符返回对应的文件结构,然后调用函数sock_from_file函数返回该文件对应的套接字结构体地址,它存储在file->private_data属性中。

再回到sys_bind函数,在返回了对应的套接字结构之后,调用move_addr_to_kernel将用户地址空间的socket拷贝到内核空间。

然后调用INET协议族的操作集中bind函数inet_bind函数将socket地址(内核空间)和socket绑定。

  1. int inet_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)  
  2. {  
  3.     struct sockaddr_in *addr = (struct sockaddr_in *)uaddr;  
  4.     struct sock *sk = sock->sk;  
  5.     struct inet_sock *inet = inet_sk(sk);  
  6.     unsigned short snum;  
  7.     int chk_addr_ret;  
  8.     int err;  
  9.   
  10.     //RAW类型套接字若有自己的bind函数,则使用之  
  11.     if (sk->sk_prot->bind) {  
  12.         err = sk->sk_prot->bind(sk, uaddr, addr_len);  
  13.         goto out;  
  14.     }  
  15.     err = -EINVAL;  
  16.     .....................  
  17.         //地址合法性检查  
  18.     chk_addr_ret = inet_addr_type(sock_net(sk), addr->sin_addr.s_addr);  
  19.   
  20.     /* Not specified by any standard per-se, however it breaks too 
  21.      * many applications when removed.  It is unfortunate since 
  22.      * allowing applications to make a non-local bind solves 
  23.      * several problems with systems using dynamic addressing. 
  24.      * (ie. your servers still start up even if your ISDN link 
  25.      *  is temporarily down) 
  26.      */  
  27.     err = -EADDRNOTAVAIL;  
  28.     if (!sysctl_ip_nonlocal_bind &&  
  29.         !(inet->freebind || inet->transparent) &&  
  30.         addr->sin_addr.s_addr != htonl(INADDR_ANY) &&  
  31.         chk_addr_ret != RTN_LOCAL &&  
  32.         chk_addr_ret != RTN_MULTICAST &&  
  33.         chk_addr_ret != RTN_BROADCAST)  
  34.         goto out;  
  35.   
  36.     snum = ntohs(addr->sin_port);  
  37.     err = -EACCES;  
  38.     if (snum && snum < PROT_SOCK && !capable(CAP_NET_BIND_SERVICE))  
  39.         goto out;  
  40.   
  41.     /*      We keep a pair of addresses. rcv_saddr is the one 
  42.      *      used by hash lookups, and saddr is used for transmit. 
  43.      * 
  44.      *      In the BSD API these are the same except where it 
  45.      *      would be illegal to use them (multicast/broadcast) in 
  46.      *      which case the sending device address is used. 
  47.      */  
  48.     lock_sock(sk);  
  49.   
  50.     /* Check these errors (active socket, double bind). */  
  51.     err = -EINVAL;  
  52.     if (sk->sk_state != TCP_CLOSE || inet->inet_num)//如果sk的状态是CLOSE或者本地端口已经被绑定  
  53.         goto out_release_sock;  
  54.   
  55.     inet->inet_rcv_saddr = inet->inet_saddr = addr->sin_addr.s_addr;//设置源地址  
  56.     if (chk_addr_ret == RTN_MULTICAST || chk_addr_ret == RTN_BROADCAST)  
  57.         inet->inet_saddr = 0;  /* Use device */  
  58.   
  59.     /* Make sure we are allowed to bind here. */  
  60.     if (sk->sk_prot->get_port(sk, snum)) {  
  61.         inet->inet_saddr = inet->inet_rcv_saddr = 0;  
  62.         err = -EADDRINUSE;  
  63.         goto out_release_sock;  
  64.     }  
  65.   
  66.     if (inet->inet_rcv_saddr)  
  67.         sk->sk_userlocks |= SOCK_BINDADDR_LOCK;  
  68.     if (snum)  
  69.         sk->sk_userlocks |= SOCK_BINDPORT_LOCK;  
  70.     inet->inet_sport = htons(inet->inet_num);//设置源端口号,标明该端口已经被占用  
  71.     inet->inet_daddr = 0;  
  72.     inet->inet_dport = 0;  
  73.     sk_dst_reset(sk);  
  74.     err = 0;  
  75. out_release_sock:  
  76.     release_sock(sk);  
  77. out:  
  78.     return err;  
  79. }  
这样套接字绑定结束。


2、套接字的监听

  1. SYSCALL_DEFINE2(listen, int, fd, int, backlog)  
  2. {  
  3.     struct socket *sock;  
  4.     int err, fput_needed;  
  5.     int somaxconn;  
  6.   
  7.     sock = sockfd_lookup_light(fd, &err, &fput_needed);  
  8.     if (sock) {  
  9.         ......................  
  10.   
  11.         err = security_socket_listen(sock, backlog);  
  12.         if (!err)  
  13.             err = sock->ops->listen(sock, backlog);  
  14.   
  15.         fput_light(sock->file, fput_needed);  
  16.     }  
  17.     return err;  
  18. }  
该函数先通过文件描述符查找到对应的套接字结构,然后调用inet_listen函数对将套接字sk的状态设置为TCP_LISTEN。

  1. int inet_listen(struct socket *sock, int backlog)  
  2. {  
  3.     struct sock *sk = sock->sk;  
  4.     unsigned char old_state;  
  5.     int err;  
  6.     lock_sock(sk);  
  7.   
  8.     err = -EINVAL;  
  9.     if (sock->state != SS_UNCONNECTED || sock->type != SOCK_STREAM)  
  10.         goto out;  
  11.   
  12.     old_state = sk->sk_state;  
  13.     if (!((1 << old_state) & (TCPF_CLOSE | TCPF_LISTEN)))  
  14.         goto out;  
  15.   
  16.     if (old_state != TCP_LISTEN) {  
  17.         err = inet_csk_listen_start(sk, backlog);//该函数将sk的状态设置为TCP_LISTEN  
  18.         if (err)  
  19.             goto out;  
  20.     }  
  21.     sk->sk_max_ack_backlog = backlog;  
  22.     err = 0;  
  23. out:  
  24.     release_sock(sk);  
  25.     return err;  
  26. }  

3、套接字的连接和接受连接

3.1、申请连接

  1. SYSCALL_DEFINE3(connect, int, fd, struct sockaddr __user *, uservaddr,  
  2.         int, addrlen)  
  3. {  
  4.     struct socket *sock;  
  5.     struct sockaddr_storage address;  
  6.     int err, fput_needed;  
  7.   
  8.     sock = sockfd_lookup_light(fd, &err, &fput_needed);  
  9.     if (!sock)  
  10.         goto out;  
  11.     err = move_addr_to_kernel(uservaddr, addrlen, (struct sockaddr *)&address);  
  12.     if (err < 0)  
  13.         goto out_put;  
  14.   
  15.     err =  
  16.         security_socket_connect(sock, (struct sockaddr *)&address, addrlen);  
  17.     if (err)  
  18.         goto out_put;  
  19.   
  20.     err = sock->ops->connect(sock, (struct sockaddr *)&address, addrlen,  
  21.                  sock->file->f_flags);  
  22. out_put:  
  23.     fput_light(sock->file, fput_needed);  
  24. out:  
  25.     return err;  
  26. }  
还是先调用sockfd_lookup_light函数获得socket指针,然后将用户空间地址移到内核空间,然后调用函数inet_stream_connect函数。
  1. int inet_stream_connect(struct socket *sock, struct sockaddr *uaddr,  
  2.             int addr_len, int flags)  
  3. {  
  4.     struct sock *sk = sock->sk;  
  5.     int err;  
  6.     long timeo;  
  7.   
  8.     if (addr_len < sizeof(uaddr->sa_family))  
  9.         return -EINVAL;  
  10.   
  11.     lock_sock(sk);  
  12.   
  13.     ......................  
  14.   
  15.     switch (sock->state) {  
  16.     default:  
  17.         err = -EINVAL;  
  18.         goto out;  
  19.     case SS_CONNECTED:  
  20.         err = -EISCONN;  
  21.         goto out;  
  22.     case SS_CONNECTING:  
  23.         err = -EALREADY;  
  24.         /* Fall out of switch with err, set for this state */  
  25.         break;  
  26.     case SS_UNCONNECTED:  
  27.         err = -EISCONN;  
  28.         if (sk->sk_state != TCP_CLOSE)  
  29.             goto out;  
  30.   
  31.         err = sk->sk_prot->connect(sk, uaddr, addr_len);  
  32.         if (err < 0)  
  33.             goto out;  
  34.   
  35.         sock->state = SS_CONNECTING;  
  36.   
  37.         err = -EINPROGRESS;  
  38.         break;  
  39.     }  
  40.   
  41.     timeo = sock_sndtimeo(sk, flags & O_NONBLOCK);  
  42.   
  43.     if ((1 << sk->sk_state) & (TCPF_SYN_SENT | TCPF_SYN_RECV)) {  
  44.         /* Error code is set above */  
  45.         if (!timeo || !inet_wait_for_connect(sk, timeo))  
  46.             goto out;  
  47.   
  48.         err = sock_intr_errno(timeo);  
  49.         if (signal_pending(current))  
  50.             goto out;  
  51.     }  
  52.   
  53.     /* Connection was closed by RST, timeout, ICMP error 
  54.      * or another process disconnected us. 
  55.      */  
  56.     if (sk->sk_state == TCP_CLOSE)  
  57.         goto sock_error;  
  58.   
  59.     sock->state = SS_CONNECTED;  
  60.     err = 0;  
  61. out:  
  62.     release_sock(sk);  
  63.     return err;  
  64.   
  65. sock_error:  
  66.     err = sock_error(sk) ? : -ECONNABORTED;  
  67.     sock->state = SS_UNCONNECTED;  
  68.     if (sk->sk_prot->disconnect(sk, flags))  
  69.         sock->state = SS_DISCONNECTING;  
  70.     goto out;  
  71. }  

调用函数tcp_v4_connect函数后然后将sock的状态置SS_CONNECTING。

  1. int tcp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len)  
  2. {  
  3.     struct sockaddr_in *usin = (struct sockaddr_in *)uaddr;  
  4.     struct inet_sock *inet = inet_sk(sk);  
  5.     struct tcp_sock *tp = tcp_sk(sk);  
  6.     __be16 orig_sport, orig_dport;  
  7.     __be32 daddr, nexthop;  
  8.     struct flowi4 *fl4;  
  9.     struct rtable *rt;  
  10.     int err;  
  11.     struct ip_options_rcu *inet_opt;  
  12.         //合法性检查  
  13.     if (addr_len < sizeof(struct sockaddr_in))  
  14.         return -EINVAL;  
  15.   
  16.     if (usin->sin_family != AF_INET)  
  17.         return -EAFNOSUPPORT;  
  18.         //记录吓一跳地址和目的地址  
  19.     nexthop = daddr = usin->sin_addr.s_addr;  
  20.     inet_opt = rcu_dereference_protected(inet->inet_opt,  
  21.                          sock_owned_by_user(sk));  
  22.     if (inet_opt && inet_opt->opt.srr) {  
  23.         if (!daddr)  
  24.             return -EINVAL;  
  25.         nexthop = inet_opt->opt.faddr;  
  26.     }  
  27.         //本地端口和目的端口  
  28.     orig_sport = inet->inet_sport;  
  29.     orig_dport = usin->sin_port;  
  30.     fl4 = &inet->cork.fl.u.ip4;  
  31.     rt = ip_route_connect(fl4, nexthop, inet->inet_saddr,  
  32.                   RT_CONN_FLAGS(sk), sk->sk_bound_dev_if,  
  33.                   IPPROTO_TCP,  
  34.                   orig_sport, orig_dport, sk, true);//维护路由表  
  35.     if (IS_ERR(rt)) {  
  36.         err = PTR_ERR(rt);  
  37.         if (err == -ENETUNREACH)  
  38.             IP_INC_STATS_BH(sock_net(sk), IPSTATS_MIB_OUTNOROUTES);  
  39.         return err;  
  40.     }  
  41.         //处理多播或广播  
  42.     if (rt->rt_flags & (RTCF_MULTICAST | RTCF_BROADCAST)) {  
  43.         ip_rt_put(rt);  
  44.         return -ENETUNREACH;  
  45.     }  
  46.   
  47.     if (!inet_opt || !inet_opt->opt.srr)  
  48.         daddr = fl4->daddr;  
  49.   
  50.     if (!inet->inet_saddr)  
  51.         inet->inet_saddr = fl4->saddr;  
  52.     inet->inet_rcv_saddr = inet->inet_saddr;  
  53.   
  54.     if (tp->rx_opt.ts_recent_stamp && inet->inet_daddr != daddr) {  
  55.         /* Reset inherited state */  
  56.         tp->rx_opt.ts_recent    = 0;  
  57.         tp->rx_opt.ts_recent_stamp = 0;  
  58.         tp->write_seq           = 0;  
  59.     }  
  60.   
  61.     if (tcp_death_row.sysctl_tw_recycle &&  
  62.         !tp->rx_opt.ts_recent_stamp && fl4->daddr == daddr) {  
  63.         struct inet_peer *peer = rt_get_peer(rt, fl4->daddr);  
  64.         /* 
  65.          * VJ's idea. We save last timestamp seen from 
  66.          * the destination in peer table, when entering state 
  67.          * TIME-WAIT * and initialize rx_opt.ts_recent from it, 
  68.          * when trying new connection. 
  69.          */  
  70.         if (peer) {  
  71.             inet_peer_refcheck(peer);  
  72.             if ((u32)get_seconds() - peer->tcp_ts_stamp <= TCP_PAWS_MSL) {  
  73.                 tp->rx_opt.ts_recent_stamp = peer->tcp_ts_stamp;  
  74.                 tp->rx_opt.ts_recent = peer->tcp_ts;  
  75.             }  
  76.         }  
  77.     }  
  78.         //设置套接字中的目的端口和目的地址  
  79.     inet->inet_dport = usin->sin_port;  
  80.     inet->inet_daddr = daddr;  
  81.   
  82.     inet_csk(sk)->icsk_ext_hdr_len = 0;  
  83.     if (inet_opt)  
  84.         inet_csk(sk)->icsk_ext_hdr_len = inet_opt->opt.optlen;  
  85.   
  86.     tp->rx_opt.mss_clamp = TCP_MSS_DEFAULT;  
  87.   
  88.     //设置sk的状态为TCP_SYN_SENT  
  89.     tcp_set_state(sk, TCP_SYN_SENT);  
  90.     err = inet_hash_connect(&tcp_death_row, sk);  
  91.     if (err)  
  92.         goto failure;  
  93.   
  94.     rt = ip_route_newports(fl4, rt, orig_sport, orig_dport,  
  95.                    inet->inet_sport, inet->inet_dport, sk);  
  96.     if (IS_ERR(rt)) {  
  97.         err = PTR_ERR(rt);  
  98.         rt = NULL;  
  99.         goto failure;  
  100.     }  
  101.     /* OK, now commit destination to socket.  */  
  102.     sk->sk_gso_type = SKB_GSO_TCPV4;  
  103.     sk_setup_caps(sk, &rt->dst);  
  104.   
  105.     if (!tp->write_seq)  
  106.         tp->write_seq = secure_tcp_sequence_number(inet->inet_saddr,  
  107.                                inet->inet_daddr,  
  108.                                inet->inet_sport,  
  109.                                usin->sin_port);  
  110.   
  111.     inet->inet_id = tp->write_seq ^ jiffies;  
  112.   
  113.     err = tcp_connect(sk);//创建SYN报文并发送,该函数实现过程挺复杂,需进行TCP连接初始化以及发送  
  114.     rt = NULL;  
  115.     if (err)  
  116.         goto failure;  
  117.   
  118.     return 0;  
  119.   
  120. failure:  
  121.     //失败处理  
  122.     tcp_set_state(sk, TCP_CLOSE);  
  123.     ip_rt_put(rt);  
  124.     sk->sk_route_caps = 0;  
  125.     inet->inet_dport = 0;  
  126.     return err;  
  127. }  

3.2、接受连接

系统调用函数sys_accept实现如下:

  1. SYSCALL_DEFINE3(accept, int, fd, struct sockaddr __user *, upeer_sockaddr,  
  2.         int __user *, upeer_addrlen)  
  3. {  
  4.     return sys_accept4(fd, upeer_sockaddr, upeer_addrlen, 0);  
  5. }  

调用系统调用sys_accept4

  1. SYSCALL_DEFINE4(accept4, int, fd, struct sockaddr __user *, upeer_sockaddr,  
  2.         int __user *, upeer_addrlen, int, flags)  
  3. {  
  4.     struct socket *sock, *newsock;  
  5.     struct file *newfile;  
  6.     int err, len, newfd, fput_needed;  
  7.     struct sockaddr_storage address;  
  8.     .......................  
  9.     sock = sockfd_lookup_light(fd, &err, &fput_needed);//根据fd获得一个socket  
  10.     if (!sock)  
  11.         goto out;  
  12.   
  13.     err = -ENFILE;  
  14.     newsock = sock_alloc();//重新创建一个新的socket  
  15.     if (!newsock)  
  16.         goto out_put;  
  17. <span style="white-space:pre">  </span>//复制套接字部分属性  
  18.     newsock->type = sock->type;  
  19.     newsock->ops = sock->ops;  
  20.     __module_get(newsock->ops->owner);  
  21. <span style="white-space:pre">  </span>//给新建的socket分配文件结构,并返回新的文件描述符  
  22.     newfd = sock_alloc_file(newsock, &newfile, flags);  
  23.     if (unlikely(newfd < 0)) {  
  24.         err = newfd;  
  25.         sock_release(newsock);  
  26.         goto out_put;  
  27.     }  
  28.   
  29.     err = security_socket_accept(sock, newsock);  
  30.     if (err)  
  31.         goto out_fd;  
  32. <span style="white-space:pre">  </span>//调用inet_accept接受连接  
  33.     err = sock->ops->accept(sock, newsock, sock->file->f_flags);  
  34.     if (err < 0)  
  35.         goto out_fd;  
  36.   
  37.     if (upeer_sockaddr) {//将地址信息从内核移到用户空间  
  38.         if (newsock->ops->getname(newsock, (struct sockaddr *)&address,  
  39.                       &len, 2) < 0) {  
  40.             err = -ECONNABORTED;  
  41.             goto out_fd;  
  42.         }  
  43.         err = move_addr_to_user((struct sockaddr *)&address,  
  44.                     len, upeer_sockaddr, upeer_addrlen);  
  45.         if (err < 0)  
  46.             goto out_fd;  
  47.     }  
  48.   
  49.     /* File flags are not inherited via accept() unlike another OSes. */  
  50. <span style="white-space:pre">  </span>//安装文件描述符  
  51.     fd_install(newfd, newfile);  
  52.     err = newfd;  
  53.   
  54. out_put:  
  55.     fput_light(sock->file, fput_needed);  
  56. out:  
  57.     return err;  
  58. out_fd:  
  59.     fput(newfile);  
  60.     put_unused_fd(newfd);  
  61.     goto out_put;  
  62. }  
该函数创建一个新的套接字,设置客户端连接并唤醒客户端并返回一个新的文件描述符fd。

下面是inet_accept函数的实现

  1. int inet_accept(struct socket *sock, struct socket *newsock, int flags)  
  2. {  
  3.     struct sock *sk1 = sock->sk;  
  4.     int err = -EINVAL;  
  5.     struct sock *sk2 = sk1->sk_prot->accept(sk1, flags, &err);//调用<span style="font-family: Consolas, 'Courier New', Courier, mono, serif; line-height: 18px;">inet_csk_accept函数从队列icsk_accept_queue取出已经连接的套接字</span>  
  6.   
  7.     if (!sk2)  
  8.         goto do_err;  
  9.   
  10.     lock_sock(sk2);  
  11.   
  12.     sock_rps_record_flow(sk2);  
  13.     WARN_ON(!((1 << sk2->sk_state) &  
  14.           (TCPF_ESTABLISHED | TCPF_CLOSE_WAIT | TCPF_CLOSE)));  
  15.   
  16.     sock_graft(sk2, newsock);  
  17.   
  18.     newsock->state = SS_CONNECTED;//设置套接字状态  
  19.     err = 0;  
  20.     release_sock(sk2);  
  21. do_err:  
  22.     return err;  
  23. }  


4、关闭连接

关闭一个socket连接,系统调用sys_shutdown

  1. SYSCALL_DEFINE2(shutdown, int, fd, int, how)  
  2. {  
  3.     int err, fput_needed;  
  4.     struct socket *sock;  
  5.   
  6.     sock = sockfd_lookup_light(fd, &err, &fput_needed);  
  7.     if (sock != NULL) {  
  8.         err = security_socket_shutdown(sock, how);  
  9.         if (!err)  
  10.             err = sock->ops->shutdown(sock, how);  
  11.         fput_light(sock->file, fput_needed);  
  12.     }  
  13.     return err;  
  14. }  
函数最后调用inet_shutdown关闭套接字

  1. int inet_shutdown(struct socket *sock, int how)  
  2. {  
  3.     struct sock *sk = sock->sk;  
  4.     int err = 0;  
  5.     .................  
  6.     lock_sock(sk);  
  7.     if (sock->state == SS_CONNECTING) {  
  8.         if ((1 << sk->sk_state) &  
  9.             (TCPF_SYN_SENT | TCPF_SYN_RECV | TCPF_CLOSE))  
  10.             sock->state = SS_DISCONNECTING;  
  11.         else  
  12.             sock->state = SS_CONNECTED;  
  13.     }  
  14.   
  15.     switch (sk->sk_state) {  
  16.     case TCP_CLOSE:  
  17.         err = -ENOTCONN;  
  18.     default:  
  19.         sk->sk_shutdown |= how;  
  20.         if (sk->sk_prot->shutdown)  
  21.             sk->sk_prot->shutdown(sk, how);//调用<span style="font-family: Consolas, 'Courier New', Courier, mono, serif; line-height: 18px;">tcp_shutdown强制关闭连接</span>  
  22.         break;  
  23.   
  24.     /* Remaining two branches are temporary solution for missing 
  25.      * close() in multithreaded environment. It is _not_ a good idea, 
  26.      * but we have no choice until close() is repaired at VFS level. 
  27.      */  
  28.     case TCP_LISTEN:  
  29.         if (!(how & RCV_SHUTDOWN))  
  30.             break;  
  31.         /* Fall through */  
  32.     case TCP_SYN_SENT:  
  33.         err = sk->sk_prot->disconnect(sk, O_NONBLOCK);//调用<span style="font-family: Consolas, 'Courier New', Courier, mono, serif; line-height: 18px; background-color: rgb(248, 248, 248);">tcp_disconnect断开连接</span>  
  34.         sock->state = err ? SS_DISCONNECTING : SS_UNCONNECTED;//设置套接字状态  
  35.         break;  
  36.     }  
  37.   
  38.     sk->sk_state_change(sk);  
  39.     release_sock(sk);  
  40.     return err;  
  41. }  
后面会详细分析TCP协议的发送和接收过程。

posted on 2013-04-13 13:42  疯子123  阅读(247)  评论(0编辑  收藏  举报

导航