为何TCP报头WINDOW字段被设为0?SYN包场景存疑
关于SYN包中TCP窗口字段设为0的疑问
我找到一段可正常运行的原始SYN报文发送代码,但里面设置TCP窗口字段的方式很奇怪:用htonl(65535)给tcph->window赋值,而window是16位字段,实际结果是0x0000。我搞不懂两点:
- 为什么不直接设为0?
- SYN包里把窗口设为0有什么意义?按我理解,窗口为0表示接收缓冲区已满,示意对方暂停发送,但SYN包本来是等对方回应的,不该让对方停发,而且我用Wireshark验证过窗口确实是0。
代码片段
void setup_tcp_header(struct tcphdr *tcph) { tcph->source = htons(src_port); // SOURCE PORT tcph->dest = htons(dst_port); // TARGET PORT tcph->seq = random(); tcph->ack_seq = 0; tcph->res2 = 0; tcph->doff = 5; tcph->syn = 1; tcph->window = htonl(65535); // 0x FFFF 0000 but window is uint16_t so only gets the 00 00 // LSBs tcph->check = 0; tcph->urg_ptr = 0; }
解答
为什么不直接设为0?
这明显是代码笔误。htonl()是用来转换32位整数的主机字节序到网络字节序,而tcph->window是16位的uint16_t类型。65535的32位二进制表示是0x0000FFFF,经过htonl()转成大端字节序后变成0xFFFF0000,赋值给16位字段时只会保留低16位,也就是0x0000。
写这段代码的人大概率是想把窗口设为最大的16位值65535,结果误用了htonl()(正确应该用htons()),歪打正着得到了0的结果。如果真的要设窗口为0,正确写法应该是tcph->window = htons(0),现在的写法完全是错误的实现,只是碰巧达成了设0的效果。
SYN包窗口设为0的意义?
首先明确:标准TCP握手的SYN包,窗口字段应该填写本地接收缓冲区的大小,用来告知对方自己能接收的最大字节数,设为0是不符合规范的行为。
但你的代码能正常运行,是因为多数TCP实现对SYN包的窗口字段有较高容错性:
- SYN包本身不携带数据,对方回复的SYN+ACK包也只是完成握手流程,不会立刻发送业务数据,所以窗口为0暂时不会阻碍握手。
- 等三次握手完成后,后续的ACK包会更新正确的接收窗口大小,对方会根据新的窗口值发送数据,因此整个连接能正常建立。
不过这种写法属于不规范的错误写法,严格来说应该把SYN包的窗口字段设置为合理的接收缓冲区大小(比如tcph->window = htons(65535)),设为0只是被多数实现兼容了而已。
内容的提问来源于stack exchange,提问作者X-Maki
相关产品推荐
相关产品推荐

