TCP协议关键数据结构

49 篇文章 11 订阅
43 篇文章 1 订阅

1、TCP协议头数据结构

TCP协议头数据结构是struct tcphdr,定义在include/linux/tcp.h中,主要包含源端口、目的端口、协议长度、控制标志flags....

struct tcphdr {
	__be16	source;		//源端口
	__be16	dest;		//目的端口
	__be32	seq;			//数据段的起始序列号	
	__be32	ack_seq;		//确认序列号
#if defined(__LITTLE_ENDIAN_BITFIELD)   //小端                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                
	__u16	res1:4,
		doff:4,			//协议头长度
		fin:1,			//断开标志
		syn:1,			
		rst:1,
		psh:1,
		ack:1,
		urg:1,
		ece:1,
		cwr:1;
#elif defined(__BIG_ENDIAN_BITFIELD)    //大端
	__u16	doff:4,
		res1:4,
		cwr:1,
		ece:1,
		urg:1,
		ack:1,
		psh:1,
		rst:1,
		syn:1,
		fin:1;
#else
#error	"Adjust your <asm/byteorder.h> defines"
#endif	
	__be16	window;		//窗口控制
	__sum16	check;		//阻塞控制
	__be16	urg_ptr;		
};

2、TCP的控制缓冲区

TCP是完全异步的协议,实际数据段的传送独立于来自所有套接字层的写操作。TCP层分配的socket buffer来存放应用层写入套接字的数据,但应用程序控制管理数据包的信息存放在TCP控制缓冲区,TCP缓冲区由struct tcp_skb_cb数据结构描述,当数据从应用程序复制到TCP层的socket buffer时,函数需要TCP控制缓冲区中TCP协议头的信息来设置struct tcphdr数据结构中的相关数据域。

struct tcp_skb_cb {
	union {
		struct inet_skb_parm	h4;							//ip选项
#if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE)
		struct inet6_skb_parm	h6;
#endif
	} header;	/* For incoming frames		*/
	__u32		seq;		/* Starting sequence number	数据段起始序列号*/
	__u32		end_seq;	/* SEQ + FIN + SYN + datalen	最后一个数据段结束序列号*/
	__u32		when;		/* used to compute rtt's	*/
	__u8		flags;		/* TCP header flags.	TCP协议头中的flags	*/

	/* NOTE: These must match up to the flags byte in a
	 *       real TCP header.
	 */
#define TCPCB_FLAG_FIN		0x01
#define TCPCB_FLAG_SYN		0x02
#define TCPCB_FLAG_RST		0x04
#define TCPCB_FLAG_PSH		0x08
#define TCPCB_FLAG_ACK		0x10
#define TCPCB_FLAG_URG		0x20
#define TCPCB_FLAG_ECE		0x40
#define TCPCB_FLAG_CWR		0x80

	__u8		sacked;		/* State flags for SACK/FACK.	前送回答/选择回答的状态标志*/
#define TCPCB_SACKED_ACKED	0x01	/* SKB ACK'd by a SACK block	*/
#define TCPCB_SACKED_RETRANS	0x02	/* SKB retransmitted		*/
#define TCPCB_LOST		0x04	/* SKB is lost			*/
#define TCPCB_TAGBITS		0x07	/* All tag bits			*/

#define TCPCB_EVER_RETRANS	0x80	/* Ever retransmitted frame	*/
#define TCPCB_RETRANS		(TCPCB_SACKED_RETRANS|TCPCB_EVER_RETRANS)

	__u32		ack_seq;	/* Sequence number ACK'd	*/
};

关键元素详解:

seq:输出数据段的起始序列号。

end_seq:最后一个输出数据段结束序列号,结束序列号的值等于最后发送的数据段序列号 + 一个SYN数据段 + 一个FIN数据段 + 数据段长度,即 end_seq = seq + SYN + FIN + 当前数据段长度。

when:用于计算RTT(Round-trip time)值,即数据段在网络上传送时间,when数据域用来管理数据段传输起始计时和数据重传,TCP数据段发送方根据when的值计算出数据后要等待多长时间,如果等待一定时间后仍没有收到接受段回复的ACK,就重传数据段。

