當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 }
這裏主要看流式套接字的實現;cookie
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實現後續閱讀過程當中會進行補充;socket
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,須要加入到等待隊列中,等待鏈接完成;tcp
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 }