UDP内容概述
Internet 协议集支持一个无连接的传输协议,该协议称为用户数据报协议(UDP,User Datagram Protocol)。UDP 为应用程序提供了一种无需建立连接就可以发送封装的 IP 数据包的方法。
它的实现原理是:“发短信”,为什么说发短信呢?
发短信代表他不连接,也不稳定,我不管你接不接受,我就发你;
如果你对这方面的基础信息不是很懂可以看下这篇学UE4网络你需要懂的基础
文章的素材来自jiesengmianfei.cn
ue4UDP和C++UDP之间的区别
ue4的UDP协议和C++的UDP协议并没有多大的区别,区别只是ue4进行多次的封装,本质还是同一个东西。
写法上的区别 | ue4需要在cs文件中加入sockets和networking模块,然后在需要设置网络模型的文件的头文件中写入需要用到的网络头文件进入对应的文件,在下面的例子中会对对应的文件进行解析和备注,可进行观看 |
---|---|
用法上的区别 | ue4需要在c++项目中的制作调用和正常C++项目没区别,只是多了一个可以蓝图封装调用的过程 |
这是C++的UDP使用方式
udp接收端
#include "pch.h"
#include<WINSOCK2.H>
#include<iostream>
#pragma comment(lib,"WS2_32.lib")
using namespace std;
int main()
{
WSADATA wsaData;//初始化
SOCKET RecvSocket;
sockaddr_in RecvAddr;//服务器地址
int Port = 8888;//服务器监听地址
char RecvBuf[1024];//发送数据的缓冲区
int BufLen = 1024;//缓冲区大小
sockaddr_in SenderAddr;
int SenderAddrSize = sizeof(SenderAddr);
//初始化Socket
WSAStartup(MAKEWORD(2, 2), &wsaData);
//创建接收数据报的socket
RecvSocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
//将socket与制定端口和0.0.0.0绑定
RecvAddr.sin_family = AF_INET;
RecvAddr.sin_port = htons(Port);
RecvAddr.sin_addr.s_addr = htonl(INADDR_ANY);
bind(RecvSocket, (SOCKADDR *)&RecvAddr, sizeof(RecvAddr));
//调用Recvfrom函数在绑定的socket上接收数据
printf("等待数据返回");
recvfrom(RecvSocket, RecvBuf, BufLen, 0, (SOCKADDR *)&SenderAddr, &SenderAddrSize);
int charlength = strlen(RecvBuf);
for (int i = 0; i < charlength; i++)
{
char s = RecvBuf[i];
cout<<s;
}
//关闭socket,结束接收数据
printf("接收到数据,关闭socket通道");
closesocket(RecvSocket);
//释放资源,退出
printf("Exiting.");
WSACleanup();
getchar();
return 0;
}
UDP发送端
#include "pch.h"
#include<WINSOCK2.H>
#include<iostream>
#pragma comment(lib,"WS2_32.lib")
using namespace std;
int main()
{
WSADATA wsaData;//初始化
SOCKET SendSocket;
sockaddr_in RecvAddr;//服务器地址
int Port = 4000;//服务器监听地址
char SendBuf[1024];//发送数据的缓冲区
int BufLen = 1024;//缓冲区大小
//初始化Socket
WSAStartup(MAKEWORD(2, 2), &wsaData);
//创建Socket对象
SendSocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
//设置服务器地址
RecvAddr.sin_family = AF_INET;
RecvAddr.sin_port = htons(Port);
RecvAddr.sin_addr.s_addr = inet_addr("192.168.137.1");
//向服务器发送数据报
printf("Sending a datagram to the receiver...");
sendto(SendSocket, SendBuf, BufLen, 0, (SOCKADDR *)&RecvAddr, sizeof(RecvAddr));
//发送完成,关闭Socket
printf("finished sending,close socket.");
closesocket(SendSocket);
printf("Exting.");
WSACleanup();
return 0;
}
ue4中的UDP操作:
1.先在项目的build.cs文件中加入模块组(build.cs是为UE4中的UBT(unrealBuildTool虚幻构建工具)描述环境依赖信息)
在模块名称列表包括模块public的头文件,不需要使用导入或者进行链接(如纯C++中使用的#pragma comment(lib,“WS2_32.lib”)一个道理)
代码在这
,"Sockets","Networking"
后进入ue4创建一个BlueprintFunctionLibrary类(而蓝图函数库类的作用:1.储存函数的库,可用于在不同的蓝图中实现对同一个函数的调用 2.创建时无需选择类,哪里都能用)
来自官网的一个大佬的代码
MyBlueprintFunctionLibrary.h
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "Kismet/BlueprintFunctionLibrary.h"
#include "Runtime/Networking/Public/Networking.h"
#include "Engine.h"
#include "MyBlueprintFunctionLibrary.generated.h"
/**
*
*/
UCLASS()
class UDPRECV_API UMyBlueprintFunctionLibrary : public UBlueprintFunctionLibrary
{
GENERATED_BODY()
public:
UFUNCTION(BlueprintCallable, Category = "UDP")
//新建初始化Receiver函数
static void StartUDPReceiver(const FString& YourChosenSocketName, const FString& TheIP, const int32 ThePort, bool& success);
UFUNCTION(BlueprintCallable, Category = "UDP")
static bool StartUDPSender(const FString& YourChosenSocketName, const FString& TheIP, const int32 ThePort);
UFUNCTION(BlueprintCallable, Category = "UDP")
static void CloseUDPReceiver();
UFUNCTION(BlueprintCallable, Category = "UDP")
static void CloseUDPSender();
UFUNCTION(BlueprintCallable, Category = "UDP")
static bool RamaUDPSender_SendString(FString ToSend);
UFUNCTION(BlueprintPure, Category = "UDP")
//DataRecv返回函数
static void DataRecv(FString& str, bool& success);
};
来自官网的一个大佬的代码
MyBlueprintFunctionLibrary.cpp
// Fill out your copyright notice in the Description page of Project Settings.
#include "MyBlueprintFunctionLibrary.h"
TSharedPtr<FInternetAddr> RemoteAddr;
FSocket* SenderSocket;
FSocket* ListenSocket;
FUdpSocketReceiver* UDPReceiver = nullptr;
+
//ScreenMsg
void ScreenMsg(const FString& Msg)
{
GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Red, *Msg);
}
void ScreenMsg(const FString& Msg, const float Value)
{
GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Red, FString::Printf(TEXT("%s %f"), *Msg, Value));
}
void ScreenMsg(const FString& Msg, const FString& Msg2)
{
GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Red, FString::Printf(TEXT("%s %s"), *Msg, *Msg2));
}
void UMyBlueprintFunctionLibrary::CloseUDPSender()
{
if (SenderSocket) //Clear all sockets!
{
SenderSocket->Close();
ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM)->DestroySocket(SenderSocket);
}
}
bool UMyBlueprintFunctionLibrary::RamaUDPSender_SendString(FString ToSend) //发送消息处理
{
if (!SenderSocket)
{
ScreenMsg("No sender socket");
return false;
}
//~~~~~~~~~~~~~~~~
//发送消息
int32 BytesSent = 0;
FString serialized = ToSend;
TCHAR *serializedChar = serialized.GetCharArray().GetData();
int32 size = FCString::Strlen(serializedChar);
int32 sent = 0;
//SenderSocket->SendTo(Writer.GetData(), Writer.Num(), BytesSent, *RemoteAddr);
SenderSocket->SendTo((uint8*)TCHAR_TO_UTF8(serializedChar), size, BytesSent, *RemoteAddr);//发送给远端地址
if (BytesSent <= 0)
{
const FString Str = "Socket is valid but the receiver received 0 bytes, make sure it is listening properly!";
UE_LOG(LogTemp, Error, TEXT("%s"), *Str);
ScreenMsg(Str);
return false;
}
ScreenMsg("UDP Send Succcess! INFO Sent = ", ToSend);
return true;
}
void UMyBlueprintFunctionLibrary::DataRecv(FString & str, bool & success)
{
if (!ListenSocket)
{
ScreenMsg("No sender socket");
success = false;
//return success;
}
TSharedRef<FInternetAddr> targetAddr = ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM)->CreateInternetAddr();
TArray<uint8> ReceivedData;//定义一个接收器
uint32 Size;
if (ListenSocket->HasPendingData(Size))
{
success = true;
str = "";
uint8 *Recv = new uint8[Size];
int32 BytesRead = 0;
ReceivedData.SetNumUninitialized(FMath::Min(Size, 65507u));
ListenSocket->RecvFrom(ReceivedData.GetData(), ReceivedData.Num(), BytesRead, *targetAddr);//创建远程接收地址
char ansiiData[1024];
memcpy(ansiiData, ReceivedData.GetData(), BytesRead);//拷贝数据到接收器
ansiiData[BytesRead] = 0; //判断数据结束
FString debugData = ANSI_TO_TCHAR(ansiiData); //字符串转换
str = debugData;
// memset(ansiiData,0,1024);//清空
}
else
{
success = false;
}
}
bool UMyBlueprintFunctionLibrary::StartUDPSender(const FString & YourChosenSocketName, const FString & TheIP, const int32 ThePort)///初始化远端IP 发送信息前
{
//FIPv4Endpoint Endpoint(FIPv4Address::Any, 6789);
//Create Remote Address.
RemoteAddr = ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM)->CreateInternetAddr();
bool bIsValid;
RemoteAddr->SetIp(*TheIP, bIsValid);
RemoteAddr->SetPort(ThePort);
if (!bIsValid)
{
ScreenMsg("Rama UDP Sender>> IP address was not valid!", TheIP);
return false;
}
SenderSocket = FUdpSocketBuilder(*YourChosenSocketName)
.AsReusable()
.WithBroadcast() // 广播
.WithSendBufferSize(2 * 1024 * 1024)
//.BoundToEndpoint(Endpoint)
;
//check(SenderSocket->GetSocketType() == SOCKTYPE_Datagram);
//Set Send Buffer Size
int32 SendSize = 2 * 1024 * 1024;
SenderSocket->SetSendBufferSize(SendSize, SendSize);
SenderSocket->SetReceiveBufferSize(SendSize, SendSize);
if (bIsValid)
{
bIsValid = true;
}
return bIsValid;
}
void UMyBlueprintFunctionLibrary::CloseUDPReceiver()
{
delete UDPReceiver;
UDPReceiver = nullptr;
//Clear all sockets!
// makes sure repeat plays in Editor dont hold on to old sockets!
if (ListenSocket)
{
ListenSocket->Close();
ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM)->DestroySocket(ListenSocket);
}
}
void UMyBlueprintFunctionLibrary::StartUDPReceiver(const FString& YourChosenSocketName, const FString& TheIP, const int32 ThePort, bool& success)
{
TSharedRef<FInternetAddr> targetAddr = ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM)->CreateInternetAddr();
FIPv4Address Addr;
FIPv4Address::Parse(TheIP, Addr);
//Create Socket
FIPv4Endpoint Endpoint(FIPv4Address::Any, ThePort); //所有ip地址本地
//FIPv4Endpoint Endpoint(Addr, ThePort); //指定ip地址
ListenSocket = FUdpSocketBuilder(*YourChosenSocketName)
.AsNonBlocking()
.AsReusable()
.BoundToEndpoint(Endpoint)
.WithReceiveBufferSize(2 * 1024 * 1024)
;
//BUFFER SIZE
int32 BufferSize = 2 * 1024 * 1024;
ListenSocket->SetSendBufferSize(BufferSize, BufferSize);
ListenSocket->SetReceiveBufferSize(BufferSize, BufferSize);
if (!ListenSocket)
{
ScreenMsg("No socket");
success = false;
}
if (ListenSocket)
{
ScreenMsg("The receiver is initialized");
success = true;
}
//return true;
}
其中内容是创建一个socket接收和发送和一个获取数据判断,只是调用了UE4封装的
#include “Runtime/Networking/Public/Networking.h”
可以用ue4和纯C++的UDP互相传送信息
文章的素材来自jiesengmianfei.cn
这样就可以实现两者相互发送接收信息,而有的时候他发送或接收不到信息,可以看下是否是被其他进程抢先开启端口或者忘记closesocket导致的,或者是因为你的缓冲区域太小导致出的问题