flags:与TCP协议头中的flags数据域定义相同。

sacked:保存了选择回答(SACK:Selective Acknowledge)和前送回答(FACK:Forward Acknowledge)的状态标志,有效标志如下:

有效的状态标志位
标志作用
TCPCB_SACKED_ACKED1SACK块已经给出了skb数据缓冲区中的段回答信息
TCPCB_SACKED_RESTRANS2数据段需要重传
TCPCB_LOST4数据段丢失
TCPCB_TAGBITS 结合前面三个标志来标识数据段
TCPCB_EVER_RETRANS0X80指明数据段以前是否重传过
TCPCB_RETRANS 指明数段是一个重传过的数据段

TCPCB_TAGBITS = TCPCB_SACKED_ACKED |  TCPCB_SACKED_RESTRANS | TCPCB_LOST

3、TCP套接字数据结构

TCP套接字数据结构struct tcp_sock包含了管理TCP协议各方面的信息,如发送和接受方的序列号、TCP窗口尺寸、避免网络阻塞等。struct sock数据结构很庞大,在include/linux/tcp.h文件中定义。

(1)inet_conn:INET协议族面向连接的套接字结构体,定义在include/net/inet_connection_sock文件中,其中包含了sturct inet_connection_sock_af_ops *icsk_af_ops数据结构,这个数据结构是套接字操作函数指针,比如getsockopt函数和setsockopt函数指针。

struct inet_connection_sock_af_ops {
	int	    (*queue_xmit)(struct sk_buff *skb);
	void	    (*send_check)(struct sock *sk, struct sk_buff *skb);
	int	    (*rebuild_header)(struct sock *sk);
	int	    (*conn_request)(struct sock *sk, struct sk_buff *skb);
	struct sock *(*syn_recv_sock)(struct sock *sk, struct sk_buff *skb,
				      struct request_sock *req,
				      struct dst_entry *dst);
	int	    (*remember_stamp)(struct sock *sk);
	u16	    net_header_len;
	u16	    sockaddr_len;
	int	    (*setsockopt)(struct sock *sk, int level, int optname, 
				  char __user *optval, unsigned int optlen);
	int	    (*getsockopt)(struct sock *sk, int level, int optname, 
				  char __user *optval, int __user *optlen);

...
}

(2)tcp_hader_len:传送数据段TCP协议头的长度。

(3)xmit_size_goal:传输数据段你的目标。

(4)pred_flags:TCP协议预定向完成标志。

(5)rcv_nxt:下一个输入数据段序列号。

(6)snd_nxt:下一个发送数据段的序列号。

(7)prequeue:输入队列。

(8)task:用户进程,接受prequeue队列中数据段的用户进程。

(9)iov:向量指针,指向用户地址空间中存放数据的数组。

(10)memory:在prequeue队列中所有socket buffer中数据长度的总和。

(11)Len:prequeue队列上socket buffer缓冲区的个数。

(12)DMA,当网络设备支持Scatter/Gather I/O功能,可以利用DMA直接访问内存,将数据异步从网络设备硬件缓冲区复制到应用程序地址空间的缓冲区。

...

//DMA
#ifdef CONFIG_NET_DMA
		/* members for async copy */
		struct dma_chan		*dma_chan;
		int			wakeup;
		struct dma_pinned_list	*pinned_list;
		dma_cookie_t		dma_cookie;
#endif

...

struct tcp_sock:

struct tcp_sock {
	/* inet_connection_sock has to be the first member of tcp_sock */
	struct inet_connection_sock	inet_conn;				//面向连接结构体
	u16	tcp_header_len;	/* Bytes of tcp header to send		TCP协议头长度*/
	u16	xmit_size_goal_segs; /* Goal for segmenting output packets 输出数据段目标*/

/*
 *	Header prediction flags
 *	0x5?10 << 16 + snd_wnd in net byte order
 */
	__be32	pred_flags;


 	u32	rcv_nxt;	/* What we want to receive next 	*/
	u32	copied_seq;	/* Head of yet unread data		*/
	u32	rcv_wup;	/* rcv_nxt on last window update sent	*/
 	u32	snd_nxt;	/* Next sequence we send		*/

