Потоковая передача некоторых данных из PHP на C ++

Моя цель — отправить из приложения C ++ простой запрос GET в PHP, такой как «do re mi fa sol», и получить некоторые данные, которые я могу воспроизвести.

Скрипт PHP вернет поток байтов, который я буду интерпретировать в клиенте C ++.
Я прочитал один байт, чтобы узнать природу следующего сегмента (текстовый или WAV-аудиофайл).
Я прочитал 2 байта для размера следующего сегмента.
Я читаю данные сегмента и занимаюсь им соответственно.

Я не эксперт ни в C ++, ни в PHP, но мне удалось, используя библиотеку boost.
Он отлично работает, пока данные, которые я отправляю, невелики.
Когда данные достигают определенного размера, они перепутаны: я получаю недопустимый байт для характера сегмента (первый, который я прочитал).

Я думаю, что предел, после которого он испортился, составляет 8192 байт, и я предполагаю, что он просто записывает обратно в начало буфера?

Я попробовал несколько вещей:
— изменение размера буфера, в котором я читаю сокет.
— заставить мой скрипт PHP спать, но клиент c ++ начинает ждать завершения запроса, поэтому у меня просто задержка, прежде чем я получу ту же ошибку.
— попробуйте выполнить сброс в моем PHP-скрипте, и ob-flush … он не работает лучше, и он использует эти методы, добавляя несколько байтов к выходным данным.

Вот мой код:
C ++

    std::string host = "192.168.1.232";
std::string port = "80";
std::string path = "/praat/play.php";
int httpVersion = 11;

boost::asio::io_context ioc;
boost::asio::ip::tcp::resolver resolver{ ioc };
boost::asio::ip::tcp::socket socket{ ioc };

auto const results = resolver.resolve( host, port );

boost::asio::connect( socket, results.begin(), results.end() );

boost::beast::http::request<boost::beast::http::string_body> req{ boost::beast::http::verb::get, path, httpVersion };
req.set( boost::beast::http::field::host, host );
req.set( boost::beast::http::field::user_agent, BOOST_BEAST_VERSION_STRING );

boost::beast::http::write( socket, req );


OutputDebugString( L"████ Let's read the request:\n" );

std::array< byte, 32 > buf;
bool parsingHeader = true;
std::string header = "";
std::deque< byte > data;
unsigned long cumul = 0;

while ( true ) {

boost::system::error_code error;

int len = socket.read_some( boost::asio::buffer( buf ), error );

cumul += len;

OutputDebugString((L"████ Error value: " + std::to_wstring(error.value()) + L"\n").c_str());

OutputDebugString( ( L"████ Bytes received: " + std::to_wstring( len ) + L" for a total of " + std::to_wstring( cumul ) + L"\n" ).c_str() );

if ( error == boost::asio::error::eof ) {

OutputDebugString(L"████ End of file\n");
break;
}

std::string s = (char *)buf.data();
s = s.substr( 0, len );

if ( parsingHeader ) {

std::size_t index = s.find( "\r\n\r\n" );

if ( index != std::string::npos ) {

header += s.substr( 0, index );

std::wstring ws( header.begin(), header.end() );

OutputDebugString( L"████ We got a header:\n" );
OutputDebugString( ( ws + L"\n" ).c_str() );

int dataLen = len - index - 4;

data.resize( dataLen );

std::copy( std::begin( buf ) + index + 4, std::begin( buf ) + len, std::begin( data ) );

parsingHeader = false;
}
else {

header += s;
}
}
else {

int currentSize;

currentSize = data.size();

data.resize( currentSize + len );

std::copy( std::begin(buf), std::begin(buf) + len, std::begin(data) + currentSize );
}

if ( !parsingHeader ) {

OutputDebugString( ( L"████ Data size: " + std::to_wstring( data.size() ) + L"\n" ).c_str() );

while ( data.size() != 0 ) {

int typeFlag = ( unsigned int )data[0];

OutputDebugString( ( L"████ First byte: " + std::to_wstring( typeFlag ) + L"\n" ).c_str() );

switch ( typeFlag ) {

case 1:
OutputDebugString(L"████ We have text");
break;
case 2:
OutputDebugString(L"████ We have sound");
break;
default:
OutputDebugString( L"████ Unhandled byte\n" );
goto exitloop;
}

if ( data.size() < 3 ) {

OutputDebugString( L" but not enough bytes to read its length.\n" );
break;
}

int segmentSize = int( (unsigned char)data[1] << 8 | (unsigned char)data[2] );

OutputDebugString( ( L" for " + std::to_wstring( segmentSize )  + L" bytes" ).c_str() );

if ( data.size() < 3 + segmentSize ) {

OutputDebugString( L" but they are not ready yet.\n" );
break;
}

OutputDebugString( L".\n" );

if ( typeFlag == 1 ) {

std::wstring encoded( data.begin() + 3, data.begin() + 3 + segmentSize );

std::wstring text = base64_decode( encoded );

OutputDebugString( ( text + L"\n" ).c_str() );
}
else if (typeFlag == 2) {


}

data.erase( data.begin(), data.begin() + 3 + segmentSize );
}
}
}

