TCP/IP工作流5 connect开始
2017-01-13 21:32
477 查看
TCP/IP工作流5 connect
引子
沿上一篇的思路,当socket创建完成了。就要调用connect函数来连接到远程的服务器了。int connect(int sockfd, const struct sockaddr * addr, socklen_t addrlen)
这个函数原型中,sockfd是就用socket调用得到一个socket的文件ID。addr参数是,远程要连接到的服务器的地址了。addrlen是这个地址的描述符的长度。这里,我就有了一个疑问了,struct sockaddr的类型是确定的,为什么这里要要求一个长度呢?看源码当然能找到答案,我们先猜测下,看代码的不多的乐趣中的一个。
* 猜想1:难道我们可以连接到远程多个服务器,或者有多个备选的服务器可供使用。从系统可靠性的角度来说,这个猜测是合理的,为了可靠的服务,我提供给用户一个可选的服务器列表,让用户一个个都去尝试下,选择最优的,或者当前的服务器宕掉了,选择下一个备用的。这里的实现就是建立一个列表,把服务器的地址都写进去,然后把列表的总长度作为参数传递进去。但这个猜测也有不合理的地方。
* 猜想2:如果是一个列表的话,可以直接把列表中的元素的数量作为参数传递进去。何必传递一个地址长度,还要让函数自己计算数量。那么有了第二个猜测,就是struct sockaddr是一个变长的结构体,变长的结构体应该怎么实现呢?C语言里,一般把结构体里的最后一个成员设置为一个指针,用来指向一个数组。这个也不合理,因为如果是一个指针的话,就是一个地址长度。32位OS是4字节,也是一个定值,所以如何实现变长,还不明所以,这个推断也不合理。
* 猜想3:sockaddr会在编译时根据一些配置选项,来确定要不要包含一些成员。这个猜测也不合理。针对一个编译后的变量,用一个sizeof也可以求出它的长度。
综合以上3种猜测,现在看来,第一种最合理。去源码中看下。
开始源码
connect调用还是开始于sys_sockcall函数。sys_socketcall(call, args) net/socket.c 2004
然后调用sys_connect。
2032 sys_connect(a0, (struct sockaddr __user *)a0, a[2]);
sys_connect函数定义为于net/socket.c中。
1479 asmlinkage long sys_connect(int fd, struct sockaddr __user *uservaddr, 1480 int addrlen) 1481 { 1482 struct socket *sock; 1483 char address[MAX_SOCK_ADDR]; 1484 int err, fput_needed; 1486 sock = sockfd_lookup_light(fd, &err, &fput_needed); 1487 if (!sock) 1488 goto out; 1489 err = move_addr_to_kernel(uservaddr, addrlen, address); 1490 if (err < 0) 1491 goto out_put; 1493 err = 1494 security_socket_connect(sock, (struct sockaddr *)address, addrlen); 1495 if (err) 1496 goto out_put; 1498 err = sock->ops->connect(sock, (struct sockaddr *)address, addrlen, 1499 sock->file->f_flags); 1500 out_put: 1501 fput_light(sock->file, fput_needed); 1502 out: 1503 return err; 1504 }
从函数的声明看到,从connect函数调用传递过来的两个参数还没有得到处理。
1482 就不用再说了。是用户层的socket的表达。
1483 定义了一个最大地址数的字符数组。C语言里没有单字节数据类型,都是用char类型来代替的。这里定义的MAX_SOCK_ADDR为128。我们假设这里存的是IP地址,我们知道IPV4里一个地址要用4个字节表示,那么这里能存贮的地址的最大数量为128/4 = 32。
1484 定义两个临时变量。
1486 根据文件ID fd去得到其sock的地址。这里恰好是之前socket创建时的反过程上。从创建时,不难看出,大概的流程是根据fd找到struct file,struct file中的private变量就是指向struct sock的地址。感兴趣的可以去看下。
1489 把用户空间的地址,转换到内核空间地址。因为系统调用目前还是在内核空间里操作,想要处理用户传递来的数据就需要用到move_addr_to_kernel的函数调用。
1494 安全检查,有以前的经验知道这里面跟协议相关的东西没有多少,我们先不考虑。
1498 这里是真正执行调用的地方。第一篇 TCP/IP工作流的开始 socket创建 1 中在socket创建过程中我们知道,这里sock->ops为inet_stream_ops函数集。根据它的定义,最终执行的函数是inet_stream_connect。
546 int inet_stream_connect(struct socket *sock, struct sockaddr *uaddr, 547 int addr_len, int flags) 548 { 549 struct sock *sk = sock->sk; 550 int err; 551 long timeo; 553 lock_sock(sk); 555 if (uaddr->sa_family == AF_UNSPEC) { 556 err = sk->sk_prot->disconnect(sk, flags); 557 sock->state = err ? SS_DISCONNECTING : SS_UNCONNECTED; 558 goto out; 559 } 561 switch (sock->state) { 562 default: 563 err = -EINVAL; 564 goto out; 565 case SS_CONNECTED: 566 err = -EISCONN; 567 goto out; 568 case SS_CONNECTING: 569 err = -EALREADY; 571 break; 572 case SS_UNCONNECTED: 573 err = -EISCONN; 574 if (sk->sk_state != TCP_CLOSE) 575 goto out; 577 err = sk->sk_prot->connect(sk, uaddr, addr_len); 578 if (err < 0) 579 goto out; 581 sock->state = SS_CONNECTING; 587 err = -EINPROGRESS; 588 break; 589 } 591 timeo = sock_sndtimeo(sk, flags & O_NONBLOCK); 593 if ((1 << sk->sk_state) & (TCPF_SYN_SENT | TCPF_SYN_RECV)) { 595 if (!timeo || !inet_wait_for_connect(sk, timeo)) 596 goto out; 598 err = sock_intr_errno(timeo); 599 if (signal_pending(current)) 600 goto out; 601 } 606 if (sk->sk_state == TCP_CLOSE) 607 goto sock_error; 614 sock->state = SS_CONNECTED; 615 err = 0; 616 out: 617 release_sock(sk); 618 return err; 620 sock_error: 621 err = sock_error(sk) ? : -ECONNABORTED; 622 sock->state = SS_UNCONNECTED; 623 if (sk->sk_prot->disconnect(sk, flags)) 624 sock->state = SS_DISCONNECTING; 625 goto out; 626 }
经过以上源码,我们发现,最终执行的是sk->sk_prot->connect函数。通过之前的TCP/IP工作流的开始 socket创建2 中的分析,这里最终执行的是tcp_v4_connect。OK,下一篇就开始真正的TCP协议中与连接相关的部分了。
相关文章推荐
- java-模拟tomcat服务器
- Linux socket 初步
- java socket 注意的地方
- java socket 注意的地方
- C#基于socket模拟http请求的方法
- 简单的Ruby中的Socket编程教程
- Socket不能选择本地IP连接问题如何解决
- C#之Socket操作类实例解析
- 使用C#来编写一个异步的Socket服务器
- C#使用Socket快速判断数据库连接是否正常的方法
- 基于c#用Socket做一个局域网聊天工具
- C# Socket的TCP通讯的实例代码
- 科学知识:理解socket
- C++ socket实现miniFTP
- websocket++简单使用及实例分析
- socket多人聊天程序C语言版(一)
- socket多人聊天程序C语言版(二)
- Android聊天工具基于socket实现
- PHP socket 模拟POST 请求实例代码