 	u32	snd_una;	/* First byte we want an ack for	*/
 	u32	snd_sml;	/* Last byte of the most recently transmitted small packet */
	u32	rcv_tstamp;	/* timestamp of last received ACK (for keepalives) */
	u32	lsndtime;	/* timestamp of last sent data packet (for restart window) */

...

}

4、TCP协议选项Options

TCP协议是可配置协议,TCP选项可以通过setsockopt系统调用来设置,也可以通过getsockopt来返回当前TCP选项,TCP选项在struct tcp_sock数据结构中定义了,下面介绍TCP选项值的含义:

(1)、TCP_CORK/nonagle

这个选项对应struct tcp_sock中的nonagle数据域,如果配置了这个选项接受应用层的数据后TCP不会立即发送数据段,知道数据段达到TCP协议数据段最大值,它使应用程序可以在路由的MTU小于TCP的数据段最大段大小(MSS)时停止发送。TCP_CORK和TCP_NODELAY选项是互斥的。

(2)、TCP_DEFER_ACCEPT/defer_accept

应用层序调用者在数据还没到达套接字之前,可以处于休眠状态。但当数据到达套接字时应用程序被唤醒,如果等待超时应用层序也会被唤醒,盗用者设定一个时间值来描述应用程序等待数据到达的时间。该选项保存struct sock 数据结构的defer_accept数据域。

(3)、TCP_INFO

使用此选项可以获取大部分套接字的配置信息,获取的配置信息保存在struct tcp_info数据结构中:

struct tcp_info {
	__u8	tcpi_state;		//当前tcp连接状态
	__u8	tcpi_ca_state;
	__u8	tcpi_retransmits;
	__u8	tcpi_probes;
	__u8	tcpi_backoff;
	__u8	tcpi_options;
	__u8	tcpi_snd_wscale : 4, tcpi_rcv_wscale : 4;

	__u32	tcpi_rto;
	__u32	tcpi_ato;
	__u32	tcpi_snd_mss;
	__u32	tcpi_rcv_mss;

	__u32	tcpi_unacked;
	__u32	tcpi_sacked;
	__u32	tcpi_lost;
	__u32	tcpi_retrans;
	__u32	tcpi_fackets;

	/* Times. */
	__u32	tcpi_last_data_sent;		//最近发送数据的时间戳
	__u32	tcpi_last_ack_sent;     /* Not remembered, sorry. */
	__u32	tcpi_last_data_recv;		//最近接受数据的时间戳
	__u32	tcpi_last_ack_recv;

	/* Metrics. */
	__u32	tcpi_pmtu;				//mtu
	__u32	tcpi_rcv_ssthresh;
	__u32	tcpi_rtt;
	__u32	tcpi_rttvar;
	__u32	tcpi_snd_ssthresh;
	__u32	tcpi_snd_cwnd;
	__u32	tcpi_advmss;
	__u32	tcpi_reordering;

	__u32	tcpi_rcv_rtt;
	__u32	tcpi_rcv_space;

	__u32	tcpi_total_retrans;
};

(4)、TCP_KEEPCNT

此选项可以设置TCP在断开连接之前可以通过套接字发送多少个保持连接活动(keepalive)的探测数据段,该选项存放在struct tcp_sock数据结构中的keepalive_probes数据域,如果设置了该选项还要设置套接字层的SO_KEEPALIVE选项。

(5)、TCP_KEEPIDEL

TCP开始传送连接是否保持活动的探测数据段之前,连接处于空闲状态的时间值,保持在struct tcp_sock数据结构中的keepalive_time数据域,默认值是2个小时,如果设置此选项还要设置套接字层的SO_KEEPALIVE选项。

(6)、TCP_KEEPINTVL 

设定两次传送探测连接保持活动数据段之前要等待多少秒,该值存放在struct tcp_sock数据结构的deepalive_intvl数据域中,初始值是75秒。

(7)TCP_LINGER2

