菜鸟笔记
提升您的技术认知

面试经典:tcp粘包是怎么回事?-ag真人官方网

前言

关于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算法

最近和几个大学学弟聊天,他们在找实习面试时这种问题还是会经常被问到的,

网站地图