Full duplex named pipes on Windows

I would like to implement full duplex named pipe communications on Windows. The server needs to be in C++, and needs to be able to connect to multiple clients simultaneously. The client needs to be in C#. The client/server languages are not negotiable, this is part of an existing project.

This is what I have currently as a demo app to try and provide a POC. This is the C++ server side:

#include <windows.h>
#include <iostream>
#include <string>
#include <thread>
#include <vector>

// Handle client communication
void handleClientCommunication(HANDLE hPipe) {
    char buffer[512];
    DWORD bytesRead, bytesWritten;
    BOOL result;

    while (true) {
        // Read from client
        result = ReadFile(hPipe, buffer, sizeof(buffer), &bytesRead, NULL);
        if (!result || bytesRead == 0) {
            if (GetLastError() == ERROR_BROKEN_PIPE) {
                std::cout << "Client disconnected." << std::endl;
            } else {
                std::cerr << "ReadFile failed, GLE=" << GetLastError() << std::endl;
            }
            break;
        }

        std::string receivedMessage(buffer, bytesRead);
        std::cout << "Received: " << receivedMessage << std::endl;

        // Echo the message back to the client
        result = WriteFile(hPipe, buffer, bytesRead, &bytesWritten, NULL);
        if (!result || bytesWritten != bytesRead) {
            std::cerr << "WriteFile failed, GLE=" << GetLastError() << std::endl;
            break;
        }
    }

    CloseHandle(hPipe);
}

// Read server input and send messages to client
void readServerInputAndSendMessages(HANDLE hPipe) {
    std::string input;
    DWORD bytesWritten;

    while (true) {
        std::getline(std::cin, input);  // Read input from server's keyboard
        input += 'n';  // Optionally add a newline character
        
        // Send message to client
        BOOL result = WriteFile(hPipe, input.c_str(), input.size(), &bytesWritten, NULL);
        if (!result || bytesWritten != input.size()) {
            std::cerr << "WriteFile failed (sending from server), GLE=" << GetLastError() << std::endl;
            break;
        }
    }
}

// Create named pipe server
void createNamedPipeServer() {
    const std::wstring pipeName = L"\\.\pipe\MyNamedPipe";
    std::vector<std::thread> threads;

    while (true) {
        HANDLE hPipe = CreateNamedPipe(
            pipeName.c_str(),             // pipe name
            PIPE_ACCESS_DUPLEX,           // read/write access
            PIPE_TYPE_MESSAGE |           // message type pipe
            PIPE_READMODE_MESSAGE |       // message-read mode
            PIPE_WAIT,                    // blocking mode
            PIPE_UNLIMITED_INSTANCES,     // max. instances
            512,                          // output buffer size
            512,                          // input buffer size
            0,                            // client time-out
            NULL);                        // default security attribute

        if (hPipe == INVALID_HANDLE_VALUE) {
            std::cerr << "CreateNamedPipe failed, GLE=" << GetLastError() << std::endl;
            return;
        }

        std::cout << "Waiting for client connection..." << std::endl;
        BOOL connected = ConnectNamedPipe(hPipe, NULL) ? TRUE : (GetLastError() == ERROR_PIPE_CONNECTED);

        if (connected) {
            std::cout << "Client connected, creating communication threads." << std::endl;

            // Start thread to handle client communication
            std::thread clientThread(handleClientCommunication, hPipe);
            clientThread.detach();

            // Start thread to read server input and send messages to client
            std::thread inputThread(readServerInputAndSendMessages, hPipe);
            inputThread.detach();
        } else {
            CloseHandle(hPipe);
        }
    }
}

int main() {
    createNamedPipeServer();
    return 0;
}

This is the C# client:

using System;
using System.IO;
using System.IO.Pipes;
using System.Threading;
using System.Threading.Tasks;

