2012-09-22 9 views
6

Estoy utilizando Canalizaciones con nombre configuradas para enviar datos como una secuencia de bytes únicos para enviar estructuras de datos serializados entre dos aplicaciones. Los datos serializados cambian de tamaño dramáticamente. En el lado del envío, esto no es un problema, puedo ajustar el número de bytes para enviar exactamente.Canalizaciones con nombre, cómo saber el número exacto de bytes para leer en el lado de lectura. C++, Windows

¿Cómo puedo configurar el búfer en el extremo de recepción (Lectura) con el número exacto de bytes para leer? ¿Hay alguna manera de saber qué tan grande es la información en el lado de envío (Escritura)?

He mirado PeekNamedPipe, pero la función parece inútil para byte mecanografiado named pipes?

lpBytesLeftThisMessage [a cabo, opcional] Un puntero a una variable que recibe el número de bytes restantes en este mensaje. Este parámetro será cero para las tuberías con nombre de tipo de byte o para las tuberías anónimas. Este parámetro puede ser NULL si no se deben leer datos.

http://msdn.microsoft.com/en-us/library/windows/desktop/aa365779(v=vs.85).aspx

¿Cómo se puede manejar una situación de este tipo mejor si no se puede determinar el tamaño del búfer exacta requerida?

Envío de Código

string strData; 
strData = "ShortLittleString"; 

DWORD numBytesWritten = 0; 
result = WriteFile(
    pipe, // handle to our outbound pipe 
    strData.c_str(), // data to send 
    strData.length(), // length of data to send (bytes) 
    &numBytesWritten, // will store actual amount of data sent 
    NULL // not using overlapped IO 
); 

Código de lectura:

DWORD numBytesToRead0 = 0; 
DWORD numBytesToRead1 = 0; 
DWORD numBytesToRead2 = 0; 

BOOL result = PeekNamedPipe(
pipe, 
NULL, 
42, 
&numBytesToRead0, 
&numBytesToRead1, 
&numBytesToRead2 
); 

char * buffer ; 

buffer = new char[numBytesToRead2]; 

char data[1024]; //1024 is way too big and numBytesToRead2 is always 0 
DWORD _numBytesRead = 0; 

BOOL result = ReadFile(
pipe, 
data, // the data from the pipe will be put here 
1024, // number of bytes allocated 
&_numBytesRead, // this will store number of bytes actually read 
NULL // not using overlapped IO 
); 

En el código anterior búfer siempre es de tamaño 0 como la función PeakNamedPipe devuelve 0 para todos los numBytesToRead variables. ¿Hay alguna manera de establecer este tamaño de búfer exactamente? Si no, ¿cuál es la mejor manera de manejar una situación así? ¡Gracias por cualquier ayuda!

+0

Un enfoque simple es escribir la longitud primero, luego los datos. El receptor puede leer primero la longitud y asignar el búfer. No muy diferente a la forma en que funciona el modo de mensaje de tubería. –

Respuesta

1

Bueno, está utilizando ReadFile(). La documentación dice, entre otras cosas:

Si una tubería con nombre se lee en el modo de mensaje y el siguiente mensaje es más largo que el parámetro especifica nNumberOfBytesToRead, ReadFile devuelve FALSE y GetLastError devuelve ERROR_MORE_DATA. El resto del mensaje se puede leer mediante una llamada posterior a ReadFile o PeekNamedPipefunction.

¿Has probado eso? Nunca utilicé un tubo como este :-), solo los usé para llegar a los manejadores stdin/out de un proceso hijo.

Supongo que lo anterior se puede repetir tantas veces como sea necesario, haciendo que el "resto del mensaje" sea una descripción algo inexacta: creo que si el "resto" no cabe en el búfer, simplemente obtener otro ERROR_MORE_DATA para que sepa obtener el resto del resto.

O, si te estoy malinterpretando completamente y no estás realmente usando este "modo de mensaje": tal vez solo estás leyendo las cosas de la manera incorrecta. Podrías simplemente usar un buffer de tamaño fijo para leer datos y anexarlos a tu bloque final, hasta que hayas llegado al final de los datos. O optimice esto un poco aumentando el tamaño del búfer de tamaño "fijo" a medida que avanza.

+0

No, no lo he hecho. Parece una especie de truco, pero ¿sugieres que lea en incrementos muy pequeños y en bucle hasta que ya no obtenga un error ERROR_MORE_DATA? :) Eso debería funcionar, pero estoy tratando de evitar agregar el tamaño de los datos al mensaje o terminar con datos irrelevantes agregados a los datos serializados, básicamente cualquier trabajo de limpieza basado en char/string después de leer el mensaje. ¿No hay forma de leer hasta EOF como leer un archivo de texto? ¿Puedo insertar mi propio EOF en el lado de escritura tal vez? – Rudolf

+0

No estoy usando el modo de mensaje, estoy usando el modo byte. :) Pero creo que me acabas de dar una idea, creo que el modo byte está destinado a ser utilizado cuando se envían caracteres de longitud fija, cortaré y cambiaré el sistema a un canal de modo de mensaje e informaré de nuevo. El modo de byte – Rudolf

+0

está específicamente diseñado para poner la responsabilidad * * de administrar el protocolo de mensajería, o la falta del mismo, para el caso de uso que está implementando. En otras palabras, parte de message-pipes es el atributo de la longitud del mensaje. no existe tal atributo en modo byte a menos que * usted * lo administre enviando datos que lo representen. Dicho de otra manera, el modo mensaje es un tipo que le entrega una caja con una etiqueta que dice cuánto contiene. el modo byte es solo un tipo que lanza productos hacia ti. a menos que sepa lo que le pasa a la mente de 'el muchacho', no tiene suficiente con lo que realmente trabajar. – WhozCraig

3

¿Por qué crees que no puedes usar lpTotalBytesAvail para obtener el tamaño de datos enviado?Siempre funciona para mí en modo bytes. Si es siempre cero posiblemente hiciste algo mal. También sugiera utilizar std::vector como buffer de datos, es bastante más seguro que jugar con punteros sin procesar y la instrucción new.

lpTotalBytesAvail [out, optional] Puntero a una variable que recibe el número total de bytes disponibles para leer desde la tubería. Este parámetro puede ser NULL si no se deben leer datos.

Código de ejemplo:

// Get data size available from pipe 
DWORD bytesAvail = 0; 
BOOL isOK = PeekNamedPipe(hPipe, NULL, 0, NULL, &bytesAvail, NULL); 
if(!isOK) 
{ 
    // Check GetLastError() code 
} 

// Allocate buffer and peek data from pipe 
DWORD bytesRead = 0;  
std::vector<char> buffer(bytesAvail); 
isOK = PeekNamedPipe(hPipe, &buffer[0], bytesAvail, &bytesRead, NULL, NULL); 
if(!isOK) 
{ 
    // Check GetLastError() code 
} 
+0

lpTotalBytesAvail funcionó muy bien para mí! Esto me ahorró muchos problemas y costosas copias de memoria intermedia para anteponer el tamaño como Hans Passant había sugerido. –

Cuestiones relacionadas