通俗粘包分包处理逻辑

网络发送频率和网络堵塞,就可能出现粘包情况。

1 发送端需要等发送缓冲区满才发送出去,造成粘包
2 接收方不及时接收缓冲区的包,造成多个包接收

如图(盗取的)

处理逻辑:

声明一个读取到数据的数据缓存变量,从收到第一个数据包开始步骤:

1.读出来的数据依次存放到该数据缓存变量,然后判断处理该数据缓存变量的内容:

2.只处理内容数据长度大于等于当前包体大小的情况(其他情况跳过继续步骤1),然后把该等于包体数据拿出来存放分发,剩余内容数据拷贝到数据缓存前面(拿出的数据,后面剩余继续补上,如缓存123456789,拿出来的12345,剩下放到前面如6789XXXXX)。然后继续步骤1,循环读取处理。

UE4具体代码描述实现如下:

MessageBase.h:

#define MESSAGE_MAX_LENGTH 10240

class FMessageBase//消息结构,后面自定义消息结构继承该类
{
public:
    FMessageBase():HeadKey(0)
    {
        Length = sizeof(this);
    }
    ~FMessageBase()
    {}
    int32 Length;//包体大小,消息长度
   
};

 

struct RecvBufferData
{
    RecvBufferData()
        :index(0)
    {
        Bufferdata = (uint8*)malloc(MESSAGE_MAX_LENGTH * 10);
    }

    ~RecvBufferData()
    {
        free(Bufferdata);
    }

    bool ReadBufferData(FSocket* Socket);//从Socket->Recv读取数据

    template<typename T>
    T* GetMessage();//针对数据缓存区处理,拿到所需包的数据,剩余的数据继续放到Bufferdata最前面地址开始
     
    int OffsetIndex;//记录Bufferdata的最终数据位置偏移,换句话就是缓存数据大小
    uint8* Bufferdata;//读取消息数据缓存区,最后对这个判断处理。
};

MessageBase.cpp:

bool RecvBufferData::ReadBufferData(FSocket* Socket)
{
    int32 BytesRead = 0;//实际这次所能读到的数据量
    bool RecvRet = Socket->Recv(Bufferdata + OffsetIndex, MESSAGE_MAX_LENGTH, BytesRead);
    if(RecvRet)
    {
        OffsetIndex += BytesRead;//记录当前数据位置偏移
    }
    return RecvRet;
}

template<typename T>//T 就是FMessageBase
T* RecvBufferData::GetMessage()
{
    auto package_size = sizeof(FMessageBase);
    if (OffsetIndex < package_size)//小于包体跳过返回不处理
        return nullptr;
     
    FMessageBase * package = reinterpret_cast<FMessageBase *>(Bufferdata);
    if (OffsetIndex < package->Length)//小于包体跳过返回不处理
        return nullptr;

    T* Ret = (T*)malloc(package->Length);//声明包大小的消息结构来存放数据
    memcpy(Ret, Bufferdata, package->Length);//从Bufferdata拿出等于package->Length的数据放到Ret
    OffsetIndex = OffsetIndex - package->Length;//由于Bufferdata拿了package->Length数据,计算剩余数据大小

///memmove能够保证源串在被覆盖之前将重叠区域的字节拷贝到目标区域中,但复制后源内容会被更改

//把Bufferdata拿出的数据后,剩余数据起始地址Bufferdata + package->Length,然后把剩余数据拷贝到Bufferdata最前面来,

//为后面读取到数据继续可以依次放到Bufferdata后面
    memmove(Bufferdata, Bufferdata + package->Length, OffsetIndex);


    return Ret;//返回当前取到的数据包
}

 

NetActor.h:

class HIGHSPEEDNETDRIVER_API ANetActor: public AActor
{

public:    

RecvBufferData* ClientRecvBufferData;
 virtual void Tick( float DeltaSeconds ) override;

virtual void BeginPlay() override;

virtual void Destroyed();

FSocket*    ClientSocket;/

virtual bool ReceiveData();

virtual bool InitClient();

}

