最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

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

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

TCP粘包問題

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

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

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

TCP客戶端

TCP客戶端主動連接到TCP服務(wù)端,并接收TCP服務(wù)端發(fā)送的數(shù)據(jù),對接收的數(shù)據(jù)按照定義的公共包頭進(jìn)行分割組包,每當(dāng)組成一個完整數(shù)據(jù)包時,打印相關(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ò)包最大長度
//業(yè)務(wù)包頭
struct CommMsgHdr
{
? ? uint16_t uMsgType;
? ? uint32_t uTotalLen;
};
typedef struct _TcpHandle_{
? ? int32_t fd;
? ? uint32_t ? ? uRcvLen; ? ? ? ?//已接收數(shù)據(jù)大小
? ? uint32_t ? ? uAllLen; ? ? ? ?//消息總長度
? ? 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;//超時時長,單位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.開始接收頭部
? ? ? ? ? ? ? ? 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;// 重新指向新開辟的空間
? ? ? ? ? ? ? ? ? ? ? ? ? ? recv_buffer_max = ((CommMsgHdr *)recv_buffer)->uTotalLen;// 重新賦值最大buffer長度
? ? ? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? //2.開始接收數(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ù)端啟動監(jiān)聽,當(dāng)有客戶端接入時,向客戶端循環(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ò)包最大長度
//業(yè)務(wù)包頭
struct CommMsgHdr
{
? ? uint16_t uMsgType;
? ? uint32_t uTotalLen;
};
typedef struct _TcpHandle_{
? ? int32_t fd;
? ? uint32_t ? ? uRcvLen; ? ? ? ?//已接收數(shù)據(jù)大小
? ? uint32_t ? ? uAllLen; ? ? ? ?//消息總長度
? ? 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)
? ? ? ? {
? ? ? ? ? ? //開始發(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;
}

源碼測試

先啟動服務(wù)端

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

再啟動客戶端

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

客戶端打印

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粘包的問題實(shí)現(xiàn)的文章就介紹到這了,更多相關(guān)C++ TCP粘包內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • C++中的volatile關(guān)鍵字及其作用

    C++中的volatile關(guān)鍵字及其作用

    本文介紹了C++中的volatile關(guān)鍵字,它用于標(biāo)識變量可能被意外修改,以及編譯器不應(yīng)進(jìn)行優(yōu)化。本文通過具體的代碼示例,闡述了volatile關(guān)鍵字的作用和使用方法,幫助讀者更好地了解該關(guān)鍵字在C++語言中的應(yīng)用場景和實(shí)現(xiàn)原理
    2023-04-04
  • Qt Designer的簡單使用方法

    Qt Designer的簡單使用方法

    用 C++ 代碼編寫圖形界面的問題就是不直觀,因此 Qt 項(xiàng)目開發(fā)了專門的可視化圖形界面編輯器,本文就詳細(xì)的介紹一下
    2021-08-08
  • C語言代碼實(shí)現(xiàn)掃雷小游戲

    C語言代碼實(shí)現(xiàn)掃雷小游戲

    這篇文章主要為大家詳細(xì)介紹了C語言代碼實(shí)現(xiàn)掃雷小游戲,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2020-02-02
  • C++實(shí)現(xiàn)LeetCode(88.混合插入有序數(shù)組)

    C++實(shí)現(xiàn)LeetCode(88.混合插入有序數(shù)組)

    這篇文章主要介紹了C++實(shí)現(xiàn)LeetCode(88.混合插入有序數(shù)組),本篇文章通過簡要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下
    2021-07-07
  • C語言實(shí)現(xiàn)BMP圖像處理(彩色圖轉(zhuǎn)灰度圖)

    C語言實(shí)現(xiàn)BMP圖像處理(彩色圖轉(zhuǎn)灰度圖)

    這篇文章主要為大家詳細(xì)介紹了C語言實(shí)現(xiàn)BMP圖像處理,彩色圖轉(zhuǎn)灰度圖,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2021-10-10
  • OpenCV實(shí)現(xiàn)繪制輪廓外接矩形

    OpenCV實(shí)現(xiàn)繪制輪廓外接矩形

    這篇文章主要為大家詳細(xì)介紹了OpenCV實(shí)現(xiàn)繪制輪廓外接矩形的方法,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-12-12
  • Qt實(shí)現(xiàn)卡牌對對碰游戲(附demo)

    Qt實(shí)現(xiàn)卡牌對對碰游戲(附demo)

    本文主要介紹了Qt實(shí)現(xiàn)卡牌對對碰游戲,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-10-10
  • C++中對象的賦值與復(fù)制操作詳細(xì)解析

    C++中對象的賦值與復(fù)制操作詳細(xì)解析

    對象之間的賦值也是通過賦值運(yùn)算符“=”進(jìn)行的。本來賦值運(yùn)算符“=”只能用來對單個的變量賦值,現(xiàn)在被擴(kuò)展為兩個同類對象之間的賦值,這是通過對賦值運(yùn)算符的重載實(shí)現(xiàn)的
    2013-10-10
  • C語言數(shù)據(jù)結(jié)構(gòu)之動態(tài)分配實(shí)現(xiàn)串

    C語言數(shù)據(jù)結(jié)構(gòu)之動態(tài)分配實(shí)現(xiàn)串

    這篇文章主要介紹了C語言數(shù)據(jù)結(jié)構(gòu)之動態(tài)分配實(shí)現(xiàn)串的相關(guān)資料,希望通過本文能幫助到大家,讓大家實(shí)現(xiàn)數(shù)據(jù)結(jié)構(gòu)中動態(tài)分配實(shí)現(xiàn)串的實(shí)例,需要的朋友可以參考下
    2017-10-10
  • 一文帶你徹底搞定C++中文輸出亂碼的問題

    一文帶你徹底搞定C++中文輸出亂碼的問題

    這篇文章主要為大家詳細(xì)介紹了C++中文輸出亂碼的相關(guān)解決方法,文中的示例代碼講解詳細(xì),具有一定的借鑒價值,有需要的小伙伴可以了解下
    2025-10-10

最新評論

洞头县| 望江县| 广丰县| 玉环县| 兰考县| 家居| 扶余县| 罗江县| 昂仁县| 北宁市| 玉环县| 洮南市| 丘北县| 余江县| 罗定市| 安仁县| 丰城市| 镇巴县| 雅江县| 民乐县| 石屏县| 壶关县| 秦安县| 建昌县| 平泉县| 类乌齐县| 贡山| 白银市| 苍溪县| 瑞昌市| 岢岚县| 凤庆县| 吉水县| 周口市| 仪征市| 永清县| 闸北区| 乐山市| 中卫市| 奉化市| 留坝县|