exitloop:



boost::system::error_code ec;
socket.shutdown( boost::asio::ip::tcp::socket::shutdown_both, ec );

if ( ec && ec != boost::system::errc::not_connected )
throw boost::system::system_error{ ec };

PHP:

<?php


set_time_limit( 0 );

// header or not, it doesn't seam to change anything.
/*header( 'Content-Type: application/octet-stream' );
header( 'Content-Transfer-Encoding: binary' );
header( 'Expires: 0' );
header( 'Cache-Control: must-revalidate' );
header( 'Pragma: public' );*/

//$partition = $_GET[ "partition" ];
$partition = "do"; //,re,mi,fa,sol! la:si?";
$separators = " ,.;:!?";

chdir( ".." );

function echoObject( $object ) {

$json = json_encode( $object );

$encoded = base64_encode( $json );

echo pack( "Cn", 1, strlen( $encoded ) );
echo $encoded;
}

function play( $partition, $start, $length ) {

$note = substr( $partition, $start, $length );

$object = new stdClass();
$object->type = "highlight";
$object->start = $start;
$object->length = $length;
$object->note = $note;

echoObject( $object );

$wav = "notes/" . $note . ".wav";

$fileHandle = fopen( $wav, 'rb' );

fseek( $fileHandle, 32 );

$infos = unpack( 'Vsize', fread( $fileHandle, 4 ) );

$size = intval( $infos[ 'size' ] );

fread( $fileHandle, 4 ); //remove the 4 bytes for "data";

$total = 0;

do {

$len = min( $size - $total, 256 );

echo pack( "Cn", 2, $len );
echo fread( $fileHandle, $len );

$total += $len;

//usleep( 100000 );

echoObject( array( "total" => $total, "size" => $size ) );
}
while( $total < $size );

fclose( $fileHandle );
}

$start = 0;
$length = 0;

echoObject( array( "type"=> "start" ) );

do {

if ( strpos( $separators, substr( $partition, $start + $length, 1 ) ) ) {

if ( $length == 0 ) {

$start++;
}
else {

play( $partition, $start, $length );

$start += $length + 1;
$length = 0;
}
}
else {

$length++;

if ( $start + $length == strlen( $partition ) ) {

play( $partition, $start, $length );
}
}
}
while( $start + $length < strlen( $partition ) );

echoObject( array( "type"=> "over" ) );

?>

На данный момент я застрял.
Любая помощь приветствуется.
Благодарю.

ОБНОВЛЕНИЕ 1

После перехода на другой метод, следуя примеру elarmando, у меня на клиенте следующий код:

boost::asio::streambuf response;
boost::asio::read_until( socket, response, "\r\n" );

// Check that response is OK.
std::istream response_stream( &response );
std::string http_version;
response_stream >> http_version;
unsigned int status_code;
response_stream >> status_code;
std::string status_message;
std::getline( response_stream, status_message );

if ( !response_stream || http_version.substr( 0, 5 ) != "HTTP/" ) {

OutputDebugString( L"████ Invalid response\n" );
return S_FALSE;
}

if ( status_code != 200 ) {

OutputDebugString( ( L"████ Response returned with status code " + std::to_wstring( status_code ) + L"\n" ).c_str() );
return S_FALSE;
}

// Read the response headers, which are terminated by a blank line.
boost::asio::read_until( socket, response, "\r\n\r\n" );

