我有一个 C++ 管道服务器应用和一个 C# 管道客户端应用,通过 Windows 命名管道进行通信(双工、消息模式、在单独的读线程中等待/阻塞)。
它们之间的数据传输一切正常(通过管道发送和接收数据),直到我尝试在客户端响应表单“textchanged”事件后向管道写入数据。这时,客户端在管道写入调用(如果自动冲洗关闭,则为 flush 调用)上挂起。进入服务器应用程序时发现,它也在等待管道 ReadFile 调用而未返回。 我尝试在另一个线程上运行客户端写操作,但结果相同。
怀疑出现了某种死锁或竞争条件,但无法看到是哪里出错… 不认为我同时写入管道。
更新1:将管道从消息模式改为字节模式,结果仍然是锁定状态。
更新2:奇怪的是,只有当我从服务器向客户端积累大量数据时,才会解决锁定问题!?
服务器代码:
它们之间的数据传输一切正常(通过管道发送和接收数据),直到我尝试在客户端响应表单“textchanged”事件后向管道写入数据。这时,客户端在管道写入调用(如果自动冲洗关闭,则为 flush 调用)上挂起。进入服务器应用程序时发现,它也在等待管道 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();
}
}