NetActor.cpp:

ANetActor::ANetActor(const FObjectInitializer& ObjectInitializer)
    : Super(ObjectInitializer)
{

ClientRecvBufferData = new RecvBufferData();

}

void ANetActor::BeginPlay()
{
    Super::BeginPlay();

   InitClient();

}

bool ANetActor::InitClient()
{
    if (GetNetMode() == NM_ClientServerPort!=0 && ServerIP != 0)
    {

        FString ServerIPAddr = "";
        UNetDriver* Driver = GetWorld()->GetNetDriver();
        if ( Driver&&Driver->ServerConnection)
        {
   
                Driver->ServerConnection->GetAddrAsInt(),
                *(Driver->ServerConnection->LowLevelDescribe()),
                *(Driver->ServerConnection->LowLevelGetRemoteAddress()));
            {
                ServerIPAddr = Driver->ServerConnection->LowLevelGetRemoteAddress();
            }
        }
        FSocket* Socket = ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM)->CreateSocket(NAME_Stream, TEXT("default"), false);
        if (!Socket)
        {
     
            return false;
        }

        TSharedRef<FInternetAddr> InternetAddr = ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM)->CreateInternetAddr();

        if (!ServerIPAddr.IsEmpty())
        {
            InternetAddr->SetIp(*ServerIPAddr, bValid);
        }
        else
        {
            InternetAddr->SetIp(ServerIP);
        }
        InternetAddr->SetPort(ServerPort);

    
        int32 ReceiveBufferSize = 2 * 1024 * 1024;
        int32 SendBufferSize = 2 * 1024 * 1024;
        int32 NewSize = 0;
        Socket->SetReceiveBufferSize(ReceiveBufferSize, NewSize);
        Socket->SetSendBufferSize(SendBufferSize, NewSize);

        bool Connected = Socket->Connect(*InternetAddr);
        if (!Connected)
        {
            if (Socket)
            {
                Socket->Close();
                ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM)->DestroySocket(Socket);
            }
         
            return false;
        }
        if (ClientSocket)
        {
            ClientSocket->Close();
            ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM)->DestroySocket(ClientSocket);
        }
        ClientSocket = Socket; 

        bClientIsReady = true;
    }
    return bClientIsReady;
}
 

 

void ANetActor::Destroyed()
{
    Super::Destroyed();

  if (nullptr != ClientRecvBufferData)
    {
        delete ClientRecvBufferData;
        ClientRecvBufferData = nullptr;
    }

}

void ANetActor::Tick( float DeltaTime )
{
    Super::Tick( DeltaTime );

if (GetNetMode() == NM_Client)

ReceiveData();

}

bool ANetActor::ReceiveData()
{
    if (ClientSocket)
    {

        uint32 DatagramSize = 0;
        if(ClientSocket->HasPendingData(DatagramSize))
        {
            if (ClientRecvBufferData->ReadBufferData(ClientSocket))//开始读取数据到数据缓存
            {
#if PLATFORM_WINDOWS && !WITH_EDITOR
                // canot dump my game!!!
                //__try//try
                try
                {
#endif
                    FMessageBase* Message = ClientRecvBufferData->GetMessage<FMessageBase>();//获取拿到所需数据包
                    while (Message)
                    {
                        ReceiveMessage(Message);//消息分发应用层
                        free(Message);//对应T* Ret = (T*)malloc(package->Length);//声明包大小的消息结构来存放数据,释放
                        Message = ClientRecvBufferData->GetMessage<FMessageBase>();//没有拿到在拿一次,稳定处理
                    }
#if PLATFORM_WINDOWS && !WITH_EDITOR
                }
                //__except (EXCEPTION_EXECUTE_HANDLER)//catch (std::exception& e)
                catch(std::exception& e)
               {
                    printf("%s", e.what());
                    BufferMessage.Remove(ClientSocket);
               }
#endif
            }
        }
    }
    return true;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值