欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

C++解決TCP粘包的問(wèn)題實(shí)現(xiàn)

 更新時(shí)間:2023年08月07日 09:37:05   作者:夏天匆匆2過(guò)  
本文主要介紹了C++解決TCP粘包的問(wèn)題實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧

TCP粘包問(wèn)題

TCP是面向連接的,面向流的可靠性傳輸。TCP會(huì)將多個(gè)間隔較小且數(shù)據(jù)量小的數(shù)據(jù),合并成一個(gè)大的數(shù)據(jù)塊,然后進(jìn)行封包發(fā)送,這樣一個(gè)數(shù)據(jù)包里就可能含有多個(gè)消息的數(shù)據(jù),面向流的通信是無(wú)消息保護(hù)邊界的,也就是TCP粘包。接收端需要自己完成數(shù)據(jù)的拆包和組包,解決粘包問(wèn)題。

要解決TCP粘包問(wèn)題,就要給TCP定義公共包頭,包頭一般包括消息類(lèi)型和消息大小,用包頭來(lái)分割每個(gè)數(shù)據(jù)包,做數(shù)據(jù)包的邊界。

下面分別用C++實(shí)現(xiàn)TCP客戶(hù)端和TCP服務(wù)端,使用qt測(cè)試。

TCP客戶(hù)端

TCP客戶(hù)端主動(dòng)連接到TCP服務(wù)端,并接收TCP服務(wù)端發(fā)送的數(shù)據(jù),對(duì)接收的數(shù)據(jù)按照定義的公共包頭進(jìn)行分割組包,每當(dāng)組成一個(gè)完整數(shù)據(jù)包時(shí),打印相關(guān)信息。

TcpClient.h

#ifndef TCPCLIENT_H
#define TCPCLIENT_H
#include <string.h>
#include <stdint.h>
#include <stdint.h>
#include <errno.h>
#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/epoll.h>
#include <fcntl.h>
#include <new>
#define MAX_PKT_SIZE ? ? ? ?(256<<20) ? //網(wǎng)絡(luò)包最大長(zhǎng)度
//業(yè)務(wù)包頭
struct CommMsgHdr
{
? ? uint16_t uMsgType;
? ? uint32_t uTotalLen;
};
typedef struct _TcpHandle_{
? ? int32_t fd;
? ? uint32_t ? ? uRcvLen; ? ? ? ?//已接收數(shù)據(jù)大小
? ? uint32_t ? ? uAllLen; ? ? ? ?//消息總長(zhǎng)度
? ? struct sockaddr_in local_addr;
? ? struct sockaddr_in remote_addr;
? ? _TcpHandle_()
? ? {
? ? ? ? uRcvLen = 0;
? ? ? ? uAllLen = 0;
? ? }
}TcpHandle;
class TcpClient
{
public:
? ? TcpClient();
? ? int32_t create_tcpClient(char *serverIp, int32_t serverPort);
? ? int32_t SendData(char *data, int32_t len);
? ? bool m_runing;
? ? int epoll_fd;
? ? TcpHandle* pTcpHandle;
private:
? ? pthread_t threadId;
};
#endif // TCPCLIENT_H

TcpClient.cpp

