Reputation: 3306
I'm trying to send 4 parameters - one integer, one bool and two strings from server to client using named pipes. I've tried different ways, but still not succeeded. First way - I just converted all parameters to string and tried to send like that, but on client I received all parameters as null:
Server code:
static void StartServer()
{
var server = new NamedPipeServerStream("PipesEnroll", PipeDirection.InOut);
while (true)
{
server.WaitForConnection();
StreamWriter writer = new StreamWriter(server);
string terminalTemplate;
string matcherTemplate;
int mathVersionNumber = 9;
int numberFingers;
bool isOk = Enroll.EnrollWithoutWCF(retrievedList, mathVersionNumber, out terminalTemplate, out matcherTemplate, out numberFingers);
writer.WriteLine(isOk.ToString());
writer.WriteLine(terminalTemplate);
writer.WriteLine(matcherTemplate);
writer.WriteLine(numberFingers.ToString());
writer.Flush();
server.Disconnect();
}
Client code:
using (var client = new NamedPipeClientStream(".", "PipesEnroll", PipeDirection.InOut))
{
client.Connect();
StreamReader reader = new StreamReader(client);
bool isOK = Convert.ToBoolean(reader.ReadLine());
string terminalTemplate = reader.ReadLine();
string matcherTemplate = reader.ReadLine();
int numberFingers = Convert.ToInt32(reader.ReadLine());
}
Second way I did is creating list of strings and serialized it on server, deserialized on client using BinaryFormatter, but got this exception:"System.Runtime.Serialization.SerializationException: End of Stream encountered before parsing was completed"
Server code:
var server = new NamedPipeServerStream("PipesEnroll", PipeDirection.InOut);
while (true)
{
server.WaitForConnection();
StreamWriter writer = new StreamWriter(server);
List<string> sendList = new List<string>();
sendList.Add(isOk.ToString());
sendList.Add(terminalTemplate);
sendList.Add(matcherTemplate);
sendList.Add(numberFingers.ToString());
BinaryFormatter formatterSerialize = new BinaryFormatter();
formatterSerialize.Serialize(writer.BaseStream, sendList);
writer.Flush();
server.Disconnect();
}
Client code:
using (var client = new NamedPipeClientStream(".", "PipesEnroll", PipeDirection.InOut))
{
client.Connect();
StreamReader reader = new StreamReader(client);
BinaryFormatter formatterDeserialize = new BinaryFormatter();
List<string> retrievedList = (List<string>) formatterDeserialize.Deserialize(reader.BaseStream);
}
Upvotes: 1
Views: 7783
Reputation: 679
There is a way to do it without serialization/deserialization. serialization/deserialization is the process of parsing a string in a specific format and reading it to a native object. The parsing process requires extra processing for converting such a specific format into the desired data format..
Instead, you can just send the actual bytes of the object and in the client you read the bytes and copy them to the actual class.
Here is how it can be done -
public class ServerProgram
{
public static void Main()
{
new ServerProgram().Run();
}
NamedPipeServerStream pipeServer;
struct RequestCommand
{
public int commandId;
public int commandValue;
};
IntPtr requestCommandHBuffer;
byte[] requestCommandBuffer;
RequestCommand requestCommand;
int requestCommandBufferSize;
public void Run()
{
// Setup buffers for RequestCommand
requestCommand = new RequestCommand();
requestCommandBufferSize = Marshal.SizeOf(requestCommand);
requestCommandBuffer = new byte[requestCommandBufferSize];
requestCommandHBuffer = Marshal.AllocHGlobal(requestCommandBufferSize);
// Create the pipe server
pipeServer = new NamedPipeServerStream("MY_PIPE_RESOURCE_KEY", PipeDirection.InOut, 1,
PipeTransmissionMode.Byte,
PipeOptions.Asynchronous);
// Wait for the client to connect
pipeServer.WaitForConnection();
// Set some data in our custom class
requestCommand.commandId = 123;
requestCommand.commandValue = 456;
// Convert the class data to bytes
Marshal.StructureToPtr(requestCommand, requestCommandHBuffer, true);
Marshal.Copy(requestCommandHBuffer, requestCommandBuffer, 0, requestCommandBufferSize);
// Write the data
pipeServer.Write(requestCommandBuffer, 0, requestCommandBufferSize);
// Close the pipe server
pipeServer.Close();
}
}
class ClientProgram
{
static void Main(string[] args)
{
new ClientProgram().Run();
}
struct RequestCommand
{
public int commandId;
public int commandValue;
};
IntPtr requestCommandHBuffer;
byte[] requestCommandBuffer;
int requestCommandBufferSize;
public void Run()
{
// Setup buffers for RequestCommand
requestCommandBufferSize = Marshal.SizeOf<RequestCommand>();
requestCommandBuffer = new byte[requestCommandBufferSize];
requestCommandHBuffer = Marshal.AllocHGlobal(requestCommandBufferSize);
// Create the pipe client
var pipeClient = new NamedPipeClientStream(".", "MY_PIPE_RESOURCE_KEY",
PipeDirection.In, PipeOptions.WriteThrough);
// Wait until connection (this should continue when the server pipe connected
pipeClient.Connect();
// Read the passed RequestCommand bytes
// NOTE: actualBytesRead should be equal to requestCommandBufferSize. You should check it in production code
var actualBytesRead = pipeClient.Read(requestCommandBuffer, 0, requestCommandBufferSize);
// Convert the bytes to instance of RequestCommand
Marshal.Copy(requestCommandBuffer, 0 /*int startIndex*/, requestCommandHBuffer /*IntPtr destination*/, requestCommandBufferSize);
var requestCommand = Marshal.PtrToStructure(requestCommandHBuffer, typeof(RequestCommand));
// This will contain the actual data the sent from the server
Console.WriteLine("Got message");
}
}
Only when you run both programs they will continue to execute. Put a break point in pipe client code to see that actual data.
Upvotes: 0
Reputation: 3306
Finally I've been able to perform this, using xml serialization and particular data protocol for reading and writing strings.
Class with multiply data that we need pass from server to client, implements two methods for serialization/deserialization into/from xml:
[Serializable]
public class ServerData
{
public bool Result { get; set; }
public int Int1 { get; set; }
public string Str1 { get; set; }
public string Str2 { get; set; }
public static string Serialize(ServerData dto)
{
//Add an empty namespace and empty value
XmlSerializerNamespaces ns = new XmlSerializerNamespaces();
ns.Add("", "");
XmlSerializer ser = new XmlSerializer(typeof(ServerData));
using (StringWriter textWriter = new StringWriter())
{
ser.Serialize(textWriter, dto, ns);
return textWriter.ToString();
}
}
public static ServerData Deserialize(string xml)
{
XmlSerializer ser = new XmlSerializer(typeof(ServerData));
using (var reader = new StringReader(xml))
{
return (ServerData)ser.Deserialize(reader);
}
}
}
Class helper that defines the data protocol for reading and writing strings:
public class StreamString
{
private Stream ioStream;
private UnicodeEncoding streamEncoding;
public StreamString(Stream ioStream)
{
this.ioStream = ioStream;
streamEncoding = new UnicodeEncoding();
}
public string ReadString()
{
byte[] strSizeArr = new byte[sizeof(int)];
ioStream.Read(strSizeArr, 0, sizeof(int));
int strSize = BitConverter.ToInt32(strSizeArr, 0);
byte[] inBuffer = new byte[strSize];
ioStream.Read(inBuffer, 0, strSize);
return streamEncoding.GetString(inBuffer);
}
public int WriteString(string outString)
{
byte[] outBuffer = streamEncoding.GetBytes(outString);
byte[] strSize = BitConverter.GetBytes(outBuffer.Length);
ioStream.Write(strSize, 0, strSize.Length);
ioStream.Write(outBuffer, 0, outBuffer.Length);
ioStream.Flush();
return outBuffer.Length + 2;
}
}
Server code:
[STAThread]
static void Main(string[] args)
{
Thread serverThread = new Thread(ServerThread);
serverThread.Priority = ThreadPriority.Highest;
serverThread.Start();
serverThread.Join();
}
static void ServerThread()
{
using (var server = new NamedPipeServerStream("PipesEnroll", PipeDirection.InOut, 1))
{
server.WaitForConnection();
var ss = new StreamString(server);
string terminalTemplate;
string matcherTemplate;
const int mathVersionNumber = 9;
int numberFingers;
bool isOk = Enroll.EnrollWithoutWCF(images, mathVersionNumber, out terminalTemplate, out matcherTemplate,
out numberFingers);
var dtoSend = new ServerData();
dtoSend.Result = isOk;
dtoSend.Int1 = numberFingers;
dtoSend.Str1 = terminalTemplate;
dtoSend.Str2 = matcherTemplate;
var xmlSend = ServerData.Serialize(dtoSend);
ss.WriteString(xmlSend);
server.WaitForPipeDrain();
server.Close();
}
}
Client code:
using (
var client = new NamedPipeClientStream(".", "PipesEnroll", PipeDirection.InOut,
PipeOptions.None, TokenImpersonationLevel.Impersonation))
{
client.Connect();
var ss = new StreamString(client);
string xmlReceive = ss.ReadString();
var dtoReceived = ServerData.Deserialize(xmlReceive);
bool isOK = dtoReceived.Result;
string terminalTemplate = dtoReceived.Str1;
string matcherTemplate = dtoReceived.Str2;
int numberFingers = dtoReceived.Int1;
}
}
Upvotes: 5