无法通过 NamedPipeServerStream 和 NamedPipeClientStream 获取字符串数据
Posted
技术标签:
【中文标题】无法通过 NamedPipeServerStream 和 NamedPipeClientStream 获取字符串数据【英文标题】:Cannot get string data to pass through NamedPipeServerStream and NamedPipeClientStream 【发布时间】:2010-06-04 15:16:56 【问题描述】:我正在尝试使用两个简单的 C# 表单解决方案在我的 Win-XP 工作站上实现双向命名管道通信。一个用于客户端,一个用于服务器。它们看起来几乎相同,并使用 NamedPipeServerStream 和 NamedPipeClientStream (.NET 3.5)。客户端和服务器都通过 PipeDirection.InOut
设置为双向通信启动事件的顺序是: 1)启动服务器。它等待来自客户端的连接。 2)启动客户端,它立即找到并连接到服务器。同样,服务器完成与客户端的连接。 3)客户端和服务器都启动它们的“读取”线程,从而创建流读取器的实例。然后这些线程调用 ReadLn() 并阻塞 - 等待数据。在所有情况下,自动刷新都是正确的。
然后我使用 streamwriter.WriteLn() 将字符串数据从服务器发送到客户端(反之亦然)。但是,执行永远不会从该调用返回。我不知道为什么,任何见解都会受到欢迎。
我花了相当多的时间研究有关这个主题的所有内容,但我仍然缺少一些东西。
客户端和服务端代码sn-ps如图:
服务器:
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();
【问题讨论】:
你有没有弄清楚为什么 Write 从来没有返回? 【参考方案1】:尝试在流上设置异步标志:
NamedPipeClientStream(".", PipeName, PipeDirection.InOut, PipeOptions.Asynchronous);
【讨论】:
【参考方案2】:我现在已经放弃并转向使用两个管道的安全、明显的技术,一个用于通信的每个方向。它们工作正常。
【讨论】:
【参考方案3】:我不确定这是否会有所帮助,但我也遇到了同样的问题。首先,我不知道为什么对 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 中有很好的示例......
【讨论】:
【参考方案4】:我不是命名管道或匿名管道方面的专家,但我会尽我最大的努力帮助他人,即使你已经解决了你的问题。
客户端服务器通信是思考如何实现此过程的最佳方式。
服务器启动并侦听连接 --> 客户端发起到服务器的连接 -->服务器接受连接 -->客户端发出请求 -->服务器做出响应 --> 连接关闭.
服务器启动并监听连接:
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 是一个异步回调。这个方法(在客户端)是管理连接的地方:
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
【讨论】:
以上是关于无法通过 NamedPipeServerStream 和 NamedPipeClientStream 获取字符串数据的主要内容,如果未能解决你的问题,请参考以下文章
无法通过 PancakeSwap 路由器移除流动性。通过“写”合约成功添加,但无法删除LiquidityETH
为啥 charles 无法通过 c++ 捕获 http 请求?