不少介绍网络编程的书籍中会这样介绍connect系统调用:将本机的一个指定的套接字链接到一个指定地址的服务器套接字上去。下面是connect系统调用的定义:
int connect(int sockfd, const struct sockaddr *serv_addr, socklen_t addrlen);
参数sockfd是本地机器上的一个套接字描述符,在内核的系统调用函数中该描述符会被转换成与之绑定的一个struct socket结构,这是真正的一个socket,表明了网络通信中链接的一端。serv_addr和addrlen则是要链接的服务器的地址和地址长度。
因而乎,有了这样的理解:connect将在本机和指定服务器间创建一个链接。但实际上,connect操做并不引起网络设备传送任何的数据到对端。它所 作的操做只是经过路由规则和路由表等一些信息,在struct socket结构中填入一些有关对端服务器的信息。这样,之后向对端发送数据报时,就不须要每次进行路由查询等操做以肯定对端地址信息和本地发送接口,应 用程序也就不须要每次传入对端地址信息(可使用send而不使用sendto)。基于这样的理解,咱们就不难弄明白,为何不仅是tcp socket能够connect,udp, raw socket也能够经过connect进行链接。它们的本质其实没有多大差异:把经过路由查询获得的对端主机的地址信息缓存到套接字结构struct socket中。
udp和raw的connect操做实际上是彻底一致的,都使用了myip4_datagram_connect函数。
为方便起见,咱们再以一个实际的例子来描述该函数所作的事情,咱们在主机172.16.48.2上向主机172.16.48.1的端口16000发送一个 udp数据报,172.16.48.2上的udp端口由系统自动选择(为32768)。下面是一个简单的应用程序示例:
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include "my_inet.h"
#include <stdio.h>
#include <errno.h>
#include <arpa/inet.h>
#include <unistd.h>
int main()
{
int i;
//表明服务器地址的结构。
struct sockaddr_in dest;
dest.sin_family = MY_PF_INET;
dest.sin_port = htons(16000);
dest.sin_addr.s_addr = 0x013010AC;//172.16.48.1的网络字节序。
int fd = socket( MY_PF_INET, SOCK_DGRAM, MY_IPPROTO_UDP );
if( fd < 0 ){
perror("socket: ");
return -1;
}
//链接操做。
if( connect( fd, (struct sockaddr*)&dest, sizeof(dest) ) < 0 )
perror("connect: ");
//没必要经过sendto每次传入对端地址信息了。
int bwrite = send( fd, "abcdefg", 7, 0 );
if( bwrite == -1 ){
perror("send: ");
return -1;
}
printf("sendto: %d\n", bwrite);
close( fd );
return 0;
}
connect系统调用的执行流在到达myip4_datagram_connect函数以前,已经对本地端口号进行自动选择,并把socket绑定到了 myudp_hash表中。到达myip4_datagram_connect函数以后,第一件事情是创建一个struct flowi:
struct flowi fl = { .oif = 0, //输出设备接口未定。
.nl_u = { .ip4_u = { .daddr = 172.16.48.1 //目的地址。
.saddr = 0.0.0.0 //源地址未定。
.tos = 0 } }, //通常服务
.proto = MY_IPPROTO_UDP, //UDP协议
.uli_u = { .ports =
{ .sport = 32768, //自动选择的第一个源端口
.dport = 16000 } } }; //目的端口
以该结构体为信息查询路由表,结果确定查到main表,肯定saddr为172.16.48.2。并获得一个struct rtable结构做为路由查询结果。
对于my_inet域的套接字,结构体struct socket有一个成员struct inet_sock sock表明网络层的一个套接字,其成员rcv_saddr(含义尚不明确)和saddr被赋172.16.48.2,daddr, dport被赋于服务器的地址和端口号。而表示链接状态的sk_state成员被赋于TCP_ESTABLISHED,这里须要注意的是 TCP_ESTABLISHED并不专指TCP链接创建状态,全部执行connect成功的套接字,其状态都是TCP_ESTABLISHED。id被赋 于当前时间。成员sk_dst_cache指向路由查询结果rtable的成员u.dst。从而套接字彻底缓存路由查询的结果。
执行了connect后的socket,须要发送数据报时,关于对端的信息所有能够从socket自己获得。但须要重申的一点是:因为路由缓存的存在,在链接的socket上发送数据报并不会比在未链接的socket上发送数据报效率高多少。