namespace PipeClient
{
    class Program
    {
        static void Main(string[] args)
        {
            using (NamedPipeClientStream pipeClient = new NamedPipeClientStream(".", "MyNamedPipe", PipeDirection.InOut, PipeOptions.Asynchronous))
            {
                Console.WriteLine("Connecting to server...n");
                pipeClient.Connect();

                CancellationTokenSource cts = new CancellationTokenSource();

                // Start sending and receiving tasks
                Task sendTask = Task.Run(() => SendMessages(pipeClient, cts.Token));
                Task receiveTask = Task.Run(() => ReceiveMessages(pipeClient, cts.Token));

                Task.WhenAny(sendTask, receiveTask).Wait();

                // Cancel the other task and wait for it to complete
                cts.Cancel();
                Task.WaitAll(new[] { sendTask, receiveTask }, TimeSpan.FromSeconds(2));
            }
            Console.WriteLine("closed pipe");
        }

        static async Task SendMessages(NamedPipeClientStream pipeClient, CancellationToken token)
        {
            using (StreamWriter sw = new StreamWriter(pipeClient))
            {
                sw.AutoFlush = true;

                while (!token.IsCancellationRequested)
                {
                    Console.Write("Enter message (or 'exit' to quit): ");
                    string message = Console.ReadLine();
                    if (message == "exit")
                        break;

                    await sw.WriteLineAsync(message);
                }
            }
        }

        static async Task ReceiveMessages(NamedPipeClientStream pipeClient, CancellationToken token)
        {
            using (StreamReader sr = new StreamReader(pipeClient))
            {
                while (!token.IsCancellationRequested)
                {
                    try
                    {
                        string response = await sr.ReadLineAsync();
                        if (response == null)
                            break;

                        Console.WriteLine("Received from server: " + response);
                    }
                    catch (IOException)
                    {
                        break; // Exit the loop if the pipe is closed
                    }
                }
            }
        }
    }
}

Everything is working as intended, (though I have not tested multiple clients, I am sure that will need more work), except the server won’t send a message unless it first receives one from the client. The message from the server seems to be cached till the client initiates a connection, then the ‘cached’ data is returned along with the echoed response from the clients message.

I am trying to figure out if the issue is indeed a stream caching issue that can be resolved, or if my whole named pipe connection method is flawed.

Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa Dịch vụ tổ chức sự kiện 5 sao Thông tin về chúng tôi Dịch vụ sinh nhật bé trai Dịch vụ sinh nhật bé gái Sự kiện trọn gói Các tiết mục giải trí Dịch vụ bổ trợ Tiệc cưới sang trọng Dịch vụ khai trương Tư vấn tổ chức sự kiện Hình ảnh sự kiện Cập nhật tin tức Liên hệ ngay Thuê chú hề chuyên nghiệp Tiệc tất niên cho công ty Trang trí tiệc cuối năm Tiệc tất niên độc đáo Sinh nhật bé Hải Đăng Sinh nhật đáng yêu bé Khánh Vân Sinh nhật sang trọng Bích Ngân Tiệc sinh nhật bé Thanh Trang Dịch vụ ông già Noel Xiếc thú vui nhộn Biểu diễn xiếc quay đĩa Dịch vụ tổ chức tiệc uy tín Khám phá dịch vụ của chúng tôi Tiệc sinh nhật cho bé trai Trang trí tiệc cho bé gái Gói sự kiện chuyên nghiệp Chương trình giải trí hấp dẫn Dịch vụ hỗ trợ sự kiện Trang trí tiệc cưới đẹp Khởi đầu thành công với khai trương Chuyên gia tư vấn sự kiện Xem ảnh các sự kiện đẹp Tin mới về sự kiện Kết nối với đội ngũ chuyên gia Chú hề vui nhộn cho tiệc sinh nhật Ý tưởng tiệc cuối năm Tất niên độc đáo Trang trí tiệc hiện đại Tổ chức sinh nhật cho Hải Đăng Sinh nhật độc quyền Khánh Vân Phong cách tiệc Bích Ngân Trang trí tiệc bé Thanh Trang Thuê dịch vụ ông già Noel chuyên nghiệp Xem xiếc khỉ đặc sắc Xiếc quay đĩa thú vị
Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa
Thiết kế website Thiết kế website Thiết kế website Cách kháng tài khoản quảng cáo Mua bán Fanpage Facebook Dịch vụ SEO Tổ chức sinh nhật