libevent的粘包笔记
“粘包”现象是指 多个数据包的内容被合并在一起,一次性被接收端读取了,从而造成接收方难以区分包与包的边界。
简而言之:粘包 = 同时收到两个或多个包的内容了。
更详细解释:
粘包常出现在 TCP协议 中,原因是:
-
TCP 是面向字节流的协议,没有消息边界的概念;
-
如果发送端连续发送多个小数据包,TCP 可能会将它们合并为一个较大的包发送出去;
-
接收端读取时就会一次性读到多个包的内容,而不是一个一个独立的包。
举个例子:
假设发送端连续发送了两个数据包:
包1: Hello 包2: World
粘包时,接收端可能一次 recv() 就读到:
HelloWorld
对比一下“拆包”(另一个相关概念):
-
拆包 是指一个数据包太大,被 TCP 拆成了若干段分别发送,接收端一次 recv() 可能只能读到其中一部分。
如何解决粘包问题?
通常要靠 应用层协议来定包的边界,常见方法有:
-
固定长度:每个包长度固定,比如每个包100字节。
-
分隔符:在每个包末尾加上特殊字符,如
\n
、|END|
等。 -
包头加长度字段:先发送一个表示包体长度的字段,然后再发送包体。
其它问题
除了粘包和拆包的基本理论知识,想进行一次完整的实战体验,还要了解所用网络库的接口机制和底层机制,比如本次我操作的是:libevent网络库在差分数据同步中的应用。
先看下发送端代码把:
int net_send_diff_data(S_CONNECT *conn, char *dest_file_name, FILE_METEDATA *p_meta,MASTER_CONFIG *p_local_pmaster_cfg, GQueue *tag_queue)
{int len = 0;uint32_t total_len = 0;int left_len = 0;int ret = ZX_SUCCESS;int njson_len = 0;char *str_json = NULL;cJSON *cjson = NULL;FILE *fp = NULL;char* buffer = malloc(MAX_READ_BUFFER);char *pkg = NULL;int16_t uextend;uint64_t diff_data_len = 0;int diff_tags = tag_queue->length;EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();TRUNK_INFO* ptrunk_info = malloc(sizeof(TRUNK_INFO) + MAX_READ_BUFFER);if (!ptrunk_info){ret = ZX_ERR_GENERAL;goto exit;}(void)p_local_pmaster_cfg;cjson = cJSON_CreateObject();if(cjson){cJSON_AddStringToObject(cjson, "file_name", dest_file_name);#ifdef _MSC_VERsprintf(buffer, "%I64u", p_meta->file_stat.st_size);#elsesprintf(buffer, "%lu", p_meta->file_stat.st_size);#endifcJSON_AddStringToObject(cjson, "file_size", buffer);sprintf(buffer, "%d", tag_queue->length + 1);cJSON_AddStringToObject(cjson, "diff_trunks_sum", buffer);sprintf(buffer, "%d", p_meta->file_stat.st_mode);cJSON_AddStringToObject(cjson, "file_mode", buffer);}else{log_error(__FILE__,__LINE__,"cJSON_CreateObject failed");ret = ZX_ERR_GENERAL;goto exit;}str_json = cJSON_Print(cjson);njson_len = strlen(str_json);cJSON_Delete(cjson);fp = zfopen(p_meta->file_name, "rb");if(!fp){log_error(__FILE__,__LINE__,"net_send_diff_data fopen %s failed ,ERR : %s",p_meta->file_name, strerror(errno));ret = ZX_ERR_FD_OPEN;goto exit;}pkg = malloc(sizeof(COMMON_PKG) + njson_len);uextend = -100 - njson_len;/* 发包头 */net_create_package((COMMON_PKG *)pkg, PKG_TYPE_ZCOPY_TO_ZCOPY_SYNC,PKG_TYPE_SYNC_SET_FILE_TRUNKS_DATA_REQ,IS_JSON,