Linux内核源码-sys_connect()

(本文部分参考了《Linux内核源代码情景分析》)
操作SYS_CONNECT请求连接由sys_connect()完成。
有连接”模式的插口与“无连接”模式的插口都可以调用库函数 connect(),但是意义却不同。内核中的函数 sys_connect()是二者公用的。

sys_connect()代码如下:

/* connet系统调用 */
asmlinkage long sys_connect(int fd, struct sockaddr __user *uservaddr, int 
addrlen)
{
    struct socket *sock;
    char address[MAX_SOCK_ADDR];
    int err;

    sock = sockfd_lookup(fd, &err);/* 查找文件句柄对应的socket */
    if (!sock)
        goto out;
    /* 从用户态复制地址参数到内核中 */
    err = move_addr_to_kernel(uservaddr, addrlen, address);
    if (err < 0)
        goto out_put;

    /* 安全审计 */
    err = security_socket_connect(sock, (struct sockaddr *)address, addrlen);
    if (err)
        goto out_put;

    /* 调用传输层的connet方法inet_stream_connect或inet_dgram_connect */
    err = sock->ops->connect(sock, (struct sockaddr *) address, addrlen,
                 sock->file->f_flags);
out_put:
    sockfd_put(sock);
out:
    return err;
}

本文只看“有连接”模式的插口。如前所述,只有 client 插口才可以(并且一定要)通过 connect()向一个 server 插口提出连接请求,在请求被 server 插口接受而建立起连接之前是不能在两个插口之间传递数据报文的。

inet_stream_connect()代码如下:

/* connect系统调用的套接口层实现 */
int inet_stream_connect(struct socket *sock, struct sockaddr *uaddr,
            int addr_len, int flags)
{
    struct sock *sk = sock->sk;
    int err;
    long timeo;

    lock_sock(sk);/* 获取套接口的锁 */

    if (uaddr->sa_family == AF_UNSPEC) {
  /* 未指定地址类型,错误 */
        err = sk->sk_prot->disconnect(sk, flags);
        sock->state = err ? SS_DISCONNECTING : SS_UNCONNECTED;
        goto out;
    }

    switch (sock->state) {
    default:
        err = -EINVAL;
        goto out;
    case SS_CONNECTED:/* 已经与对方端口连接*/
        err = -EISCONN;
        goto out;
    case SS_CONNECTING:/*正在连接过程中*/
        err = -EALREADY;
        /* Fall out of switch with err, set for this state */
        break;
    case SS_UNCONNECTED:/* 只有此状态才能调用connect */
        err = -EISCONN;
        if (sk->sk_state != TCP_CLOSE)/* 如果不是TCP_CLOSE状态,说明已经连接了 */
            goto out;

/* 调用传输层接口tcp_v4_connect建立与服务器连接,并发送SYN段 */
        err = sk->sk_prot->connect(sk, uaddr, addr_len);
        if (err < 0)
            goto out;

        /* 发送SYN段后,设置状态为SS_CONNECTING */
        sock->state = SS_CONNECTING;


        err = -EINPROGRESS;/* 如果是以非阻塞方式进行连接,则默认的返回值为
EINPROGRESS,表示正在连接 */
        break;
    }

    /* 获取连接超时时间,如果指定非阻塞方式,则不等待直接返回 */
    timeo = sock_sndtimeo(sk, flags & O_NONBLOCK);

    if ((1 << sk->sk_state) & (TCPF_SYN_SENT | TCPF_SYN_RECV)) {
  /* 发送完SYN
后,连接状态一般为这两种状态,但是如果连接建立非常快,则可能越过这两种状态 */
        if (!timeo || !inet_wait_for_connect(sk, timeo))/* 等待连接完成或超时 */
            goto out;

        err = sock_intr_errno(timeo);
        if (signal_pending(current))
            goto out;
    }

    if (sk->sk_state == TCP_CLOSE)/* 运行到这里说明连接建立失败 */
        goto sock_error;

    sock->state = SS_CONNECTED;/* 连接建立成功,设置为已经连接状态 */
    err = 0;
out:
    release_sock(sk);
    return err;

sock_error:
    err = sock_error(sk) ? : -ECONNABORTED;
    sock->state = SS_UNCONNECTED;
    if (sk->sk_prot->disconnect(sk, flags))
        sock->state = SS_DISCONNECTING;
    goto out;
}

inet_stream_connect()函数主要功能如下:
(1)调用tcp_v4_connect函数建立与服务器联系并发送SYN段;
(2)获取连接超时时间timeo,如果timeo不为0,则会调用inet_wait_for_connect一直等待到连接成功或超时;

tcp_v4_connect函数代码如下,比较长,分段来看:

/* 建立与服务器连接,发送SYN段 */
int tcp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len)
{
    struct inet_sock *inet = inet_sk(sk);
    struct tcp_sock *tp = tcp_sk(sk);
    struct sockaddr_in *usin = (struct sockaddr_in *)uaddr;
    struct rtable *rt;
    u32 daddr, nexthop;
    int tmp;
    int err;

    /* 校验目的地址的长度及地址族的有效性 */
    if (addr_len < sizeof(struct sockaddr_in))
        return -EINVAL;

    if (usin->sin_family != AF_INET)
        return -EAFNOSUPPORT;

    /* 将下一跳和目的地址都设置为源地址 */
 
  • 2
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值