FUdpSender和FUdpReceiver提供了一种更加便捷的方式来进行UDP消息的收发。
UdpSender
class FUdpSocketSender
: public FRunnable
, private FSingleThreadRunnable
FUdpSender继承于FRunnale和FSiungleThreadRunnable。
每次要发送的数据被封装成了一个包,如下结构。
// Structure for outbound packets.
struct FPacket
{
/** Holds the packet's data. */
TSharedPtr<TArray<uint8>, ESPMode::ThreadSafe> Data;
/** Holds the recipient. */
FIPv4Endpoint Recipient;
/** Default constructor. */
FPacket() { }
/** Creates and initializes a new instance. */
FPacket(const TSharedRef<TArray<uint8>, ESPMode::ThreadSafe>& InData, const FIPv4Endpoint& InRecipient)
: Data(InData)
, Recipient(InRecipient)
{ }
};
因为涉及到多线程访问数据,所以使用TSharedRef和TSharedPtr的线程安全版本。另外使用 IPv4Endpoint记录了一个要发送的目标socket地址。
发送一个包:
/**
* Sends data to the specified recipient.
*
* @param Data The data to send.
* @param Recipient The recipient.
* @return true if the data will be sent, false otherwise.
*/
bool Send(const TSharedRef<TArray<uint8>, ESPMode::ThreadSafe>& Data, const FIPv4Endpoint& Recipient)
{
if (!Stopping && SendQueue.Enqueue(FPacket(Data, Recipient)))
{
WorkEvent->Trigger();
return true;
}
return false;
}
/**
* Creates and initializes a new socket sender.
*
* @param InSocket The UDP socket to use for sending data.
* @param ThreadDescription The thread description text (for debugging).
*/
FUdpSocketSender(FSocket* InSocket, const TCHAR* ThreadDescription)
: SendRate(0)
, Socket(InSocket)
, Stopping(false)
, WaitTime(FTimespan::FromMilliseconds(100))
{
check(Socket != nullptr);
check(Socket->GetSocketType() == SOCKTYPE_Datagram);
int32 NewSize = 0;
Socket->SetSendBufferSize(512 * 1024, NewSize);
WorkEvent = FPlatformProcess::GetSynchEventFromPool();
Thread = FRunnableThread::Create(this, ThreadDescription, 128 * 1024, TPri_AboveNormal, FPlatformAffinity::GetPoolThreadMask()); //创建线程
}
可以看见FUdpSender使用了多线程+消息队列来处理要发送的消息。注意接口使用的是const TSharedRef<TArray<uint8>
而不是uint8*
目的是为了保证线程安全。
UdpReciver
FUdpReveiver同样继承于FRunnale和FSiungleThreadRunnable。
class FUdpSocketReceiver
: public FRunnable
, private FSingleThreadRunnable
接受消息
/**
* Returns a delegate that is executed when data has been received.
*
* This delegate must be bound before the receiver thread is started with
* the Start() method. It cannot be unbound while the thread is running.
*
* @return The delegate.
*/
FOnSocketDataReceived& OnDataReceived()
{
check(Thread == nullptr);
return DataReceivedDelegate;
}
使用OnDataReceived返回一个可接受两个参数的单播委托。通过对这个委托进行绑定,可完成对接受数据的处理。
/**
* Delegate type for received data.
*
* The first parameter is the received data.
* The second parameter is sender's IP endpoint.
*/
DECLARE_DELEGATE_TwoParams(FOnSocketDataReceived, const FArrayReaderPtr&, const FIPv4Endpoint&);
第一个参数是收到到的数据,第二个参数是发送者的地址。其中FArrayReaderPtr
是一个typedef
/**
* Temporary fix for concurrency crashes. This whole class will be redesigned.
*/
typedef TSharedPtr<FArrayReader, ESPMode::ThreadSafe> FArrayReaderPtr;