zoukankan      html  css  js  c++  java
  • 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协议的发送和接收过程。
  • 相关阅读:
    httpVueLoader.js props传递参数
    iview自定义图标,render方式自定义图标
    C# MVC API WebSocket的聊天室案例
    CSS/HTML实现Switch开关按钮
    AndroidStudio打包Jar包给Unity使用的期间遇到的错误
    AndroidStudio4.0.1 打包 Jar包
    解决SVN异常 cleanup failed
    c++ sizeof的实现
    c++ 函数指针简单实例
    C++ socket 网络编程 简单聊天室
  • 原文地址:https://www.cnblogs.com/hehehaha/p/6332902.html
Copyright © 2011-2022 走看看