99热99这里只有精品6国产,亚洲中文字幕在线天天更新,在线观看亚洲精品国产福利片 ,久久久久综合网

歡迎加入QQ討論群258996829
麥子學(xué)院 頭像
蘋(píng)果6袋
6
麥子學(xué)院

詳解TCP粘包

發(fā)布時(shí)間:2016-07-05 23:42  回復(fù):0  查看:3132   最后回復(fù):2016-07-05 23:42  
學(xué)習(xí)網(wǎng)絡(luò)協(xié)議的過(guò)程中,我們也許會(huì)遇到TCP 粘包的現(xiàn)象,那設(shè)呢么是TCP 粘包呢,是什么原因造成的呢?這種問(wèn)題該怎么解決呢?給大家一個(gè)參考。
TCP粘包現(xiàn)象
TCP 粘包通俗來(lái)講,就是發(fā)送方發(fā)送的多個(gè)數(shù)據(jù)包,到接收方后粘連在一起,導(dǎo)致數(shù)據(jù)包不能完整的體現(xiàn)發(fā)送的數(shù)據(jù)。
TCP粘包原因分析
導(dǎo)致 TCP 粘包的原因,可能是發(fā)送方的原因,也有可能是接受方的原因。
發(fā)送方
由于 TCP 需要盡可能高效和可靠,所以 TCP 協(xié)議默認(rèn)采用 Nagle 算法,以合并相連的小數(shù)據(jù)包,再一次性發(fā)送,以達(dá)到提升網(wǎng)絡(luò)傳輸效率的目的。但是接收方并不知曉發(fā)送方合并數(shù)據(jù)包,而且數(shù)據(jù)包的合并在 TCP 協(xié)議中是沒(méi)有分界線的,所以這就會(huì)導(dǎo)致接收方不能還原其本來(lái)的數(shù)據(jù)包。
接收方
TCP 是基于 的。網(wǎng)絡(luò)傳輸數(shù)據(jù)的速度可能會(huì)快過(guò)接收方處理數(shù)據(jù)的速度,這時(shí)候就會(huì)導(dǎo)致,接收方在讀取緩沖區(qū)時(shí),緩沖區(qū)存在多個(gè)數(shù)據(jù)包。在 TCP 協(xié)議中接收方是一次讀取緩沖區(qū)中的所有內(nèi)容,所以不能反映原本的數(shù)據(jù)信息。
解決TCP粘包
分析了產(chǎn)生 TCP 粘包的原因之后,針對(duì)發(fā)生的原因,針對(duì)性的采取解決方法。
禁用 Negle 算法
因?yàn)? TCP 協(xié)議采用 Negle 算法,導(dǎo)致粘包。所以可以禁用 Nagle 算法。
const char chOpt = 1;int nErr = setsockopt(m_socket, IPPROTO_TCP, TCP_NODELAY, &chOpt, sizeof(char));   
if(nErr == -1)
{
    TRACE( "setsockopt() error\n",  WSAGetLastError());
    return ;
}
這種方法雖然能一定程度上解決 TCP 粘包,但是并不能完全解決問(wèn)題。因?yàn)榻邮辗揭彩强赡茉斐烧嘲脑?,這種方法只是發(fā)送方有效。而且禁用 Nagle 算法,一定程度上使 TCP 傳輸效率降低了。所以,這并不是一種理想的方法。
PUSH 標(biāo)志
PUSH TCP 報(bào)頭中的一個(gè)標(biāo)志位,發(fā)送方在發(fā)送數(shù)據(jù)的時(shí)候可以設(shè)置這個(gè)標(biāo)志位。該標(biāo)志通知接收方將接收到的數(shù)據(jù)全部提交給接收進(jìn)程。這里所說(shuō)的數(shù)據(jù)包括與此 PUSH 包一起傳輸?shù)臄?shù)據(jù)以及之前就為該進(jìn)程傳輸過(guò)來(lái)的數(shù)據(jù)。
當(dāng) Server 端收到這些數(shù)據(jù)后,它需要立刻將這些數(shù)據(jù)提交給應(yīng)用層進(jìn)程,而不再等待是否還有額外的數(shù)據(jù)到達(dá)。
設(shè)置 PUSH 標(biāo)志也不能完全解決 TCP 粘包,只是降低了接收方粘包的可能性。實(shí)際上現(xiàn)在的 TCP 協(xié)議棧基本上都可以自行處理這個(gè)問(wèn)題,而不是交給應(yīng)用層處理。所以設(shè)置 PUSH 標(biāo)志,也不是一種理想的方法。
自定協(xié)議
自定協(xié)議,將數(shù)據(jù)包分為了封包和解包兩個(gè)過(guò)程。在發(fā)送方發(fā)送數(shù)據(jù)時(shí),對(duì)發(fā)送的數(shù)據(jù)進(jìn)行封包操作。在接收方接收到數(shù)據(jù)時(shí)對(duì)接收的數(shù)據(jù)包需要進(jìn)行解包操作。
自定協(xié)議時(shí),封包就是為發(fā)送的數(shù)據(jù)增加包頭,包頭包含數(shù)據(jù)的大小的信息,數(shù)據(jù)就跟隨在包頭之后。當(dāng)然包頭也可以有其他的信息,比如一些做校驗(yàn)的信息。這里主要討論 TCP 粘包的問(wèn)題,所以不考慮其他的。
發(fā)送方封包
PACKAGE_HEAD pPackageHead; //PACKAGE_HEAD  包頭結(jié)構(gòu)體
char PackageHead[1024];
int headLen = sizeof(PACKAGE_HEAD);
int packgeContextLen = strlen(packageContext); //packageContext  發(fā)送的數(shù)據(jù)
pPackageHead->nDataLen = packgeContextLen; // 包的大小
char *packge = (char*)malloc(headLen + packgeContextLen); // 包的內(nèi)存分配
memset(packge, 0, headLen + packgeContextLen);
char *packgeCpy = (char*)memcpy(packge, (char*)&pPackageHead, headLen);// 拷貝包頭
packgeCpy += headLen;
packge = (char*)memcpy(packgeCpy, (char*)&packageContext, packgeContextLen);// 拷貝包內(nèi)容
int ret = 0;
ret = send(m_hSocket, packge, headLen + packgeContextLen, 0); // 發(fā)送包
if (ret == SOCKET_ERROR || ret == 0)
{
    return ret;
}
接收方解包
char PackageHead[1024];char PackageContext[1024*20];
int len;
PACKAGE_HEAD *pPackageHead; //PACKAGE_HEAD  包頭結(jié)構(gòu)體 while( m_bClose == false )
{
    memset(PackageHead, 0, sizeof(PACKAGE_HEAD));
    len = ReceiveSize(m_TcpSock, (char*)PackageHead, sizeof(PACKAGE_HEAD)); // 接收包頭
    if( len == SOCKET_ERROR )
    {
        break;
    }
    if(len == 0)
    {
        break;
    }
    pPackageHead = (PACKAGE_HEAD *)PackageHead;
    memset(PackageContext,0,sizeof(PackageContext));
    if(pPackageHead->nDataLen>0) // 根據(jù)包頭中的數(shù)據(jù)長(zhǎng)度,接收數(shù)據(jù)
    {
        len = ReceiveSize(m_TcpSock, (char*)PackageContext,pPackageHead->nDataLen);
    }
}
接收指定長(zhǎng)度的數(shù)據(jù)函數(shù)
// 接收指定長(zhǎng)度的數(shù)據(jù) int ReceiveSize(SOCKET m_hSocket, char* strData, int gLen){
    if(strData == NULL)
        return ERR_BADPARAM;
    char *p = strData;
    int len = gLen;
    int ret = 0;
    int returnlen = 0;
    while( len > 0)
    {
        ret = recv( m_hSocket, p+(iLen-len), iLen-returnlen, 0);
        if (ret == SOCKET_ERROR || ret == 0)
        {
            return ret;
        }
        len -= ret;
        returnlen += ret;
    }
    return returnlen;
}
這樣就可以達(dá)到解決 TCP 粘包的問(wèn)題。在實(shí)際使用中包頭還帶有更多的信息,而且包尾可能還會(huì)帶上分隔符,在 redis 、 FTP 中就是這樣處理的。
UDP不存在粘包
由于 UDP 不是面向 的,而且 UDP 是具有消息邊界的。也就是說(shuō) UDP 的發(fā)送的每一個(gè)數(shù)據(jù)包都是獨(dú)立的。所以 UDP 并不存在粘包的問(wèn)題。
原文來(lái)自: cnblogs
您還未登錄,請(qǐng)先登錄

熱門(mén)帖子

最新帖子

?