/*********** Ping Pong handling ***********/

        /// <summary>
        /// Receives the ping messages and sends pong messages.
        /// </summary>
        /// <param name="result">The ping ping async result.</param>
        public void PingPongHandler(IAsyncResult result)
        {
            PingPongAsyncResult state = (PingPongAsyncResult)result.AsyncState;
            Socket remoteSocket       = state.RemoteSocket;

            try
            {
                _timeoutTimer.Stop();
                int bytesRead = remoteSocket.EndReceive(result);
                if (bytesRead > 0)
                {
                    byte[] pingPacketBuffer = new byte[bytesRead];
                    Buffer.BlockCopy(state.Buffer, 0, pingPacketBuffer, 0, pingPacketBuffer.Length);

                    remoteSocket.Send(StatusPacket, StatusPacket.Length, SocketFlags.None);

                    _timeoutTimer.Start();
                    remoteSocket.BeginReceive(state.Buffer, 0, state.Buffer.Length, SocketFlags.None, PingPongHandler,
                                              state);
                }
                else
                {
                    Thread.Sleep(250);
                    CloseConnection();
                    OnPs4Disconnected?.Invoke(this, "PS4 disconnected. Ping Pong socket got closed.");
                }
            }
            catch (SocketException)
            {
                if (remoteSocket != null)
                {
                    CloseConnection();
                    OnPs4Disconnected?.Invoke(this, "PS4 disconnected. Ping Pong socket exception.");
                }
            }
            catch (ObjectDisposedException) // When the socket gets closed from outside (session already removed)
            {
            }
            catch (Exception e)
            {
                OnPs4Disconnected?.Invoke(this, "PS4 disconnected. Unknown reason: " + e);
            }
        }
        /*********** Control request ***********/

        private void HandleControlRequest(string rpNonce, IPEndPoint ps4Endpoint, PS4RemotePlayData ps4RemotePlayData)
        {
            bool   connectedSuccess = false;
            Socket socket           = null;

            try
            {
                IPEndPoint ipEndPoint = new IPEndPoint(ps4Endpoint.Address, ControlPort);
                socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                socket.Connect(ipEndPoint);

                byte[] rpKeyBuffer    = HexUtil.Unhexlify(ps4RemotePlayData.RemotePlay.RpKey);
                byte[] rpNonceDecoded = Convert.FromBase64String(rpNonce);
                OnPs4LogInfo?.Invoke(this,
                                     "RP-Nonce from \"/sce/rp/session\" response: " + HexUtil.Hexlify(rpNonceDecoded));

                Session session = CryptoService.GetSessionForControl(rpKeyBuffer, rpNonceDecoded);

                string controlAesKey = HexUtil.Hexlify(CryptoService.GetSessionAesKeyForControl(rpKeyBuffer, rpNonceDecoded));
                string controlNonce  = HexUtil.Hexlify(CryptoService.GetSessionNonceValueForControl(rpNonceDecoded));
                OnPs4LogInfo?.Invoke(this, "!!! Control AES Key: " + controlAesKey);
                OnPs4LogInfo?.Invoke(this, "!!! Control AES Nonce: " + controlNonce + Environment.NewLine);

                byte[] registrationKeyBuffer    = HexUtil.Unhexlify(ps4RemotePlayData.RemotePlay.RegistrationKey);
                byte[] registrationKeyPadding   = { 0, 0, 0, 0, 0, 0, 0, 0 };
                byte[] encryptedRegistrationKey =
                    session.Encrypt(ByteUtil.ConcatenateArrays(registrationKeyBuffer, registrationKeyPadding));
                string encodedRegistrationKey = Convert.ToBase64String(encryptedRegistrationKey);

                byte[] randomDid    = Guid.NewGuid().ToByteArray();
                byte[] didPrefix    = { 0x00, 0x18, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x80 };
                byte[] didPadding   = { 48, 48, 48, 48, 48, 48, 48 };
                byte[] encryptedDid = session.Encrypt(ByteUtil.ConcatenateArrays(didPrefix, randomDid, didPadding));
                string encodedDid   = Convert.ToBase64String(encryptedDid);

                string osType          = "Win10.0.0";
                byte[] osTypeBuffer    = Encoding.UTF8.GetBytes(osType);
                byte[] osTypePadding   = { 0 };
                byte[] encryptedOsType = session.Encrypt(ByteUtil.ConcatenateArrays(osTypeBuffer, osTypePadding));
                string encodedOsType   = Convert.ToBase64String(encryptedOsType);

                string host = ps4Endpoint.Address + ":" + ControlPort;

                string requestData = "GET /sce/rp/session/ctrl HTTP/1.1\r\n" +
                                     $"HOST: {host}\r\n" +
                                     "User-Agent: remoteplay Windows\r\n" +
                                     "Connection: keep-alive\r\n" +
                                     "Content-Length: 0\r\n" +
                                     $"RP-Auth: {encodedRegistrationKey}\r\n" +
                                     "RP-Version: 8.0\r\n" +
                                     $"RP-Did: {encodedDid}\r\n" +
                                     "RP-ControllerType: 3\r\n" +
                                     "RP-ClientType: 11\r\n" +
                                     $"RP-OSType: {encodedOsType}\r\n" +
                                     "RP-ConPath: 1\r\n" +
                                     "\r\n";

                socket.Send(Encoding.UTF8.GetBytes(requestData));
                byte[] receiveBuffer = new byte[8192];
                int    readBytes     = socket.Receive(receiveBuffer);
                byte[] response      = new byte[readBytes];
                Buffer.BlockCopy(receiveBuffer, 0, response, 0, response.Length);
                string httpResponse = Encoding.ASCII.GetString(receiveBuffer, 0, readBytes);

                HttpStatusCode statusCode = HttpUtils.GetStatusCode(httpResponse);
                if (statusCode == HttpStatusCode.OK)
                {
                    OnPs4LogInfo?.Invoke(this, "\"/sce/rp/session/ctrl\" response: " + Environment.NewLine + httpResponse.Trim() + Environment.NewLine);
                    OnPs4LogInfo?.Invoke(this, "TCP connection to PS4 established" + Environment.NewLine);
                    _clientSocket = socket;
                    _clientSocket.ReceiveTimeout = 0;
                    PingPongAsyncResult connectionStateObject = new PingPongAsyncResult {
                        RemoteSocket = _clientSocket
                    };
                    connectionStateObject.RemoteSocket.BeginReceive(connectionStateObject.Buffer, 0,
                                                                    connectionStateObject.Buffer.Length, SocketFlags.None, PingPongHandler,
                                                                    connectionStateObject);
                    OnPs4ConnectionSuccess?.Invoke(this, EventArgs.Empty);
                    connectedSuccess = true;
                    InitializeRemotePlayChannel(session, ps4Endpoint);
                }
            }
            catch (Exception e)
            {
                OnPs4ConnectionError?.Invoke(this, "Exception occured /sce/rp/session/ctrl" + e);
            }
            finally
            {
                if (!connectedSuccess)
                {
                    socket?.Close();
                }
            }
        }