zoukankan      html  css  js  c++  java
  • 套接字之connect系统调用

    当connect作用于流套接字的时候,是地址指明的对端建立连接,对于TCP来讲,connect会完成与对端的三次握手建立连接的过程;当connect作用于数据报套接字的时候,用于指明发送的对端地址,并且只能向该地址发送数据,指明之后,可以使用send等发送数据,无需使用sendto的参数再次指明发送地址;

     1 /*
     2  *    Attempt to connect to a socket with the server address.  The address
     3  *    is in user space so we verify it is OK and move it to kernel space.
     4  *
     5  *    For 1003.1g we need to add clean support for a bind to AF_UNSPEC to
     6  *    break bindings
     7  *
     8  *    NOTE: 1003.1g draft 6.3 is broken with respect to AX.25/NetROM and
     9  *    other SEQPACKET protocols that take time to connect() as it doesn't
    10  *    include the -EINPROGRESS status for such sockets.
    11  */
    12 
    13 SYSCALL_DEFINE3(connect, int, fd, struct sockaddr __user *, uservaddr,
    14         int, addrlen)
    15 {
    16     struct socket *sock;
    17     struct sockaddr_storage address;
    18     int err, fput_needed;
    19 
    20     /* 找到socket */
    21     sock = sockfd_lookup_light(fd, &err, &fput_needed);
    22     if (!sock)
    23         goto out;
    24 
    25     /* 地址复制到内核 */
    26     err = move_addr_to_kernel(uservaddr, addrlen, &address);
    27     if (err < 0)
    28         goto out_put;
    29 
    30     /* 安全检查 */
    31     err =
    32         security_socket_connect(sock, (struct sockaddr *)&address, addrlen);
    33     if (err)
    34         goto out_put;
    35 
    36     /* 调用对应类型的connect */
    37     err = sock->ops->connect(sock, (struct sockaddr *)&address, addrlen,
    38                  sock->file->f_flags);
    39 out_put:
    40     fput_light(sock->file, fput_needed);
    41 out:
    42     return err;
    43 }

    这里主要看流式套接字的实现;

     1 int inet_stream_connect(struct socket *sock, struct sockaddr *uaddr,
     2             int addr_len, int flags)
     3 {
     4     int err;
     5 
     6     lock_sock(sock->sk);
     7     err = __inet_stream_connect(sock, uaddr, addr_len, flags, 0);
     8     release_sock(sock->sk);
     9     return err;
    10 }

    建立连接过程中会根据socket的状态做不同的处理,连接并不一定马上完成,所以其也会有从连接中,到连接成功的状态转移;其中还包含了对阻塞socket和非阻塞socket的处理;tcp层的connect实现后续阅读过程中会进行补充;

      1 /*
      2  *    Connect to a remote host. There is regrettably still a little
      3  *    TCP 'magic' in here.
      4  */
      5 int __inet_stream_connect(struct socket *sock, struct sockaddr *uaddr,
      6               int addr_len, int flags, int is_sendmsg)
      7 {
      8     struct sock *sk = sock->sk;
      9     int err;
     10     long timeo;
     11 
     12     /*
     13      * uaddr can be NULL and addr_len can be 0 if:
     14      * sk is a TCP fastopen active socket and
     15      * TCP_FASTOPEN_CONNECT sockopt is set and
     16      * we already have a valid cookie for this socket.
     17      * In this case, user can call write() after connect().
     18      * write() will invoke tcp_sendmsg_fastopen() which calls
     19      * __inet_stream_connect().
     20      */
     21     /* 地址存在 */
     22     if (uaddr) {
     23 
     24         /* 检查地址长度 */
     25         if (addr_len < sizeof(uaddr->sa_family))
     26             return -EINVAL;
     27 
     28         /* 对unspec的特殊处理 */
     29         if (uaddr->sa_family == AF_UNSPEC) {
     30             err = sk->sk_prot->disconnect(sk, flags);
     31             sock->state = err ? SS_DISCONNECTING : SS_UNCONNECTED;
     32             goto out;
     33         }
     34     }
     35 
     36     /* 根据socket状态对应处理 */
     37     switch (sock->state) {
     38     default:
     39         err = -EINVAL;
     40         goto out;
     41         /* 已连接 */
     42     case SS_CONNECTED:
     43         err = -EISCONN;
     44         goto out;
     45         /* 正在连接 */
     46     case SS_CONNECTING:
     47         /* 对是否发消息做不同处理,fastopen*/
     48         if (inet_sk(sk)->defer_connect)
     49             err = is_sendmsg ? -EINPROGRESS : -EISCONN;
     50         else
     51             err = -EALREADY;
     52         /* Fall out of switch with err, set for this state */
     53         break;
     54         /* 未连接 */
     55     case SS_UNCONNECTED:
     56         err = -EISCONN;
     57         /* 需要为closed状态 */
     58         if (sk->sk_state != TCP_CLOSE)
     59             goto out;
     60 
     61         /* 传输层协议的connect */
     62         err = sk->sk_prot->connect(sk, uaddr, addr_len);
     63         if (err < 0)
     64             goto out;
     65 
     66         /* 标记状态为正在连接 */
     67         sock->state = SS_CONNECTING;
     68 
     69         /* fastopen */
     70         if (!err && inet_sk(sk)->defer_connect)
     71             goto out;
     72 
     73         /* Just entered SS_CONNECTING state; the only
     74          * difference is that return value in non-blocking
     75          * case is EINPROGRESS, rather than EALREADY.
     76          */
     77         /* 非阻塞情况返回inprogress,阻塞返回already */
     78         err = -EINPROGRESS;
     79         break;
     80     }
     81 
     82     /* 阻塞情况下需要获取超时时间,非阻塞为0 */
     83     timeo = sock_sndtimeo(sk, flags & O_NONBLOCK);
     84 
     85     /* 已发送或者已收到syn */
     86     if ((1 << sk->sk_state) & (TCPF_SYN_SENT | TCPF_SYN_RECV)) {
     87         int writebias = (sk->sk_protocol == IPPROTO_TCP) &&
     88                 tcp_sk(sk)->fastopen_req &&
     89                 tcp_sk(sk)->fastopen_req->data ? 1 : 0;
     90 
     91         /* Error code is set above */
     92         /* 非阻塞退出,阻塞则等待连接,等待剩余时间为0,退出 */
     93         if (!timeo || !inet_wait_for_connect(sk, timeo, writebias))
     94             goto out;
     95 
     96         /* 处理信号,达到最大调度时间或者被打断 */
     97         err = sock_intr_errno(timeo);
     98         if (signal_pending(current))
     99             goto out;
    100     }
    101 
    102     /* Connection was closed by RST, timeout, ICMP error
    103      * or another process disconnected us.
    104      */
    105     /* 状态为关闭 */
    106     if (sk->sk_state == TCP_CLOSE)
    107         goto sock_error;
    108 
    109     /* sk->sk_err may be not zero now, if RECVERR was ordered by user
    110      * and error was received after socket entered established state.
    111      * Hence, it is handled normally after connect() return successfully.
    112      */
    113 
    114     /* 设置为连接状态 */
    115     sock->state = SS_CONNECTED;
    116     err = 0;
    117 out:
    118     return err;
    119 
    120 sock_error:
    121     err = sock_error(sk) ? : -ECONNABORTED;
    122     /* 设置未连接状态 */
    123     sock->state = SS_UNCONNECTED;
    124     if (sk->sk_prot->disconnect(sk, flags))
    125         sock->state = SS_DISCONNECTING;
    126     goto out;
    127 }

    对于阻塞socket,需要加入到等待队列中,等待连接完成;

     1 /* 阻塞等待连接 */
     2 static long inet_wait_for_connect(struct sock *sk, long timeo, int writebias)
     3 {
     4     DEFINE_WAIT_FUNC(wait, woken_wake_function);
     5 
     6     /* 添加到等待队列 */
     7     add_wait_queue(sk_sleep(sk), &wait);
     8     sk->sk_write_pending += writebias;
     9 
    10     /* Basic assumption: if someone sets sk->sk_err, he _must_
    11      * change state of the socket from TCP_SYN_*.
    12      * Connect() does not allow to get error notifications
    13      * without closing the socket.
    14      */
    15     /* 等待连接状态改变 */
    16     while ((1 << sk->sk_state) & (TCPF_SYN_SENT | TCPF_SYN_RECV)) {
    17         release_sock(sk);
    18         timeo = wait_woken(&wait, TASK_INTERRUPTIBLE, timeo);
    19         lock_sock(sk);
    20         if (signal_pending(current) || !timeo)
    21             break;
    22     }
    23 
    24     /* 从等待队列移除 */
    25     remove_wait_queue(sk_sleep(sk), &wait);
    26     sk->sk_write_pending -= writebias;
    27     return timeo;
    28 }
  • 相关阅读:
    k8s学习
    k8s学习
    k8s学习
    Linux 常用命令(持续补充)
    通过一个小故事,理解 HTTPS 工作原理
    Spring Cloud 微服务架构全链路实践
    Spring Cloud Eureka 使用 IP 地址进行服务注册
    RabbitMQ 消息顺序、消息幂等、消息重复、消息事务、集群
    Spring Boot 实现 RabbitMQ 延迟消费和延迟重试队列
    RabbitMQ 集群原理和完善
  • 原文地址:https://www.cnblogs.com/wanpengcoder/p/7622986.html
Copyright © 2011-2022 走看看