/** Initializes the message tunnel with the current settings. */ void InitializeTunnel() { ShutdownTunnel(); UUdpMessagingSettings* Settings = GetMutableDefault<UUdpMessagingSettings>(); bool ResaveSettings = false; FIPv4Endpoint UnicastEndpoint; FIPv4Endpoint MulticastEndpoint; if (!FIPv4Endpoint::Parse(Settings->TunnelUnicastEndpoint, UnicastEndpoint)) { GLog->Logf(TEXT("Warning: Invalid UDP Tunneling UnicastEndpoint '%s' - binding to all local network adapters instead"), *Settings->UnicastEndpoint); UnicastEndpoint = FIPv4Endpoint::Any; Settings->UnicastEndpoint = UnicastEndpoint.ToString(); ResaveSettings = true; } if (!FIPv4Endpoint::Parse(Settings->TunnelMulticastEndpoint, MulticastEndpoint)) { GLog->Logf(TEXT("Warning: Invalid UDP Tunneling MulticastEndpoint '%s' - using default endpoint '%s' instead"), *Settings->MulticastEndpoint, *UDP_MESSAGING_DEFAULT_MULTICAST_ENDPOINT.ToText().ToString()); MulticastEndpoint = UDP_MESSAGING_DEFAULT_MULTICAST_ENDPOINT; Settings->MulticastEndpoint = MulticastEndpoint.ToString(); ResaveSettings = true; } if (ResaveSettings) { Settings->SaveConfig(); } GLog->Logf(TEXT("UdpMessaging: Initializing tunnel on interface %s to multicast group %s."), *UnicastEndpoint.ToText().ToString(), *MulticastEndpoint.ToText().ToString()); MessageTunnel = MakeShareable(new FUdpMessageTunnel(UnicastEndpoint, MulticastEndpoint)); // initiate connections for (int32 EndpointIndex = 0; EndpointIndex < Settings->RemoteTunnelEndpoints.Num(); ++EndpointIndex) { FIPv4Endpoint RemoteEndpoint; if (FIPv4Endpoint::Parse(Settings->RemoteTunnelEndpoints[EndpointIndex], RemoteEndpoint)) { MessageTunnel->Connect(RemoteEndpoint); } else { GLog->Logf(TEXT("Warning: Invalid UDP RemoteTunnelEndpoint '%s' - skipping"), *Settings->RemoteTunnelEndpoints[EndpointIndex]); } } }
/* Provide a dummy echo service to echo received data back for development purpose */ bool UNetworkManager::StartEchoService(FSocket* ClientSocket, const FIPv4Endpoint& ClientEndpoint) { if (!this->ConnectionSocket) // Only maintain one active connection, So just reuse the TCPListener thread. { UE_LOG(LogUnrealCV, Warning, TEXT("New client connected from %s"), *ClientEndpoint.ToString()); // ClientSocket->SetNonBlocking(false); // When this in blocking state, I can not use this socket to send message back ConnectionSocket = ClientSocket; // Listening data here or start a new thread for data? // Reuse the TCP Listener thread for getting data, only support one connection uint32 BufferSize = 1024; int32 Read = 0; TArray<uint8> ReceivedData; ReceivedData.SetNumZeroed(BufferSize); while (1) { // Easier to use raw FSocket here, need to detect remote socket disconnection bool RecvStatus = ClientSocket->Recv(ReceivedData.GetData(), ReceivedData.Num(), Read); // if (!RecvStatus) // The connection is broken if (Read == 0) // RecvStatus == true if Read >= 0, this is used to determine client disconnection // -1 means no data, 0 means disconnected { ConnectionSocket = NULL; // Use this to determine whether client is connected return false; } int32 Sent; ClientSocket->Send(ReceivedData.GetData(), Read, Sent); // Echo the message back check(Read == Sent); } return true; } return false; }
/** Connected Handler */ bool UNetworkManager::Connected(FSocket* ClientSocket, const FIPv4Endpoint& ClientEndpoint) { bool ServiceStatus = false; BroadcastConnected(*ClientEndpoint.ToString()); // ServiceStatus = StartEchoService(ClientSocket, ClientEndpoint); ServiceStatus = StartMessageService(ClientSocket, ClientEndpoint); return ServiceStatus; // This is a blocking service, if need to support multiple connections, consider start a new thread here. }
/** * Start message service in listening thread * TODO: Start a new background thread to receive message */ bool UNetworkManager::StartMessageService(FSocket* ClientSocket, const FIPv4Endpoint& ClientEndpoint) { if (!this->ConnectionSocket) { ConnectionSocket = ClientSocket; UE_LOG(LogUnrealCV, Warning, TEXT("New client connected from %s"), *ClientEndpoint.ToString()); // ClientSocket->SetNonBlocking(false); // When this in blocking state, I can not use this socket to send message back FString Confirm = FString::Printf(TEXT("connected to %s"), *GetProjectName()); bool IsSent = this->SendMessage(Confirm); // Send a hello message if (!IsSent) { UE_LOG(LogUnrealCV, Error, TEXT("Failed to send welcome message to client.")); } // TODO: Start a new thread while (this->ConnectionSocket) // Listening thread, while the client is still connected { FArrayReader ArrayReader; bool unknown_error = false; if (!FSocketMessageHeader::ReceivePayload(ArrayReader, ConnectionSocket, &unknown_error)) // Wait forever until got a message, or return false when error happened { if (unknown_error) { BroadcastError(FString("ReceivePayload failed with unknown error")); } this->ConnectionSocket = NULL; return false; // false will release the ClientSocket break; // Remote socket disconnected } FString Message = StringFromBinaryArray(ArrayReader); BroadcastReceived(Message); // Fire raw message received event, use message id to connect request and response UE_LOG(LogUnrealCV, Warning, TEXT("Receive message %s"), *Message); } return false; // TODO: What is the meaning of return value? } else { // No response and let the client silently timeout UE_LOG(LogUnrealCV, Warning, TEXT("Only one client is allowed, can not allow new connection from %s"), *ClientEndpoint.ToString()); return false; // Already have a connection } }