tcp回顯)
在ESTABLISHED狀態(tài)雙方發(fā)送數(shù)據(jù)時候不管是客戶端還是服務(wù)端發(fā)送數(shù)據(jù)后對方都需要回復(fù)一個ack。這個ack可以是單獨的包也可以和需要發(fā)送的數(shù)據(jù)包放在一起發(fā)送。我們需要實現(xiàn)的是回顯那么接收到客戶端的數(shù)據(jù)后需要組織三個包第一個包是接收客戶端的數(shù)據(jù)第二個包是構(gòu)造ack第三個包是構(gòu)造發(fā)送的數(shù)據(jù)包。全面梳理ackseq的流程。兩個結(jié)構(gòu)體tcp_socket_entrytcp_datagram一個全局變量tcp_table。struct tcp_socket_entry{ int fd; //unsigned int status; // 阻塞 非阻塞 uint32_t dip; uint32_t sip; uint16_t dport; uint16_t sport; uint8_t localmac[RTE_ETHER_ADDR_LEN]; uint8_t protocol; uint32_t seq_next; uint32_t ack_next; NG_TCP_STATUS status; struct rte_ring *recvbuf; struct rte_ring *sendbuf; struct tcp_socket_entry *prev; struct tcp_socket_entry *next; pthread_cond_t cond; pthread_mutex_t mutex; }; struct tcp_table{ struct tcp_socket_entry *tcpList; int count; }; struct tcp_datagram{ uint16_t sport; uint16_t dport; uint32_t seqnum; uint32_t acknum; uint8_t hdrlen_off; // 高四位頭部長度 低四位保留位 uint8_t tcp_flags; uint16_t windows; uint16_t cksum; uint16_t tcp_urp; // 緊急指針 /* 頭部長度 4bit 最大為15. 1bit代表4Byte 所以tcp頭最多15*460Byte 前面固定20B,選項最多40B uint32 4B TCP_OPTION_LENGTH10 正好40B */ int optlen; // 表示option的真實長度 uint32_t option[TCP_OPTION_LENGTH]; unsigned char *data; int datalenth; };tcp_socket_entry是面向連接的一個連接一個tcp_socket_entry里面有兩個參數(shù)uint32_t seq_next;uint32_t ack_next;存儲的是下一次發(fā)送所需要的ackseq。tcp_datagram是組織tcp數(shù)據(jù)包用的里面也有兩個參數(shù)uint32_t seqnum;uint32_t acknum;里面存儲的是這一次組織數(shù)據(jù)包需要的seq和ack。在tcp pkt process時根據(jù)sip,dip,sport,dport查找entry如果沒有就創(chuàng)建一個entry指明seq_next為一個隨機數(shù)。// 返回時間戳秒,距離UTC1970 uint32_t seqnum time(NULL); entry-seq_next rand_r(seqnum) % TCP_MAX_SEQ;在listen時候再設(shè)置ack_nextntohl(tcphdr-sent_seq) 1;在listen的時候也需要組織數(shù)據(jù)包這里指定datatcp-seqnum entrytcp-seq_next; datatcp-acknum ntohl(tcphdr-sent_seq) 1;因為客戶端發(fā)來一個syn所以要回復(fù)這里的acknum對方的seq1因為syn占用一個序號。但是對于tcphdr-sent_seq是需要轉(zhuǎn)換字節(jié)序的。在組織服務(wù)端ack包時有如下代碼。printf(tcp_handle_established\n); printf(entrytcp-ack_next: %u, entrytcp-seq_next %u\n, entrytcp-ack_next,entrytcp-seq_next); printf(tcphdr-sent_seq: %u, ntohs(tcphdr-sent_seq):%u\n,tcphdr-sent_seq, ntohl(tcphdr-sent_seq)); printf(tcphdr-recv_ack: %u, ntohs(tcphdr-recv_ack):%u\n,tcphdr-recv_ack, ntohl(tcphdr-recv_ack)); entrytcp-ack_next entrytcp-ack_next payloadlen; entrytcp-seq_next ntohl(tcphdr-recv_ack); ackdata-seqnum entrytcp-seq_next; ackdata-acknum entrytcp-ack_next; printf(ackdata-seqnum: %u, ackdata-acknum :%u\n,ackdata-seqnum, ackdata-acknum ); tcp_handle_established entrytcp-ack_next: 2835143939, entrytcp-seq_next 146615313 tcphdr-sent_seq: 64879784, ntohl(tcphdr-sent_seq):2835143939 tcphdr-recv_ack: 825015560, ntohl(tcphdr-recv_ack):146615345 ackdata-seqnum: 146615345, ackdata-acknum :2835143971可以看出entrytcp-ack_next ntohl(tcphdr-sent_seq)與上文設(shè)置的ack_next正好相符。entrytcp-seq_next ntohl(tcphdr-recv_ack)entrytcp-ack_next ntohl(tcphdr-sent_seq)payloadlen; // entrytcp-ack_next entrytcp-ack_next payloadlen;兩條語句都可以。判斷接收的數(shù)據(jù)包合不合法時就可以通過判斷源ntohl(tcphdr-sent_seq)與entrytcp-ack_next是不是相等來判斷相等則合法。本端ack 對端seq對端發(fā)送的數(shù)據(jù)長度。entrytcp-ack_next ntohl(tcphdr-sent_seq)payloadlen;本端seq 對端的ack。entrytcp-seq_next ntohl(tcphdr-recv_ack);對端的seq 與 本端的上一次發(fā)送的ack相等。entrytcp-ack_next ntohl(tcphdr-sent_seq)組織的接收數(shù)據(jù)用的recvdata加入entrytcp的recvbuf。組織ack包的時候需要更新entrytcp的ack_next與seq_next。然后把組織好的數(shù)據(jù)包加入entrytcp的sendbuf。組織的發(fā)送數(shù)據(jù)的包的ack與seq和ack包的這兩個參數(shù)相等。因為ACK不占用序號。然后加入到sendbuf。static int fun_tcp_handle_established_rcvd(struct tcp_socket_entry* entrytcp,struct rte_tcp_hdr * tcphdr,int tcplen){ if(tcphdr-tcp_flags RTE_TCP_SYN_FLAG){ } if(tcphdr-tcp_flags RTE_TCP_PSH_FLAG){ /****************************** recv datagram ******************************/ struct tcp_datagram *recvdata rte_malloc(tcp recv datagram,sizeof(struct tcp_datagram),0); if(recvdata NULL){ rte_exit(EXIT_FAILURE,tcp recv datagram malloc fail\n); } memset(recvdata,0,sizeof(struct tcp_datagram)); recvdata-sport ntohs(tcphdr-src_port); recvdata-dport ntohs(tcphdr-dst_port); uint8_t hdrlen tcphdr-data_off 4; // payload有效載荷,協(xié)議中的數(shù)據(jù)部分 int payloadlen tcplen - hdrlen*4; if(payloadlen 0){ uint8_t *payload (uint8_t *)tcphdr hdrlen * 4; recvdata-data rte_malloc(tcp recv data,payloadlen1,0); if(recvdata-data NULL){ rte_free(recvdata); rte_exit(EXIT_FAILURE,recv data malloc fail\n); } memset(recvdata-data,0,payloadlen1); rte_memcpy(recvdata-data,payload,payloadlen); recvdata-datalenth payloadlen; printf(tcp recv data:%s len:%d\n,recvdata-data,payloadlen); } rte_ring_mp_enqueue(entrytcp-recvbuf,recvdata); /****************************** ack datagram ******************************/ struct tcp_datagram *ackdata rte_malloc(tcp ack data,sizeof(struct tcp_datagram),0); if(ackdata NULL){ rte_exit(EXIT_FAILURE,tcp ack data malloc fail\n); } memset(ackdata,0,sizeof(struct tcp_datagram)); ackdata-sport tcphdr-dst_port; ackdata-dport tcphdr-src_port; printf(tcp_handle_established\n); printf(entrytcp-ack_next: %u, entrytcp-seq_next %u\n, entrytcp-ack_next,entrytcp-seq_next); printf(tcphdr-sent_seq: %u, ntohl(tcphdr-sent_seq):%u\n,tcphdr-sent_seq, ntohl(tcphdr-sent_seq)); printf(tcphdr-recv_ack: %u, ntohl(tcphdr-recv_ack):%u\n,tcphdr-recv_ack, ntohl(tcphdr-recv_ack)); entrytcp-ack_next ntohl(tcphdr-sent_seq)payloadlen; // entrytcp-ack_next entrytcp-ack_next payloadlen; entrytcp-seq_next ntohl(tcphdr-recv_ack); ackdata-seqnum entrytcp-seq_next; ackdata-acknum entrytcp-ack_next; printf(ackdata-seqnum: %u, ackdata-acknum :%u\n,ackdata-seqnum, ackdata-acknum ); ackdata-hdrlen_off 0x50; ackdata-tcp_flags RTE_TCP_ACK_FLAG; ackdata-windows TCP_INITIAL_WINDOW; ackdata-data NULL; ackdata-datalenth 0; rte_ring_mp_enqueue(entrytcp-sendbuf,ackdata); /****************************** send datagram ******************************/ struct tcp_datagram *senddata rte_malloc(tcp send data,sizeof(struct tcp_datagram),0); if(senddata NULL){ rte_exit(EXIT_FAILURE,tcp send data malloc fail\n); } memset(senddata,0,sizeof(struct tcp_datagram)); senddata-sport tcphdr-dst_port; senddata-dport tcphdr-src_port; senddata-seqnum entrytcp-seq_next; senddata-acknum entrytcp-ack_next; senddata-hdrlen_off 0x50; senddata-tcp_flags RTE_TCP_ACK_FLAG | RTE_TCP_PSH_FLAG; senddata-windows TCP_INITIAL_WINDOW; uint8_t *payload (uint8_t *)tcphdr hdrlen * 4; senddata-data rte_malloc(tcp send data,payloadlen1,0); if(senddata-data NULL){ rte_free(senddata); rte_exit(EXIT_FAILURE,recv data malloc fail\n); } memset(senddata-data,0,payloadlen1); rte_memcpy(senddata-data,payload,payloadlen); senddata-datalenth payloadlen; printf(tcp recv data:%s\n,senddata-data); rte_ring_mp_enqueue(entrytcp-sendbuf,senddata); } if(tcphdr-tcp_flags RTE_TCP_ACK_FLAG){ } if(tcphdr-tcp_flags RTE_TCP_FIN_FLAG){ } return 0; }文章參考零聲教育的C/Clinux服務(wù)期高級架構(gòu)系統(tǒng)教程學(xué)習(xí):0voice · GitHub