簡體   English   中英

寫入時全雙工命名管道鎖定

[英]Full duplex named pipe lockup when written to

我正在嘗試使用一個 NamedPipe 進行雙向 IPC。 在我看來(我在 MSDN 上找不到更多信息),一個全雙工管道就足夠了。 這是我的代碼。

//Compiled with these commands during my test:
//g++ -DCLIENT -o client.exe xxx.cpp
//g++ -DSERVER -o server.exe xxx.cpp

#include <iostream>
#include <windows.h>
using namespace std;

DWORD WINAPI ReadingThread(LPVOID a)
{
    HANDLE pipe = (HANDLE)a;
    BOOL result;
    char buffer[256];
    DWORD numBytesRead;
    while (true)
    {
        result = ReadFile(pipe, buffer, sizeof(buffer) - 1, &numBytesRead, NULL);

        if (result)
        {
            buffer[numBytesRead] = 0;
            cout << "[Thread] Number of bytes read: " << numBytesRead << endl;
            cout << "[Thread] Message: " << endl
                 << buffer << endl
                 << endl;
        }
        else
        {
            cout << "[Thread] Failed to read data from the pipe. err=" << GetLastError() << endl;
            break;
        }
    }
    return 0;
}

int main(int argc, const char **argv)
{
#ifdef CLIENT
    cout << "[Main] Connecting to pipe..." << endl;
    HANDLE pipe = CreateFileA("\\\\.\\pipe\\PipeTest", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
#else
    cout << "[Main] Creating an instance of a named pipe..." << endl;
    HANDLE pipe = CreateNamedPipeA("\\\\.\\pipe\\PipeTest", PIPE_ACCESS_DUPLEX, PIPE_TYPE_BYTE, 1, 0, 0, 0, NULL);
#endif

    if (pipe == NULL || pipe == INVALID_HANDLE_VALUE)
    {
        cout << "[Main] Failed to acquire pipe handle." << endl;
        return 1;
    }

#ifdef CLIENT
#else
    cout << "[Server] Waiting for a client to connect to the pipe..." << endl;

    BOOL result = ConnectNamedPipe(pipe, NULL);
    if (!result)
    {
        cout << "[Server] Failed to make connection on named pipe." << endl;
        CloseHandle(pipe);
        return 1;
    }
    cout << "[Server] Client is here!" << endl;
    {
        const char *buf = "Hello pipe!\n";
        WriteFile(pipe, buf, strnlen(buf, 30), 0, 0);
    }
#endif

    CreateThread(0, 0, ReadingThread, pipe, 0, 0);
    cout << "[Main] Ready to send data." << endl;

    while (true)
    {
        char buffer[128];
        DWORD numBytesWritten = 0;
        BOOL result;

        cin >> buffer;
        if (!strcmp(buffer, "q"))
        {
            break;
        }
        cout << "[Main] Writing data to pipe..." << endl;
        result = WriteFile(pipe, buffer, strnlen(buffer, _countof(buffer)), &numBytesWritten, 0);
        if (result)
        {
            cout << "[Main] Written " << numBytesWritten << " bytes to the pipe." << endl;
        }
        else
        {
            cout << "[Main] Failed to write data to the pipe. err=" << GetLastError() << endl;
        }
    }
    CloseHandle(pipe);
    cout << "[Main] Done." << endl;
    return 0;
}

我可以得到“你好管道!” 從服務器端到客戶端的消息。 我希望在任一程序的終端上輸入一些字符串並按回車鍵,然后在另一側看到它。

然而,在 hello 消息之后,兩個程序都將停留在WriteFile調用上。 同時,線程卡在ReadFile調用處。 我怎樣才能讓它工作,或者我遺漏了什么?

當為同步 I/O 創建文件( FILE_OBJECT存在標志FO_SYNCHRONOUS_IO )時,文件上的所有I/O 操作都被序列化- 新操作將在 I/O 管理器中等待,然后FO_SYNCHRONOUS_IO驅動程序,直到當前(如果存在)未完成。 在並發中只能執行單個 I/O 請求。 如果我們在專用線程中阻止讀取 - 此文件上的所有其他 I/O 請求將被阻止,直到讀取未完成。 這不僅與寫作有關。 甚至查詢文件名/屬性也會在此處阻塞。 結果在這里單獨渲染讀取沒有幫助 - 我們阻止第一次寫入嘗試。 這里的解決方案使用異步文件 - 這讓任何數量的 I/O 操作並發執行。

Windows 中的命名管道是半雙工。 如 Windows 10 所示。MSDN 文檔是錯誤的。 已向 Microsoft 提交更正其文檔的請求。

雖然可以在客戶端上將管道打開為“通用讀取 | 通用寫入”,但您不能同時執行這兩項操作。

而在First Overlapped IO之后提交的Overlapped IO會破壞管道。

您可以提交重疊的 io。 然后等待它完成。 然后提交下一個重疊的io。 您不能同時提交重疊讀取和重疊寫入。

這就是定義,“半雙工”。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM