套接字之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 }

 

這裏主要看流式套接字的實現;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 }
相關文章
相關標籤/搜索