【发布时间】:2011-12-25 12:46:25
【问题描述】:
我有一个 C++ 管道服务器应用程序和一个 C# 管道客户端应用程序通过 Windows 命名管道进行通信(双工、消息模式、在单独的读取线程中等待/阻塞)。
一切正常(通过管道发送和接收数据),直到我尝试从客户端写入管道以响应表单“textchanged”事件。当我这样做时,客户端挂起管道写入调用(或者如果 autoflush 关闭,则刷新调用)。闯入服务器应用程序表明它也在等待管道 ReadFile 调用并且没有返回。 我尝试在另一个线程上运行客户端写入——结果相同。
怀疑某种死锁或竞争条件,但看不到在哪里...不要认为我正在同时写入管道。
更新 1:尝试使用字节模式而不是消息模式的管道 - 相同的锁定。
更新 2:奇怪的是,如果(且仅当)我将大量数据从服务器泵送到客户端,它可以解决锁定问题!?
服务器代码:
DWORD ReadMsg(char* aBuff, int aBuffLen, int& aBytesRead)
{
DWORD byteCount;
if (ReadFile(mPipe, aBuff, aBuffLen, &byteCount, NULL))
{
aBytesRead = (int)byteCount;
aBuff[byteCount] = 0;
return ERROR_SUCCESS;
}
return GetLastError();
}
DWORD SendMsg(const char* aBuff, unsigned int aBuffLen)
{
DWORD byteCount;
if (WriteFile(mPipe, aBuff, aBuffLen, &byteCount, NULL))
{
return ERROR_SUCCESS;
}
mClientConnected = false;
return GetLastError();
}
DWORD CommsThread()
{
while (1)
{
std::string fullPipeName = std::string("\\\\.\\pipe\\") + mPipeName;
mPipe = CreateNamedPipeA(fullPipeName.c_str(),
PIPE_ACCESS_DUPLEX,
PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT,
PIPE_UNLIMITED_INSTANCES,
KTxBuffSize, // output buffer size
KRxBuffSize, // input buffer size
5000, // client time-out ms
NULL); // no security attribute
if (mPipe == INVALID_HANDLE_VALUE)
return 1;
mClientConnected = ConnectNamedPipe(mPipe, NULL) ? TRUE : (GetLastError() == ERROR_PIPE_CONNECTED);
if (!mClientConnected)
return 1;
char rxBuff[KRxBuffSize+1];
DWORD error=0;
while (mClientConnected)
{
Sleep(1);
int bytesRead = 0;
error = ReadMsg(rxBuff, KRxBuffSize, bytesRead);
if (error == ERROR_SUCCESS)
{
rxBuff[bytesRead] = 0; // terminate string.
if (mMsgCallback && bytesRead>0)
mMsgCallback(rxBuff, bytesRead, mCallbackContext);
}
else
{
mClientConnected = false;
}
}
Close();
Sleep(1000);
}
return 0;
}
客户端代码:
public void Start(string aPipeName)
{
mPipeName = aPipeName;
mPipeStream = new NamedPipeClientStream(".", mPipeName, PipeDirection.InOut, PipeOptions.None);
Console.Write("Attempting to connect to pipe...");
mPipeStream.Connect();
Console.WriteLine("Connected to pipe '{0}' ({1} server instances open)", mPipeName, mPipeStream.NumberOfServerInstances);
mPipeStream.ReadMode = PipeTransmissionMode.Message;
mPipeWriter = new StreamWriter(mPipeStream);
mPipeWriter.AutoFlush = true;
mReadThread = new Thread(new ThreadStart(ReadThread));
mReadThread.IsBackground = true;
mReadThread.Start();
if (mConnectionEventCallback != null)
{
mConnectionEventCallback(true);
}
}
private void ReadThread()
{
byte[] buffer = new byte[1024 * 400];
while (true)
{
int len = 0;
do
{
len += mPipeStream.Read(buffer, len, buffer.Length);
} while (len>0 && !mPipeStream.IsMessageComplete);
if (len==0)
{
OnPipeBroken();
return;
}
if (mMessageCallback != null)
{
mMessageCallback(buffer, len);
}
Thread.Sleep(1);
}
}
public void Write(string aMsg)
{
try
{
mPipeWriter.Write(aMsg);
mPipeWriter.Flush();
}
catch (Exception)
{
OnPipeBroken();
}
}
【问题讨论】:
-
您在服务器端对读取错误的处理看起来有点狡猾。作为一个诊断措施,我建议你临时改变服务器,让它在发生读取错误时退出,这样你就可以确定客户端的写入和服务器端的读取是在同一个管道上的。
-
尝试通过更改服务器使其不向管道写入任何数据来简化情况也可能会有所帮助,从而可以使客户端成为单线程。如果这不能消除问题,您至少可以确定这不是某种线程问题。
-
Chris:添加代码 :) @Harry:它顽固地位于 ReadFile fn 中,甚至没有返回错误。是否建议使用多个管道...见下文。尝试没有服务器写入:相同的结果。还在寻找... :-/
-
Win32 管道的设计目的不是为了有用...我在这里也遇到了一个问题,代码必须采取大量奇怪的怪癖才能工作。
-
@ActiveTrayPrntrTagDataStrDrvr 愿意分享这些怪癖吗?
标签: windows multithreading named-pipes