OutputDebugString( L"████ We have a header:\n" );

// Process the response headers.
std::string header;
while ( std::getline( response_stream, header ) && header != "\r" )
OutputDebugString( ( std::wstring( header.begin(), header.end() ) + L"\n" ).c_str() );
OutputDebugString( L"\n" );

OutputDebugString( L"████ Now let us parse the body:\n" );

std::deque< byte > data;
auto handle = [ &data, &response ]() -> bool {

const BYTE* test = boost::asio::buffer_cast<const BYTE*>(response.data());

int currentSize;

currentSize = data.size();

data.resize( currentSize + response.size() );

std::copy( test, test + response.size(), std::begin(data) + currentSize );

response.consume(response.size());

OutputDebugString((L"████ Data end - begin: " + std::to_wstring( data.end() - data.begin() ) + L"\n").c_str());
OutputDebugString((L"████ Data size: " + std::to_wstring(data.size()) + L"\n").c_str());

while ( data.size() != 0 ) {

int typeFlag = (unsigned int)data[0];

OutputDebugString( ( L"████ First byte: " + std::to_wstring(typeFlag) + L"\n" ).c_str());

switch (typeFlag) {

case 1:
OutputDebugString( L"████ We have text" );
break;
case 2:
OutputDebugString( L"████ We have sound" );
break;
default:
OutputDebugString( L"████ Unhandled byte\n" );
return false;
}

if ( data.size() < 3 ) {

OutputDebugString( L" but not enough bytes to read its length.\n" );
break;
}

int segmentSize = int( ( unsigned char)data[1] << 8 | (unsigned char)data[2] );

OutputDebugString( ( L" for " + std::to_wstring( segmentSize ) + L" bytes" ).c_str());

if ( data.size() < 3 + segmentSize) {

OutputDebugString( L" but they are not ready yet.\n" );
break;
}

OutputDebugString( L".\n" );

if (typeFlag == 1) {

std::wstring encoded( data.begin() + 3, data.begin() + 3 + segmentSize );

OutputDebugString( ( encoded + L"\n").c_str() );

std::wstring text = base64_decode( encoded );

OutputDebugString( ( text + L"\n" ).c_str() );
}
else if (typeFlag == 2) {

//play the audio here
}

OutputDebugString((L"████ Data size before erase: " + std::to_wstring(data.size()) + L"\n").c_str());

data.erase( data.begin(), data.begin() + 3 + segmentSize );

OutputDebugString((L"████ Data size after resize: " + std::to_wstring(data.size()) + L"\n").c_str());
}

//OutputDebugString( ( L"████ Size: " + std::to_wstring( data.size() ) + L"\n").c_str() );

return true;
};

if ( response.size() > 0 )
if ( !handle() ) goto parsingerror;

boost::system::error_code error;

while (boost::asio::read(socket, response, boost::asio::transfer_at_least(1), error))
if ( !handle() ) goto parsingerror;

if ( error != boost::asio::error::eof )
throw boost::system::system_error( error );

goto done;

parsingerror:

int typeFlag = (unsigned int)data[0];
OutputDebugString( ( L"████ First byte is: " + std::to_wstring( typeFlag ) + L"\n" ).c_str() );

done:

OutputDebugString( L"████ Done\n" );

return S_FALSE;

И у меня точно такой же результат.

Но потом я кое-что понял, и это то, что, когда содержание больше определенного значения (в моем случае оно составляет около 8000, я предполагаю 8192), заголовок ответа меняется с:

Date: Thu, 31 May 2018 14:09:52 GMT
Server: Apache/2.4.29 (Ubuntu)
Vary: Accept-Encoding
Content-Length: 7977
Content-Type: text/html; charset=UTF-8

чтобы:

Date: Thu, 31 May 2018 14:09:31 GMT
Server: Apache/2.4.29 (Ubuntu)
Vary: Accept-Encoding
Transfer-Encoding: chunked
Content-Type: text/html; charset=UTF-8

Так что я думаю, что кодирование по частям требует дополнительного разбора.

1

Решение

Задача ещё не решена.

Другие решения

Других решений пока нет …

По вопросам рекламы ammmcru@yandex.ru
Adblock
detector