我正在尝试使用命名管道从服务器向客户端发送4个参数——一个整数、一个布尔和两个字符串。我尝试过不同的方法,但仍然没有成功。第一种方式-我只是将所有参数转换为字符串,并尝试像那样发送,但在客户端上,我收到了所有参数为null:
服务器代码:
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();
}
客户代码:
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());
}
我做的第二种方法是创建字符串列表并在服务器上对其进行序列化,在客户端上使用BinaryFormatter进行反序列化,但得到了以下异常:"System.Runtime.Serialization.Serialization异常:在分析完成前遇到流的末尾"
服务器代码:
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();
}
客户代码:
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);
}
最后,我能够执行此操作,使用xml序列化和特定的数据协议来读取和写入字符串。
使用我们需要从服务器传递到客户端的乘法数据类,实现了两种序列化/反序列化到xml/从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);
}
}
}
定义用于读取和写入字符串的数据协议的类助手:
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;
}
}
服务器代码:
[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();
}
}
客户代码:
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;
}
}
有一种方法可以做到这一点,而无需序列化/反序列化。序列化/反序列化是以特定格式解析字符串并将其读取到本机对象的过程。解析过程需要额外的处理来将这种特定格式转换为所需的数据格式。。
相反,您可以只发送对象的实际字节,然后在客户端中读取字节并将其复制到实际的类中。
以下是如何做到这一点-
管道服务器
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");
}
}
只有当你运行这两个程序时,它们才会继续执行。在管道客户端代码中放置一个断点,以查看实际数据。