tarotaroのエンジニア生活

技術ネタとか日々の仕事の話とか

C++を使ったSocketを使った通信周りの書き方

 この記事はUnreal Engine 4 (UE4) 其の弐 Advent Calendar 2015 の17日目の記事です。昨日は、@tempkinderさんの「Stationary Lightの影について」でした。影関係はモバイル等も混じってくる、出たり出なかったりするので、実装の違いが載っていてなぜでないのかなとか考える指針にもなるのでありがたい記事でしたね。

 今回は、前々回のぷちコン、前回のぷちコンで外部のデバイスと通信するために使ったSocket通信について解説したいと思います。UE4マルチプレイの実装については、以前、UE4Unreal Fest2015で@alweiさんが話していて大変勉強になるものでしたが、たとえば、PC側でゲーム画面を、iPadと通信して現在位置のMapを表示したいだとか、僕の場合のように最近はIotデバイス(Arduino等)が流行っているので、それらと通信したいとかいう時、必ずしも、マルチプレイの方法では実装できるとは限らないのでそのような場合にSocketを使った通信を実装しようというものです。

早速、解説していきたいと思います。詳細のソースに関しては、githubをみてもらうとして、要点だけ説明したいと思います。


 まず、SocketでつなぐBP用のメソッドを作ります。 

bool ABikeSensorCommunicationActor::connectBikeSensor(const FString &IP,const int32 &port){
    ConnectionSocket = CreateTCPConnection(IP, port,1024);
    if(!ConnectionSocket){
        return false;
    }
    

    if(TimerHandle.IsValid() == false){
        GetWorldTimerManager().SetTimer(TimerHandle, this,
                                        &ABikeSensorCommunicationActor::TCPSocketListener, 1.0f / 60.0f, true);
    }
    
    
    return true;
}

ここでの、SetTimerは、Socketをつないだ後、別スレッドで、通信スレッド(TCPSocketListener)を作っています。1/60秒で通信をチェックするように
します。


次に、それぞれのOSのSocketSystemを取得し、IP,Portなどを設定してConnectします。そして、FSocketのオブジェクトを作り返しています。

FSocket* ABikeSensorCommunicationActor::CreateTCPConnection(const FString& TheIP, const int32 ThePort,const int32 ReceiveBufferSize)
{
	FSocket* retSocket = NULL;

	ISocketSubsystem* SocketSubsystem = ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM);

	if (SocketSubsystem != NULL)
	{
		retSocket = SocketSubsystem->CreateSocket(NAME_Stream, "SensorConnect", true);
	}

	FIPv4Address ip;
	FIPv4Address::Parse(TheIP, ip);
	auto addr = SocketSubsystem->CreateInternetAddr();
	addr->SetIp(ip.GetValue());//setIP
	addr->SetPort(ThePort);

	if (!retSocket->Connect(*addr)){
		return NULL;
	}
	
   
    return retSocket;
}
ここで、Socketのオブジェクトが生成されれば、接続が成立します。

そして、実際にデータを送るスレッドのListenerです。
void ABikeSensorCommunicationActor::TCPSocketListener()
{
    //~~~~~~~~~~~~~
    if(!ConnectionSocket) return;
    //~~~~~~~~~~~~~
    
    
    int32 sendType = 1;
    int sent;
    ConnectionSocket->Send((uint8 *)&sendType,sizeof(sendType),sent);
    
    //Binary Array!
    uint32 Size = 0;
    
    while (ConnectionSocket->HasPendingData(Size))
    {
        TArray ReceivedData;
        ReceivedData.Init(FMath::Min(Size, 65507u));
        
        int32 Read = 0;
        ConnectionSocket->Recv(ReceivedData.GetData(), ReceivedData.Num(), Read);
        if(ReceivedData.Num() <= 0)
        {
        }else{
            const FString ReceivedUE4String = StringFromBinaryArray(ReceivedData);
            FormatReceiveDataToNumber(ReceivedUE4String,iBodyRate,iRotaion,fSpeed,iDirection);
        }
        
    }
    
}

ここで、データをSendしたり、データが送られてきているならデータが溜まってる分だけ受け取って、それぞれ処理をします。 

 以上、駆け足でSocketについて説明してきました、ちなみに今回説明したのはどちらかというと、Client側の処理なので、Peer To Peer で UE4 to UE4 で通信するときにはサーバ側のUE4の処理を書かなければいけませんけど、そのあたりはUE4 のComminityのほうのVictoryPluginの作者さんのページに解説があります。

最後まで読んでいただきありがとうございます。

最後にSocketを使ったサンプルのYoutube動画のリンクを貼っておきます。またソースに関してはgithubにあります。




明日は@Usui_KKYLさんによる「VictoryPluginの中身をざっくり紹介させていただきます。」です。VictoryPluginは自分も愛用していますが、かなり色々なことができるようになってて把握するのが大変なのでありがたいです、楽しみですね。