前言
关于TCP协议的问题在面试中会经常被问道,尤其是在应届生面试中。
TCP协议是面向连接的可靠性协议。说它可靠并不表示数据信息一定会被对端接受,而是在传输失败后会放弃重传机制并中断连接来通知用户。它提供的只是数据可靠性的传输和故障通知。
回到TCP粘包这个问题上,TCP是数据流传输,数据流是只有起点和终点的字节数据序列,只有输入流和输出流。根本不存在“包”的概念。
那大家常说的粘包的“包”是指什么呢?
其实大家说的是应用层的包,应用层协议规定了包的结构和大小,本质上就是一段数据报文。
具体的包可能像这样
//简简单单 自定义应用层协议
struct Message {
int packSize;
int type;
char buf[100];
};
//http 请求
GET /hello.txt HTTP/1.1
User-Agent: curl/7.16.3 libcurl/7.16.3 OpenSSL/0.9.7l zlib/1.2.3
Host: www.example.com
Accept-Language: en, mi
TCP粘包的原因
在说原因之前我先讲讲粘包的两种情况:
假设有A,B两个包,A包在B包之前
1.接收端接受缓冲区中有A包的一部分
2.接收端接受缓冲区中有A包和B包的一部分
上述情况都属于粘包。
发送端原因:
TCP连接在默认情况下开启Nagle算法优化。当一个连接上有待确认的数据时(也就是发送端没有收到接受端发回来的ACK),此时调用send发送数据,只是将数据填充到发送缓冲区中。等收到ACK后一起发送。
接收端原因:
处理数据不及时,数据都堆积在接收缓冲区中,致使AB两包相连。
如何解决粘包
1.固定包长度。每个包的大小都是一样的。 2.设置特定的结束标志。 3.在包中给定包大小。 例如:(固定前4个字节为包大小) struct Message { ? ? int packSize; ? ? int type; ? ? char buf[100]; };
http协议就是使用了上述的2,3方法。http使用"\r\n"为结束标识。当为post请求时则有content-Length标识请求体长度。
关于Nagle算法
我再说说nagle算法吧,上面说得比较简略。
Nagle算法的目的是减少广域网中小分组的数目。也就是说尽可能在一次传输中多发送数据,从而减少频繁的网络交互。
/*
* Return 0, if packet can be sent now without violation Nagle's rules:
* 1. It is full sized ? ???
* 2. Or it contains FIN. (already checked by caller)? ? ???
* 3. Or TCP_CORK is not set, and TCP_NODELAY is set. ? ? ???
* 4. Or TCP_CORK is not set, and all sent packets are ACKed. ? ? ???
*?With Minshall's modification: all sent small packets are ACKed.?
*/???
static inline int tcp_nagle_check(const struct tcp_sock *tp,
const struct sk_buff *skb,
unsigned mss_now, int nonagle)
{
return skb->len < mss_now &&((nonagle & TCP_NAGLE_CORK) ||
(!nonagle && tp->packets_out && tcp_minshall_check(tp)));
} ? ?
根据linux源码可看出数据在哪些情况会立即发送:
1.当数据缓冲区数据>=MSS时。(MSS为TCP连接中最大报文长度,收发双方协商通信时每一个报文段所能承载的最大数据长度)
2.数据包包含FIN选项
3.TCP_CORK不设置,TCP_NODELAY选项设置
4.TCP_CORK不设置,收到接受方的ack确认
总结
TCP粘包就是应用层规定好了两个或多个数据包,同时存在于接受缓冲区,致使接收端要进行分包或组合的情况。
粘包情况是普遍存在的,通常通过指定包大小或指定结束标志来区分。
有些场景并不适合使用Nagle算法,像游戏类这种服务要求实时比较高的,就不需要开启。
可使用TCP选项,TCP_NODELAY关闭Nagle算法
最近和几个大学学弟聊天,他们在找实习面试时这种问题还是会经常被问到的,
这些都属于经典面试题,后续准备做一个面试系列,专门讲解一些应届生找工作或实习时遇到的真实面试问题。希望和大家一起进步。
?
|