我正在尝试使用两个简单的C#表单解决方案在我的Win-XP工作站上实现双向命名管道通信。一个用于客户端,一个用于服务器。它们看起来几乎完全相同,并使用NamedPipeServerStream和NamedPipeClientStream(.NET 3.5)。客户端和服务器都通过 PipeDirection.InOut
设置为双向通信启动事件的顺序是: 1)启动服务器。它等待来自客户端的连接。 2)启动客户端,它立即找到并连接到服务器。同样,服务器完成与客户端的连接。 3)客户端和服务器都启动其“读取”线程,这反过来又创建了流读取器的实例。然后这些线程调用ReadLn()并阻塞 - 等待数据。在所有情况下,autoflush都是真的。
然后我使用streamwriter.WriteLn()将字符串数据从服务器发送到客户端(反之亦然)。但是,执行永远不会从该调用返回。我不知道为什么,任何见解都会得到很好的接受。
我花了相当多的时间研究这个主题的所有内容,但我仍然遗漏了一些东西。
显示客户端和服务器代码段:
SERVER:
private void ListenForClients()
{
// Only one server as this will be a 1-1 connection
m_pipeServerStream = new NamedPipeServerStream(PipeName, PipeDirection.InOut, 1);
// Wait for a client to connect
m_pipeServerStream.WaitForConnection();
// Ccould not create handle - server probably not running
if (!m_pipeServerStream.IsConnected)
return;
// Create a stream writer which flushes after every write
m_pipeServerWriter = new StreamWriter(m_pipeServerStream);
m_pipeServerWriter.AutoFlush = true;
Connected = true;
// Start listening for messages
if (m_pipeServerStream.CanRead)
{
ReadThread = new Thread(new ParameterizedThreadStart(Read));
ReadThread.Start(m_pipeServerStream);
}
}
/// <summary>
/// Reads data from the client
/// </summary>
/// <param name="serverObj"></param>
private void Read(object serverObj)
{
NamedPipeServerStream pipeStream = (NamedPipeServerStream)serverObj;
using (StreamReader sr = new StreamReader(pipeStream))
{
while (true)
{
string buffer;
try
{
buffer = sr.ReadLine();
}
catch
{
//read error has occurred
break;
}
//client has disconnected
if (buffer.Length == 0)
break;
//fire message received event
if (MessageReceived != null)
{
MessageReceived(buffer);
}
}
}
}
/// <summary>
/// Sends a message to the connected client
/// </summary>
/// <param name="message">the message to send</param>
public void SendMessage(string message)
{
if (m_pipeServerWriter != null)
{
m_pipeServerWriter.WriteLine(message);
m_pipeServerWriter.Flush();
}
}
客户端:
private void ConnectToServer()
{
// Seek out the one server
m_pipeClientStream = new NamedPipeClientStream(".", PipeName, PipeDirection.InOut);
// Connect to the waiting server
m_pipeClientStream.Connect();
// Ccould not create handle - server probably not running
if (!m_pipeClientStream.IsConnected)
return;
// Create a stream writer which flushes after every write
m_pipeClientWriter = new StreamWriter(m_pipeClientStream);
m_pipeClientWriter.AutoFlush = true;
Connected = true;
// Start listening for messages
if (m_pipeClientStream.CanRead)
{
ReadThread = new Thread(new ParameterizedThreadStart(Read));
ReadThread.Start(m_pipeClientStream);
}
}
/// <summary>
/// Reads data from the server
/// </summary>
private void Read(object serverObj)
{
NamedPipeClientStream pipeStream = (NamedPipeClientStream)serverObj;
using (StreamReader sr = new StreamReader(pipeStream))
{
while (true)
{
string buffer;
try
{
buffer = sr.ReadLine();
}
catch
{
//read error has occurred
break;
}
//client has disconnected
if (buffer.Length == 0)
break;
//fire message received event
if (MessageReceived != null)
{
MessageReceived(buffer);
}
}
}
}
/// <summary>
/// Sends a message to the connected server
/// </summary>
/// <param name="message"></param>
public void SendMessage(string message)
{
if (m_pipeClientWriter != null)
{
m_pipeClientWriter.WriteLine(message);
m_pipeClientWriter.Flush();
}
}
答案 0 :(得分:4)
尝试在流上设置Async标志:
NamedPipeClientStream(“。”,PipeName,PipeDirection.InOut,PipeOptions.Asynchronous);
答案 1 :(得分:2)
我现在已经放弃并转向使用两个管道的安全,明显的技术,每个管道用于每个通信方向。他们工作正常。
答案 2 :(得分:0)
我不确定这是否会有所帮助,但我也遇到了同样的问题。首先,我不知道为什么对m_pipeServerStream.IsConnected的任何引用都会破坏管道。我用一个简单的MessageBox.Show(m_pipeServerStream.IsConnected.ToString())测试了这个,这破坏了我的管道!
其次,另一个奇怪的事情是,如果您使用双工命名管道,您的streamreader调用将永远不会返回。你需要像这样手动阅读
const int BufferSize = 4096;
Decoder decoder = Encoding.UTF8.GetDecoder();
StringBuilder msg = new StringBuilder();
char[] chars = new char[BufferSize];
byte[] bytes = new byte[BufferSize];
int numBytes = 0;
MessageBox.Show("before do while loop");
numBytes = pipeServer.Read(bytes, 0, BufferSize);
if (numBytes > 0)
{
int numChars = decoder.GetCharCount(bytes, 0, numBytes);
decoder.GetChars(bytes, 0, numBytes, chars, 0, false);
msg.Append(chars, 0, numChars);
}
MessageBox.Show(numBytes.ToString() + " " + msg.ToString());
MessageBox.Show("Finished reading, now starting writing");
using (StreamWriter swr = new StreamWriter(pipeServer))
{
MessageBox.Show("Sending ok back");
swr.WriteLine("OK");
pipeServer.WaitForPipeDrain();
}
无论如何,它似乎不喜欢StreamReader的行为,但这现在可以正常工作......我从这个链接中得到了这个http://social.msdn.microsoft.com/forums/en-US/csharpgeneral/thread/23dc2951-8b59-48e4-89fe-d2b435db48c6/
我没有遵循每一步,因为我只需要找出为什么它一直挂在StreamReader.ReadLine()上。它没有从这个功能返回。 StreamWriter似乎没有这个问题。
我实际上是在本机dll和托管Windows服务之间进行通信。想象一下,当我发现托管部分是问题而不是非托管部分时,我感到惊讶,因为他们在msdn中有这么好的例子......
答案 3 :(得分:0)
我不是命名管道或匿名管道方面的专家,但即使您已经解决了问题,我也会尽力帮助其他人。
客户端服务器通信是考虑如何实现此过程的最佳方式。
服务器启动并侦听连接 - &gt;客户端启动与服务器的连接 - >服务器接受连接 - >客户端发出请求 - &gt;服务器发出响应 - &gt;连接已关闭。
服务器启动并侦听连接:
try
{
namedPipeServerStream = new NamedPipeServerStream(PipeName, PipeDirection.InOut, 1, PipeTransmissionMode.Byte, PipeOptions.Asynchronous);
// Wait for a connection here...
namedPipeServerStream.BeginWaitForConnection(new AsyncCallback(ConnectionCallBack), namedPipeServerStream);
}
catch (Exception ex)
{
Debug.WriteLine(ex.Message);
}
客户端连接,然后发出请求:
try
{
namedPipeClientStream = new NamedPipeClientStream(".", PipeName, PipeDirection.InOut, PipeOptions.Asynchronous);
// Connect with timeout...
namedPipeClientStream.Connect(TimeOut);
byte[] buffer = Encoding.UTF8.GetBytes(DataToSend);
namedPipeClientStream.BeginWrite(buffer, 0, buffer.Length, ConnectionCallBack, namedPipeClientStream);
}
catch (TimeoutException ex)
{
Debug.WriteLine(ex.Message);
}
ConnectionCallBack是一个异步回调。此方法(位于客户端上)是管理Connection的位置:
private void ConnectionCallBack(IAsyncResult iAsyncResult)
{
try
{
// Get the pipe
NamedPipeClientStream namedPipeClientStream = (NamedPipeClientStream)iAsyncResult.AsyncState;
// End the write
namedPipeClientStream.EndWrite(iAsyncResult);
namedPipeClientStream.Flush();
// Get Server Response...
GetServerResponse(namedPipeClientStream);
// Flush Data and Close Pipe...
namedPipeClientStream.Flush();
namedPipeClientStream.Close();
namedPipeClientStream.Dispose();
}
catch (Exception ex)
{
Debug.WriteLine(ex.Message);
}
}
服务器处理客户端请求并制定响应并发送它:
// Response Methods...
public void SendResponse(string ServerResponse)
{
try
{
// Fill Buffer with Server Response Data...
byte[] Buffer = Encoding.UTF8.GetBytes(ServerResponse);
// Begin Async Write to the Pipe...
namedPipeServerStream.BeginWrite(Buffer, 0, Buffer.Length, SendResponseCallBack, namedPipeServerStream);
}
catch (Exception ex)
{
Debug.WriteLine(ex.Message);
}
}
private void SendResponseCallBack(IAsyncResult iAsyncResult)
{
try
{
// Get the Pipe Handle...
NamedPipeServerStream namedPipeServerStream = (NamedPipeServerStream)iAsyncResult.AsyncState;
// End the Write and Flush...
namedPipeServerStream.EndWrite(iAsyncResult);
namedPipeServerStream.Flush();
// Close the Connection and Dispose...
namedPipeServerStream.Close();
namedPipeServerStream.Dispose();
}
catch (Exception ex)
{
Debug.WriteLine(ex.Message);
}
}
这是从客户端请求处理程序调用的:
private void ClientRequestHandler(string clientRequest)
{
try
{
if (this.InvokeRequired)
{
this.Invoke(new InvokedDelegate(ClientRequestHandler), clientRequest);
}
else
{
ProcessClientRequest(clientRequest);
}
}
catch (Exception ex)
{
Debug.WriteLine(ex.Message);
}
}
private void ProcessClientRequest(string clientRequest)
{
// Display the Client Request...
richTextBox1.Text = clientRequest;
PipeServer.SendResponse("Server has received Client Request at: " + DateTime.Now);
}
客户端启动了与服务器的连接,在异步回调方法看到这一点:
// Get Server Response...
GetServerResponse(namedPipeClientStream);
连接仍处于打开状态。客户端请求已完成,管道已刷新,并已准备好客户端读取上述服务器响应:
private void GetServerResponse(NamedPipeClientStream namedPipeClientStream)
{
byte[] buffer = new byte[255];
namedPipeClientStream.Read(buffer, 0, buffer.Length);
// Convert byte buffer to string
string ResponseData = Encoding.UTF8.GetString(buffer, 0, buffer.Length);
// Pass message back to calling form
ServerResponse.Invoke(ResponseData);
}
收到响应,然后连接再次刷新并关闭,以便客户端启动另一个连接。
代码比这更复杂,但实际上这就是它的工作原理。启动连接时,请使用它。一旦你关闭它,然后尝试重新初始化它,你需要等待一段时间才能正确处理它,否则你将得到各种信号量错误等等。当你不需要时,不要抽烟连接!!!
请参阅:Code Project - C# Async Named Pipes for an excellent example