从应用层观察TCP的三次握手和TFO

网络程序一般分为客户端和服务端,先来用一段伪代码看一下客户端和服务端程序会调用哪些函数
服务端:

server()
{
    int listenfd = socket(PF_INET,SOCK_STREAM,0); // 创建socket
    bind(listenfd,&serveraddr,sizeof(serveraddr)); // 绑定本地地址和端口
    listen(LISTENQ); // 开始监听

    while((clientfd = accept(listenfd,&clientaddr,&clientaddrlen) )> 0 //建立连接
    {
        // 连接成功后,读写clientfd
        read/write(clientfd);
    }
}

客户端伪代码:

client()
{
    int clientfd = socket(PF_INET,SOCK_STREAM,0); //创建socket
    if(connect(clientfd,&serveraddr,serveraddrlen) == 0) // 建立连接
    {
        // 连接成功后读写clientfd
        write/read(clientfd);
    }   
}

TCP的三次握手就隐含在上述的函数调用中,更确切的说,是隐含在connect和accept之间。这里借用UNP里面的对三次握手过程的描述:

从应用层观察TCP的三次握手和TFO_第1张图片

解释一下上图:
1.服务器端绑定端口,开始监听之后,就调用accept等待连接,程序会阻塞到accpet调用中
2.当客户端调用connect时,内核协议栈发送syn包给服务端,然后阻塞到connect调用中,假定此时syn包的包序号是J
3.当服务器端接收到syn包之后,协议栈会返回一个syn包K和一个对客户端的确认包ack,其中包序号要加1,即返回syn k 和 ack j+1
4.客户端接收到syn k 和 ack j+1之后,会对服务端的同步包syn k 做出回应,返回一个确认包 ack k +1 ; 同时,connect函数不再阻塞,返回到客户端程序中。此时,客户端已经认为连接已经建立
5.服务端程序接收ack k+1后,accept函数返回。此时,三次握手完毕。

考虑下TCP的为什么需要三次握手,其实三次握手的场景就是两个互相对话并确认对方存在的一个过程:
A: 你听的到我说话吗?(SYN)
B:听到了,你能听到我说话吗?(ACK+SYN)
A: 我也能听到 (ACK)
OK,握手完毕

如果这个对话过程少了任何一句,就会发现无法保证通信链路双向都是通畅的。

说到这里还想提一下google近年来提出的TFO(tcp fast open)。TFO允许在握手期间携带数据,应该是google针对http大面积应用的场景对tcp作出的改善,毕竟一个http的短连接中,三次握手就占用了不少时间。我本身对TFO也了解不多,只能大概谈谈个人理解,如果存在有失偏颇之处,敬请谅解。

与上面描述的正常的TCP三次握手,仅用来确认对方是否存在不同,TFO对话场景大概是这个样子的:
A:小红,我是小明,你现在在哪里(sync + data)
B:小明啊,我是小红,我在0.98零点酒吧呢 (sync + ack + data)

根据上述的描述可以发现,TFO就是在发送sync包的同时,附带了一些数据。熟悉http的同学也会发现,TFO简直就是把一个HTTP请求融合到了三次握手中,减少了一个RRT,据google测试描述,TFO可以减少15%的HTTP传输延迟,全页面的下载时间平均节省10%,最高可达40%。

关于TFO的更详细的介绍可以参考这篇文章

你可能感兴趣的:(socket,tcp,TFO,tcp)