從應用層程式取得傳輸層 TCP 資訊

Aaron Liao,2014

目的:

當設計伺服器網路應用程式時,由於應用層本身並沒有網路底層的資訊,因此當有異常發生時,無法立即直接知道連線的資訊,通常需要藉由評估資料收送狀況、逾時(timeout)等方式間接得知。若伺服器應用程式可以取得得知目前 TCP 的狀態,就能知道連線是否還存在著,即使只是 TCP 連線狀態的改變,都能清楚知曉,而伺服器能提早對該連線進行處理。

我們可以在 netinet/tcp.h 表頭檔案找到 TCP 資訊的定義。(在 Linux 系統的絕對路徑位於 /usr/include/netinet/tcp.h)。

若你想要取得 TCP 連線的狀態,你可以引用這個表頭檔。

#include <netinet/tcp.h> 50 #define TCP_INFO 11 /* Information about this connection. */

這邊是 TCP 資訊的資料結構

199 struct tcp_info 200 { 201 u_int8_t tcpi_state; 202 u_int8_t tcpi_ca_state; 203 u_int8_t tcpi_retransmits; 204 u_int8_t tcpi_probes; 205 u_int8_t tcpi_backoff; 206 u_int8_t tcpi_options; 207 u_int8_t tcpi_snd_wscale : 4, tcpi_rcv_wscale : 4; 208 209 u_int32_t tcpi_rto; 210 u_int32_t tcpi_ato; 211 u_int32_t tcpi_snd_mss; 212 u_int32_t tcpi_rcv_mss; 213 214 u_int32_t tcpi_unacked; 215 u_int32_t tcpi_sacked; 216 u_int32_t tcpi_lost; 217 u_int32_t tcpi_retrans; 218 u_int32_t tcpi_fackets; 219 220 /* Times. */ 221 u_int32_t tcpi_last_data_sent;222 u_int32_t tcpi_last_ack_sent; /* Not remembered, sorry. */ 223 u_int32_t tcpi_last_data_recv; 224 u_int32_t tcpi_last_ack_recv; 225 226 /* Metrics. */ 227 u_int32_t tcpi_pmtu; 228 u_int32_t tcpi_rcv_ssthresh; 229 u_int32_t tcpi_rtt; 230 u_int32_t tcpi_rttvar; 231 u_int32_t tcpi_snd_ssthresh; 232 u_int32_t tcpi_snd_cwnd; 233 u_int32_t tcpi_advmss; 234 u_int32_t tcpi_reordering; 235 236 u_int32_t tcpi_rcv_rtt;237 u_int32_t tcpi_rcv_space; 238 239 u_int32_t tcpi_total_retrans; 240 };

// Enum of TCP status

136 enum137 {138 TCP_ESTABLISHED = 1,139 TCP_SYN_SENT,140 TCP_SYN_RECV,141 TCP_FIN_WAIT1,142 TCP_FIN_WAIT2,143 TCP_TIME_WAIT,144 TCP_CLOSE,145 TCP_CLOSE_WAIT,146 TCP_LAST_ACK,147 TCP_LISTEN,148 TCP_CLOSING /* now a valid state */149 };

這邊用一個簡單的函式範例來說明如何取得 TCP 資訊。如果你的程式中的 socket descriptor 名為 sd,那麼你就能用 getsockopt() 取得 TCP 的資訊。這邊我將 getsockopt() system call 包到 get_tcp_state() 函式,而 get_tcp_state() 函式就會將 sd socket descriptor 的 TCP 的狀態傳回。

int get_tcp_state(int sd){ struct tcp_info t_info; /* the data structure of the TCP information */ socklen_t t_info_len = sizeof(t_info); if( getsockopt( sd, IPPROTO_TCP, TCP_INFO, &t_info, &t_info_len) != -1) { return t_info.tcpi_state; /* the state of the 'sd' TCP connection */ } return -1;}