此选项指处于FIN_WAIT2状态的孤立套接字还应保持存活多长时间,如果是0则关闭选项,linux使用常规方式处理FIN_WAIT_2和TIME_WAIT状态,如果值小于0,则套接字立即从FIN_WAIT2状态进入CLOSED状态,不经过FIN_WAIT,此选项保存在struct tcp_sock数据结构的liinger2数据域中,默认值由sysctl决定。

(8)、TCP_NODELAY

如果设置了该选项则TCP会立即把数据发送到网络层,而不会等待数据达到pmtu才发送,改值保存在struct tcp_sock数据结构的nonagle数据域中,和TCP_CORK互斥。

(9)、TCP_MAXSEG

此选项指定TCP最大数据段的大小值,TCP的MSS值也是此选项决定,但MSS的值不能超过MTU,TCP连接两端可以协商数据段大小。

(10)、TCP_QUICKACK

当设置这个值为1,就会关闭延迟回答,延迟回答是linux TCP的一个常规模式。延迟回答时ACK数据会延迟到可以与一个等待发送到另一端的数据段合并时,才会发送出去,如果设置为1,sruct tcp_sock数据结构中的ack部分的pingpong数据域设为0,就可以禁止延迟发送。

(11)、TCP_SYNCNT

这个选项是TCP在尝试建立连接,如果连接没有建立起来,要重传多少此SYN包后就放弃建立连接请求,该选项保存在struct tcp_syn_retries数据域中。

(12)、TCP_WINDOW_CLAMP

指定套接字窗口大小,窗口的最小值是SOCK_MIN_RCVBUF除以2,等于128个字节,该选项保存在struct tcp_sock数据结构中的windown_clamp数据域。

4、struct msghdr

应用层传给套接字的信息结构体是struct msghdr,在传送处理函数中把数据从应用层复制到内核地址空间,结构体定义在include/linux/socket.c文件中

struct msghdr {
	void	*	msg_name;	/* Socket name 套接字名字,也是对端ip地址*/
	int		msg_namelen;	/* Length of name		目的地址长度*/
	struct iovec *	msg_iov;	/* Data blocks		应用层缓冲区起始地址	*/
	__kernel_size_t	msg_iovlen;	/* Number of blocks	缓冲区数组个数	*/
	void 	*	msg_control;	/* Per protocol magic (eg BSD file descriptor passing) 控制信息*/
	__kernel_size_t	msg_controllen;	/* Length of cmsg list 控制信息长度*/
	unsigned	msg_flags;	//接受数据的标志
};

msg_name:套接字名字,不是实际套接字名,他是一个指针,指向struct sockaddr_in数据结构的变量,struct sockaddr_in数据结构包含了发送数据段的目标IP地址和端口号。

msg_namelen:msg_name指向的地址信息长度。

msg_iov:指向应用层缓冲区数组的起始地址。

msg_iovlen:msg_iov缓冲区数组中缓冲区的个数。

msg_control:保存向套接字层下发的协议控制信息。

msg_flags:套接字从应用层接受到的控制标志。

msg_falgs中标志位有效值
标志说明
MSG_OOB1请求out-of-bound数据
MSG_PEEK2从接受队列中返回数据,但不把数据从队列中移走
MSG_DONTROUTE4不对该数据路由,常用于ping历程中传送ICMP数据包
MSG_TRYHARD4不用于TCP/IP协议栈,与MSG_DONTROUTE同义
MSG_CTRUNC8用于SOL_IP内部控制信息
MSG_PROBE0x10用于发现MTU数据段
MSG_TRUNC0x20truncate消息
MSG_NONTWAIT0x40调用应用程序是否用于不被阻塞的I/O
MSG_EOR0x80信息结束
MSG_WAITALL0x100在返回数据前等待所有数据到达
MSG_FIN0x200TCP结束数据段(FIN)
MSG_SYN0x800TCP同步数据段(SYN)
MSG_CONFIRM0x800传送数据包前确认路径有效
MSG_RST0x1000TCP复位连接数据段(RST)
MSG_ERRQUEUE0x2000从错误队列读取数据段
MSG_NOSIGNAL0x4000当确认连接断开,不产生SIGPIPE信号
MSG_MORE0x8000指明将发送更多的数据信息

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值