当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 }