#include "TcpClient.h"
int32_t TcpRcv(const int32_t& fd, void* buff, const uint32_t& len)
{
? ? int32_t iCurrRecv = recv(fd, buff, len, MSG_NOSIGNAL);
? ? if (0 < iCurrRecv) {
? ? ? ? return iCurrRecv;
? ? } else if (iCurrRecv < 0) {
? ? ? ? if (errno == EINTR || errno == EWOULDBLOCK || errno == EAGAIN) {
? ? ? ? ? ? return 0;
? ? ? ? } else return -1;
? ? } else return -1;
}
void* DealTcpThread(void* obj)
{
? ? TcpClient* pTcpClient = (TcpClient*)obj;
? ? TcpHandle* pTcpHandle = pTcpClient->pTcpHandle;
? ? const int kEpollDefaultWait = 1;//超時(shí)時(shí)長(zhǎng),單位ms
? ? struct epoll_event alive_events[256];
? ? uint32_t recv_buffer_max = 1024 * 1024;
? ? uint8_t *recv_buffer = nullptr;
? ? recv_buffer = new uint8_t[recv_buffer_max];
? ? uint32_t head_len = (uint32_t)sizeof(CommMsgHdr);
? ? while (pTcpClient->m_runing)
? ? {
? ? ? ? int num = epoll_wait(pTcpClient->epoll_fd, alive_events, 256, kEpollDefaultWait);
? ? ? ? for (int i = 0; i < num; ++i)
? ? ? ? {
? ? ? ? ? ? int fd = alive_events[i].data.fd;
? ? ? ? ? ? int events = alive_events[i].events;
? ? ? ? ? ? if ( events & EPOLLIN )
? ? ? ? ? ? {
? ? ? ? ? ? ? ? //1.開(kāi)始接收頭部
? ? ? ? ? ? ? ? if(pTcpHandle->uRcvLen < head_len)
? ? ? ? ? ? ? ? {
? ? ? ? ? ? ? ? ? ? int32_t iRecvLen = TcpRcv(fd, recv_buffer + pTcpHandle->uRcvLen, head_len - pTcpHandle->uRcvLen);
? ? ? ? ? ? ? ? ? ? if (0 == iRecvLen) continue;
? ? ? ? ? ? ? ? ? ? else if (0 > iRecvLen) {
? ? ? ? ? ? ? ? ? ? ? ? printf("Recv head data, return [%d] and err[%s],fd=[%d].", iRecvLen, strerror(errno),fd);
? ? ? ? ? ? ? ? ? ? ? ? close(fd);//關(guān)閉socket
? ? ? ? ? ? ? ? ? ? ? ? continue;
? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? ? ? pTcpHandle->uRcvLen += iRecvLen;
? ? ? ? ? ? ? ? ? ? //如果已經(jīng)接收完整頭部
? ? ? ? ? ? ? ? ? ? if(pTcpHandle->uRcvLen >= head_len)
? ? ? ? ? ? ? ? ? ? {
? ? ? ? ? ? ? ? ? ? ? ? CommMsgHdr* pHdr = (CommMsgHdr *)recv_buffer;
? ? ? ? ? ? ? ? ? ? ? ? pTcpHandle->uAllLen = pHdr->uTotalLen;
? ? ? ? ? ? ? ? ? ? ? ? //如果報(bào)文頭里的uTotalLen太小或太大,異常處理
? ? ? ? ? ? ? ? ? ? ? ? if ( pHdr->uTotalLen < head_len || pHdr->uTotalLen > MAX_PKT_SIZE )
? ? ? ? ? ? ? ? ? ? ? ? {
? ? ? ? ? ? ? ? ? ? ? ? ? ? printf("uTotalLen invalid,uTotalLen=%u,fd=[%d]",
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?pHdr->uTotalLen,fd);
? ? ? ? ? ? ? ? ? ? ? ? ? ? close(fd);//關(guān)閉socket
? ? ? ? ? ? ? ? ? ? ? ? ? ? continue;
? ? ? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? ? ? ? ? //如果uTotalLen大于已分配的緩存,重新分配
? ? ? ? ? ? ? ? ? ? ? ? if (((CommMsgHdr *)recv_buffer)->uTotalLen > recv_buffer_max)
? ? ? ? ? ? ? ? ? ? ? ? {
? ? ? ? ? ? ? ? ? ? ? ? ? ? uint8_t *new_recv_buffer = new uint8_t[((CommMsgHdr *)recv_buffer)->uTotalLen];
? ? ? ? ? ? ? ? ? ? ? ? ? ? memcpy(new_recv_buffer, recv_buffer,head_len);
? ? ? ? ? ? ? ? ? ? ? ? ? ? delete [] recv_buffer;// 釋放原有空間
? ? ? ? ? ? ? ? ? ? ? ? ? ? recv_buffer = new_recv_buffer;// 重新指向新開(kāi)辟的空間
? ? ? ? ? ? ? ? ? ? ? ? ? ? recv_buffer_max = ((CommMsgHdr *)recv_buffer)->uTotalLen;// 重新賦值最大buffer長(zhǎng)度
? ? ? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? //2.開(kāi)始接收數(shù)據(jù)體
? ? ? ? ? ? ? ? else
? ? ? ? ? ? ? ? {
? ? ? ? ? ? ? ? ? ? int32_t iRecvLen = TcpRcv(fd, recv_buffer + pTcpHandle->uRcvLen, pTcpHandle->uAllLen - pTcpHandle->uRcvLen);
? ? ? ? ? ? ? ? ? ? if (0 == iRecvLen) continue;
? ? ? ? ? ? ? ? ? ? else if (0 > iRecvLen) {
? ? ? ? ? ? ? ? ? ? ? ? printf("Recv body data, return [%d] and err[%s],fd=[%d].", iRecvLen, strerror(errno),fd);
? ? ? ? ? ? ? ? ? ? ? ? close(fd);//關(guān)閉socket
? ? ? ? ? ? ? ? ? ? ? ? continue;
? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? ? ? pTcpHandle->uRcvLen += iRecvLen;
? ? ? ? ? ? ? ? ? ? //完成接收
? ? ? ? ? ? ? ? ? ? if(pTcpHandle->uRcvLen == pTcpHandle->uAllLen)
? ? ? ? ? ? ? ? ? ? {
? ? ? ? ? ? ? ? ? ? ? ? CommMsgHdr* pHdr = (CommMsgHdr*)recv_buffer;
? ? ? ? ? ? ? ? ? ? ? ? printf("Rcv completed,msgType=%d,uTotalLen=%u\n",pHdr->uMsgType,pHdr->uTotalLen);
? ? ? ? ? ? ? ? ? ? ? ? pTcpHandle->uRcvLen = 0;
? ? ? ? ? ? ? ? ? ? ? ? pTcpHandle->uAllLen = 0;
? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? }
? ? ? ? ? ? }
? ? ? ? }
? ? }
? ? delete [] recv_buffer;
? ? recv_buffer = nullptr;
? ? return nullptr;
}
TcpClient::TcpClient()
{
? ? pTcpHandle = new TcpHandle;
? ? epoll_fd = epoll_create(1);
}
int32_t TcpClient::create_tcpClient(char *serverIp, int32_t serverPort)
{
? ? if (pTcpHandle == NULL)?? ??? ?return -1;
? ? pTcpHandle->fd = -1;
? ? if((pTcpHandle->fd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
? ? {
? ? ? ? printf("socket err=%s\n",strerror(errno));
? ? ? ? return -2;
? ? }
? ? pTcpHandle->remote_addr.sin_family = AF_INET;
? ? pTcpHandle->remote_addr.sin_port = htons(serverPort);
? ? pTcpHandle->remote_addr.sin_addr.s_addr = inet_addr(serverIp);
? ? if(connect(pTcpHandle->fd, (struct sockaddr *)&pTcpHandle->remote_addr, sizeof(pTcpHandle->remote_addr)) < 0)
? ? {
? ? ? ? printf("connect err=%s\n",strerror(errno));
? ? ? ? return -3;
? ? }
? ? struct epoll_event evt;
? ? evt.events = EPOLLIN;
? ? fcntl(pTcpHandle->fd, F_SETFL, O_NONBLOCK);//設(shè)置非阻塞
? ? evt.data.fd = pTcpHandle->fd;
? ? epoll_ctl(epoll_fd,EPOLL_CTL_ADD,pTcpHandle->fd,&evt);
? ? m_runing = true;
? ? pthread_create(&threadId,NULL,DealTcpThread,this);
? ? return 0;
}
int32_t TcpClient::SendData(char *data, int32_t len)
{
? ? int32_t ret = send(pTcpHandle->fd, data, len, MSG_NOSIGNAL);
? ? return ret;
}

TCP服務(wù)端

服務(wù)端啟動(dòng)監(jiān)聽(tīng),當(dāng)有客戶(hù)端接入時(shí),向客戶(hù)端循環(huán)發(fā)送大小不相等的數(shù)據(jù)包。

TcpServer.h

#ifndef TCPSERVER_H
#define TCPSERVER_H
#include <string.h>
#include <stdint.h>
#include <stdint.h>
#include <errno.h>
#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/epoll.h>
#include <fcntl.h>
#include <new>
#define MAX_PKT_SIZE ? ? ? ?(256<<20) ? //網(wǎng)絡(luò)包最大長(zhǎng)度
//業(yè)務(wù)包頭
struct CommMsgHdr
{
? ? uint16_t uMsgType;
? ? uint32_t uTotalLen;
};
typedef struct _TcpHandle_{
? ? int32_t fd;
? ? uint32_t ? ? uRcvLen; ? ? ? ?//已接收數(shù)據(jù)大小
? ? uint32_t ? ? uAllLen; ? ? ? ?//消息總長(zhǎng)度
? ? struct sockaddr_in local_addr;
? ? struct sockaddr_in remote_addr;
? ? _TcpHandle_()
? ? {
? ? ? ? uRcvLen = 0;
? ? ? ? uAllLen = 0;
? ? }
}TcpHandle;
class TcpServer
{
public:
? ? TcpServer();
? ? int32_t create_tcpServer(int32_t listenPort);
? ? bool m_runing;
? ? int epoll_fd;
? ? TcpHandle* pTcpSerHandle;
private:
? ? pthread_t threadId;
};
#endif // TCPSERVER_H

TcpServer.cpp

#include "TcpServer.h"
int SendLoop(int32_t fd, uint8_t * buff, uint32_t len) {
? ? uint64_t total_send_bytes = 0;
? ? int64_t curr_send_len = 0;
? ? uint64_t left_bytes = len;
? ? while(total_send_bytes < len) {
? ? ? ? curr_send_len = send(fd, buff + total_send_bytes, left_bytes, MSG_NOSIGNAL);
? ? ? ? if(curr_send_len < 0) {
? ? ? ? ? ? if( errno == EINTR || errno == EAGAIN)
? ? ? ? ? ? ? ? continue;
? ? ? ? ? ? return -1;
? ? ? ? } else {
? ? ? ? ? ? total_send_bytes += curr_send_len;
? ? ? ? ? ? left_bytes -= curr_send_len;
? ? ? ? }
? ? }
? ? ?return 0;
}
void* DealTcpThread(void* obj)
{
? ? TcpServer* pTcpServer = (TcpServer*)obj;
? ? TcpHandle* pTcpSerHandle = (TcpHandle*)pTcpServer->pTcpSerHandle;
? ? socklen_t src_len = sizeof(struct sockaddr_in);
? ? while (pTcpServer->m_runing)
? ? {
? ? ? ? struct sockaddr_in src;
? ? ? ? memset(&src, 0, src_len);
? ? ? ? int connfd = accept(pTcpSerHandle->fd, (struct sockaddr*) &src, &src_len);
? ? ? ? if(connfd > -1)
? ? ? ? {
? ? ? ? ? ? //開(kāi)始發(fā)送
? ? ? ? ? ? for(int index=0;index<100;index++)
? ? ? ? ? ? {
? ? ? ? ? ? ? ? uint32_t dataLength = 1024*1024*16 + index*10;
? ? ? ? ? ? ? ? void *sendbuff = new char[dataLength];
? ? ? ? ? ? ? ? CommMsgHdr* pHead = (CommMsgHdr*)sendbuff;
? ? ? ? ? ? ? ? pHead->uMsgType = 1001;
? ? ? ? ? ? ? ? pHead->uTotalLen = dataLength;
? ? ? ? ? ? ? ? SendLoop(connfd,(uint8_t * )sendbuff,dataLength);
? ? ? ? ? ? }
? ? ? ? }
? ? }
? ? return nullptr;
}
TcpServer::TcpServer()
{
? ? pTcpSerHandle = new TcpHandle;
}
int32_t TcpServer::create_tcpServer(int32_t listenPort)
{
? ? pTcpSerHandle->fd = -1;
? ? pTcpSerHandle->local_addr.sin_family = AF_INET;
? ? pTcpSerHandle->local_addr.sin_port = htons(listenPort);
? ? pTcpSerHandle->local_addr.sin_addr.s_addr = INADDR_ANY;
? ? pTcpSerHandle->fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
? ? int opt = 1;
? ? setsockopt(pTcpSerHandle->fd,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));//復(fù)用端口
? ? if (bind(pTcpSerHandle->fd, (struct sockaddr*) &pTcpSerHandle->local_addr,sizeof(struct sockaddr_in)) < 0)
? ? {
? ? ? ? printf("http server bind error(%s)",strerror(errno));
? ? ? ? return -1;
? ? }
? ? listen(pTcpSerHandle->fd, 32);
? ? m_runing = true;
? ? pthread_create(&threadId,NULL,DealTcpThread,this);
? ? return 0;
}

源碼測(cè)試

先啟動(dòng)服務(wù)端

    TcpServer *pTcpServer;
    pTcpServer = new TcpServer;
    pTcpServer->create_tcpServer(9090);

再啟動(dòng)客戶(hù)端

    TcpClient* pTcpClient;
    pTcpClient = new TcpClient;
    pTcpClient->create_tcpClient("127.0.0.1",9090);

客戶(hù)端打印

Rcv completed,msgType=1001,uTotalLen=16777216
Rcv completed,msgType=1001,uTotalLen=16777226
Rcv completed,msgType=1001,uTotalLen=16777236
Rcv completed,msgType=1001,uTotalLen=16777246
Rcv completed,msgType=1001,uTotalLen=16777256
Rcv completed,msgType=1001,uTotalLen=16777266
Rcv completed,msgType=1001,uTotalLen=16777276
Rcv completed,msgType=1001,uTotalLen=16777286
...
...
...

到此這篇關(guān)于C++解決TCP粘包的問(wèn)題實(shí)現(xiàn)的文章就介紹到這了,更多相關(guān)C++ TCP粘包內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • C語(yǔ)言指針的長(zhǎng)度和類(lèi)型深入分析

    C語(yǔ)言指針的長(zhǎng)度和類(lèi)型深入分析

    這篇文章主要介紹了C語(yǔ)言指針的長(zhǎng)度和類(lèi)型,針對(duì)常見(jiàn)的各個(gè)類(lèi)型進(jìn)行了相對(duì)詳細(xì)的分析,需要的朋友可以參考下
    2014-09-09
  • 詳解C++編程中運(yùn)算符的使用

    詳解C++編程中運(yùn)算符的使用

    這篇文章主要介紹了詳解C++編程中運(yùn)算符的使用,是C++入門(mén)學(xué)習(xí)中的基礎(chǔ)知識(shí),需要的朋友可以參考下
    2015-09-09
  • C++ 實(shí)現(xiàn)高性能HTTP客戶(hù)端

    C++ 實(shí)現(xiàn)高性能HTTP客戶(hù)端

    HttpClient可以實(shí)現(xiàn)所有HTTP的方法,通過(guò)API傳輸接收HTTP消息。本文詳細(xì)講解了HttpClient,以及如何運(yùn)用C++實(shí)現(xiàn)HTTP客戶(hù)端,感興趣的朋友可以參考一下
    2021-08-08
  • QT使用QChart繪制柱狀圖

    QT使用QChart繪制柱狀圖

    在Qt中使用QChart類(lèi)可以快速繪制一個(gè)圖表出來(lái),比如折線(xiàn)圖、餅圖、柱狀圖等,本文就來(lái)為大家介紹一下如何利用QChart繪制簡(jiǎn)單的柱狀圖吧
    2024-11-11
  • C語(yǔ)言實(shí)現(xiàn)紙牌計(jì)算24點(diǎn)小游戲

    C語(yǔ)言實(shí)現(xiàn)紙牌計(jì)算24點(diǎn)小游戲

    這篇文章主要為大家詳細(xì)介紹了C語(yǔ)言實(shí)現(xiàn)紙牌計(jì)算24點(diǎn)小游戲,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2019-10-10
  • C語(yǔ)言題解字符串變形算法示例

    C語(yǔ)言題解字符串變形算法示例

    這篇文章主要為大家介紹了C語(yǔ)言題解字符串變形的方法示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-08-08
  • C++隱式轉(zhuǎn)換問(wèn)題分析及解決辦法

    C++隱式轉(zhuǎn)換問(wèn)題分析及解決辦法

    在本篇文章里小編給大家整理了關(guān)于C++隱式轉(zhuǎn)換問(wèn)題分析及解決辦法,有需要的朋友們可以學(xué)習(xí)下。
    2020-02-02
  • C語(yǔ)言 strftime 格式化顯示日期時(shí)間的實(shí)現(xiàn)

    C語(yǔ)言 strftime 格式化顯示日期時(shí)間的實(shí)現(xiàn)

    下面小編就為大家?guī)?lái)一篇C語(yǔ)言 strftime 格式化顯示日期時(shí)間的實(shí)現(xiàn)。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2016-12-12
  • C++ 成員變量的初始化順序問(wèn)題詳解

    C++ 成員變量的初始化順序問(wèn)題詳解

    這篇文章主要介紹了C++ 成員變量的初始化順序問(wèn)題詳解的相關(guān)資料,需要的朋友可以參考下
    2017-02-02
  • Visual?Studio2022配置ReSharper?C++?常用設(shè)置方法

    Visual?Studio2022配置ReSharper?C++?常用設(shè)置方法

    這篇文章主要介紹了Visual?Studio2022配置ReSharper?C++?常用設(shè)置,本文通過(guò)圖文并茂的形式給大家介紹的非常詳細(xì),文中介紹了卸載Resharper的方法及Resharper激活碼,感興趣的朋友參考下吧
    2024-01-01

最新評(píng)論