不少介紹網絡編程的書籍中會這樣介紹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上發送數據報效率高多少。