Duplex named pipe hangs on a specific record

I have a C ++ pipe server application and a client application for a C # channel exchanging via Windows with the name pipe (duplex, message mode, wait / block in a separate read stream).

Everything works fine (both sending and receiving data through the channel) until I try to write to the receiver from the client in response to the "textchanged" event. When I do this, the client hangs up on the recording pipe call (or during a short call if the autostart function is disabled). The inclusion of the server in the application shows that it also expects a call on the ReadFile and does not return. I tried running the client entry in another thread - the same result.

I suspect some kind of dead end or race condition, but I don’t see where ... I don’t think I am writing to the pipe at the same time.

Update1: tried channels in byte mode instead of message mode - same lock.

Update2: It's strange if (and only if) I pump a lot of data from the server to the client, it cures the lock !?

Server Code:

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; } 

client code:

 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(); } } 
+4
source share
3 answers

If you use separate streams, you cannot read from the channel at the same time as you write to it. For example, if you make a lock that is read from a channel, and then a subsequent write lock (from another stream), then the write call will wait / block until the read call is completed, and in many cases, if this is an unexpected behavior, Your program will be stalled.

I have not tested overlapping I / O, but MAY be able to solve this problem. However, if you are configured to use synchronous calls, the following problems below will help you solve the problem.

Master / slave

You can implement a master / slave model in which the client or server is the master and the other end responds only to what you find in the MSDN examples.

In some cases, you may find this problem if the slave needs to periodically send data to the master. You must either use an external signaling mechanism (outside the channel), or the master must periodically request / interrogate the slave, or you can change the roles where the client is the master and the server is the slave.

Writer / Reader

You can use a writer / reader model in which you use two different pipes. However, you must connect the two pipes in some way if you have multiple clients, since each channel will have a different descriptor. This can be done if the client sends a unique identifier value when connected to each channel, which then allows the server to link the two channels. This number can be the current system time or even a unique identifier that is global or local.

Themes

If you are configured to use the synchronous API, you can use streams with the master / slave model if you do not want to be blocked while waiting for messages on the slave side. However, you will want to block the reader after he reads the message (or meets the end of a series of messages), then writes the answer (as a subordinate should) and finally unlocks the reader. You can lock and unlock the reader using locking mechanisms that make the thread sleep, as they would be most effective.

Security Issue with TCP

Loss coming from TCP instead of named pipes is also the biggest possible problem. The TCP stream does not contain any protection initially. Therefore, if security is a concern, you will have to implement this, and you have the opportunity to create a security hole, since you will have to handle authentication yourself. A named pipe can provide security if you set the parameters correctly. In addition, emphasize again: security is not a simple matter, and usually you need to use existing tools that were designed to provide it.

+4
source

I think that you may have problems with the mode of messages with named pipes. In this mode, each entry in the kernel channel descriptor is a message. This does not necessarily correspond to what your application considers a message, and the message may be larger than your read buffer.

This means that your channel read code needs two cycles, an internal read until the current [named pipe] message is fully received, and an external loop until your message [application level] is received.

Your C # client code has a proper inner loop that reads again if IsMessageComplete false:

 do { len += mPipeStream.Read(buffer, len, buffer.Length); } while (len>0 && !mPipeStream.IsMessageComplete); 

There is no such loop in the C ++ server code - the equivalent at the API level Win32 checks the return code ERROR_MORE_DATA.

My guess is that somehow this leads to the fact that the client expects the server to read one instance of the channel while the server waits while the client writes to another instance.

0
source

It seems to me that what you are trying to do will most likely not work as expected. Some time ago, I tried to do something similar to your code and got similar results, the handset was just hung up and it was difficult to establish what went wrong.

I would rather use the client in a very simple way:

  • Createfile
  • Recording Request
  • Read answer
  • Close the pipe.

If you want to have two-way communication with clients who can also receive unregistered data from the server, you should rather implement two servers. This was the workaround I used: here you can find the sources .

0
source

Source: https://habr.com/ru/post/1380432/


All Articles