首先,回顾下tcp三次握手
tcp fastopen 握手流程:
TFO 首次建联会发送空cookie,完成首次建联后,内核网络会保存该cookie,用于后续建联使用。如下图:
当客户端断开连接,下一次请求同一个服务端的时候,会带上之前存储的Cookie和要发送的数据,在SYN的路径上一起发送给服务端,如下图:
#3的意思是开启TFO客户端和服务器端 #1表示开启客户端,2表示开启服务器端 echo 3 > /proc/sys/net/ipv4/tcp_fastopenTFO 客户端和服务端示例
client.c
#include#include #include #include #include #include #define USE_IPV6 1 #if USE_IPV6 typedef struct in6_addr sq_in_addr; typedef struct sockaddr_in6 sq_sockaddr_in; #define SQ_AF_INET AF_INET6 #define SQ_PF_INET PF_INET6 #define sq_sin_family sin6_family #define sq_sin_port sin6_port #define sq_sin_addr sin6_addr #define sq_s_addr s6_addr #define CLIENT_IP_HEX_LEN 32 #define SQ_MAX_IP_STR_LENGTH 40 #else typedef struct in_addr sq_in_addr; typedef struct sockaddr_in sq_sockaddr_in; #define SQ_AF_INET AF_INET #define SQ_PF_INET PF_INET #define sq_sin_family sin_family #define sq_sin_port sin_port #define sq_sin_addr sin_addr #define sq_s_addr s_addr #define CLIENT_IP_HEX_LEN 8 #define SQ_MAX_IP_STR_LENGTH 16 #endif int main(){ sq_sockaddr_in serv_addr; char *data = "Hello, tcp fast open"; int data_len = strlen(data); int sfd = socket(SQ_AF_INET, SOCK_STREAM, 0); #if USE_IPV6 inet_pton(SQ_AF_INET, "0:0:0:0:0:0:0:0", (struct sq_sockaddr_in *)&serv_addr.sq_sin_addr); #else inet_pton(SQ_AF_INET, "0:0:0:0", (struct sq_sockaddr_in *)&serv_addr.sq_sin_addr); #endif serv_addr.sq_sin_family = SQ_AF_INET; serv_addr.sq_sin_port = htons(5060); int len = sendto(sfd, data, data_len, MSG_FASTOPEN, (struct sockaddr *) &serv_addr, sizeof(serv_addr)); printf("error: %d-%sn", errno, strerror(errno)); close(sfd); }
server.c
#include#include #include #include #include #include #define USE_IPV6 1 #if USE_IPV6 typedef struct in6_addr sq_in_addr; typedef struct sockaddr_in6 sq_sockaddr_in; #define SQ_AF_INET AF_INET6 #define SQ_PF_INET PF_INET6 #define sq_sin_family sin6_family #define sq_sin_port sin6_port #define sq_sin_addr sin6_addr #define sq_s_addr s6_addr #define SQ_INADDR_ANY IN6ADDR_ANY_INIT #define CLIENT_IP_HEX_LEN 32 #define SQ_MAX_IP_STR_LENGTH 40 #else typedef struct in_addr sq_in_addr; typedef struct sockaddr_in sq_sockaddr_in; #define SQ_AF_INET AF_INET #define SQ_PF_INET PF_INET #define sq_sin_family sin_family #define sq_sin_port sin_port #define sq_sin_addr sin_addr #define sq_s_addr s_addr #define SQ_INADDR_ANY INADDR_ANY #define CLIENT_IP_HEX_LEN 8 #define SQ_MAX_IP_STR_LENGTH 16 #endif int main(){ char buffer[256] = {0x0}; sq_sockaddr_in serv_addr; int cfd = 0; int sfd = socket(SQ_AF_INET, SOCK_STREAM, 0); // Create socket bzero((char *) &serv_addr, sizeof(serv_addr)); serv_addr.sq_sin_family = SQ_AF_INET; #if USE_IPV6 inet_pton(SQ_AF_INET, "0:0:0:0:0:0:0:0", (struct sq_sockaddr_in *)&serv_addr.sq_sin_addr); #else inet_pton(SQ_AF_INET, "0:0:0:0", (struct sq_sockaddr_in *)&serv_addr.sq_sin_addr); #endif serv_addr.sq_sin_port = htons(5060); bind(sfd, (const struct sockaddr *)&serv_addr,sizeof(serv_addr)); // Bind to well known address int qlen = 5; // Value to be chosen by application int err = setsockopt(sfd, IPPROTO_TCP, 23, &qlen, sizeof(qlen)); listen(sfd, 1); // Mark socket to receive connections cfd = accept(sfd, NULL, 0); // Accept connection on new socket while(1){ int len = read(cfd, buffer, 256); if(len > 0) printf("tcp fast open: %s\n", buffer); else break; // read and write data on connected socket cfd } close(cfd); }
备注:
高版本系统支持TCP_FASTOPEN_CONNECT,在内核层面支持tfo的connect处理



