PipeServer不是每秒都发送文本

时间:2013-11-07 01:08:55

标签: c++ winapi pipe

这是第一个应用程序中的“PipeServer”:

  // Open Pipe and wait until ControlProgram is connecting
Pipe.Out = INVALID_HANDLE_VALUE;
const wchar_t *data = L"*** Hello Pipe World ***";
DWORD numBytesWritten = 0;
DWORD timerinit = GetTickCount();;
while (1)
{
    DWORD timer = GetTickCount();
    if ((timer - timerinit) > 1000)
    {
        timerinit = timer;
        if (ConnectNamedPipe(Pipe.Out, NULL))
        {
            WriteFile(
                Pipe.Out, // handle to our outbound pipe
                data, // data to send
                wcslen(data) * sizeof(wchar_t), // length of data to send (bytes)
                &numBytesWritten, // will store actual amount of data sent
                NULL // not using overlapped IO
            );
        }
        else
        {
            CloseHandle(Pipe.Out);
            Pipe.Out = INVALID_HANDLE_VALUE;
            do
            {
              Pipe.Out = CreateNamedPipeW(
                L"\\\\.\\pipe\\mypipe", // name of the pipe
                PIPE_ACCESS_OUTBOUND, // 1-way pipe -- send only
                PIPE_TYPE_BYTE, // send data as a byte stream
                1, // only allow 1 instance of this pipe
                0, // no outbound buffer
                0, // no inbound buffer
                0, // use default wait time
                NULL // use default security attributes
                );
            }
            while (Pipe.Out == INVALID_HANDLE_VALUE);
        }
    }
}

这是“PipeClient”应用程序:

///// CLIENT PROGRAM /////

#include <iostream>
#include <windows.h>

using namespace std;

int main(int argc, const char **argv)
{
    wcout << "Connecting to pipe..." << endl;

    // Open the named pipe
    // Most of these parameters aren't very relevant for pipes.
    HANDLE pipe = CreateFileW(
                      L"\\\\.\\pipe\\mypipe",
                      GENERIC_READ, // only need read access
                      FILE_SHARE_READ | FILE_SHARE_WRITE,
                      NULL,
                      OPEN_ALWAYS,
                      FILE_ATTRIBUTE_NORMAL,
                      NULL
                  );

    if (pipe == INVALID_HANDLE_VALUE)
    {
        wcout << "Failed to connect to pipe." << endl;
        // look up error code here using GetLastError()
        system("pause");
        return 1;
    }

    wcout << "Reading data from pipe..." << endl;
    while (1)
    {
        // The read operation will block until there is data to read
        wchar_t buffer[128];
        DWORD numBytesRead = 0;
        BOOL result = ReadFile(
                          pipe,
                          buffer, // the data from the pipe will be put here
                          127 * sizeof(wchar_t), // number of bytes allocated
                          &numBytesRead, // this will store number of bytes actually read
                          NULL // not using overlapped IO
                      );

        if (result)
        {
            buffer[numBytesRead / sizeof(wchar_t)] = '\0'; // null terminate the string
            wcout << "Number of bytes read: " << numBytesRead << endl;
            wcout << "Message: " << buffer << endl;
            FlushFileBuffers(pipe);
        }
        else
        {
            wcout << "Failed to read data from the pipe." << endl;
            wcout << result << endl;
            CloseHandle(pipe);
            break;
        }
    }
    // Close our pipe handle

    wcout << "Done." << endl;

    system("pause");
    return 0;
}

服务器应该等待客户端连接,然后每隔1秒发送一次定义的消息。

客户端应该能够随时启动/重启。

但每当我启动客户端时,它会收到一次消息,然后在从管道读取时退出并出错。

结果返回0。

更新(对于那些想知道它现在如何工作的人)

这是“更新的”PipeServer代码:

  // Open Pipe and wait until ControlProgram is connecting
    Pipe.Out = INVALID_HANDLE_VALUE;
    DWORD numBytesWritten = 0;
    DWORD timerinit = GetTickCount();
    bool connected = false;
    bool writesucc = false;
    bool initial = true;
    while (1)
    {
        DWORD timer = GetTickCount();
        wchar_t data[100];
        if (!initial)
        {
            swprintf_s(data, 100, L"Time: %d", timer); // use L"" prefix for wide chars
        }
        else
        {
            swprintf_s(data, 100, L"Welcome from your Pipe Server"); // use L"" prefix for wide chars
        }
        if ((timer - timerinit) > 1000)
        {
            timerinit = timer;
            if (!connected)
            {
                connected = ConnectNamedPipe(Pipe.Out, NULL);
            }
            if (connected)
            {
                writesucc = WriteFile(
                    Pipe.Out, // handle to our outbound pipe
                    data, // data to send
                    wcslen(data) * sizeof(wchar_t), // length of data to send (bytes)
                    &numBytesWritten, // will store actual amount of data sent
                    NULL // not using overlapped IO
                );
                if (writesucc) initial = false;
            }
            if ((!writesucc) || (Pipe.Out == INVALID_HANDLE_VALUE) || (!connected))
            {
                initial = true;
                CloseHandle(Pipe.Out);
                Pipe.Out = INVALID_HANDLE_VALUE;
                do
                {
                  Pipe.Out = CreateNamedPipeW(
                    L"\\\\.\\pipe\\mypipe", // name of the pipe
                    PIPE_ACCESS_OUTBOUND, // 1-way pipe -- send only
                    PIPE_TYPE_BYTE, // send data as a byte stream
                    1, // only allow 1 instance of this pipe
                    0, // no outbound buffer
                    0, // no inbound buffer
                    0, // use default wait time
                    NULL // use default security attributes
                    );
                }
                while (Pipe.Out == INVALID_HANDLE_VALUE);
            }
        }
    }

1 个答案:

答案 0 :(得分:0)

您的客户端失败是因为服务器每秒调用ConnectNamedPipe()然后在ConnectNamedPipe()失败时关闭连接,因为已经连接了客户端并且您一次只允许连接1个客户端。这在ConnectNamedPipe()文档中说明:

  

命名管道服务器进程可以将ConnectNamedPipe与新创建的管道实例一起使用。 它也可以与之前连接到另一个客户端进程的实例一起使用;在这种情况下,服务器进程必须首先调用DisconnectNamedPipe函数以断开句柄与先前客户端的连接,然后才能将句柄重新连接到新客户端。否则,ConnectNamedPipe返回零,如果先前的客户端已关闭其句柄,则GetLastError返回ERROR_NO_DATA,如果尚未关闭其句柄,则返回ERROR_PIPE_CONNECTED。

您只需编写一次消息 - ConnectNamedPipe()成功创建新连接时。在此之后你没有循环写作,你的下一次迭代再次调用ConnectNamedPipe()并失败。

您没有检查来自GetLastError()的任何错误代码,否则您在阅读文档后会注意到这种情况,以找出错误代码的含义。

尝试更像这样的东西:

const wchar_t *data = L"*** Hello Pipe World ***";
DWORD numBytesWritten;

Pipe.Out = CreateNamedPipeW(
    L"\\\\.\\pipe\\mypipe", // name of the pipe
    PIPE_ACCESS_OUTBOUND, // 1-way pipe -- send only
    PIPE_TYPE_BYTE, // send data as a byte stream
    1, // only allow 1 instance of this pipe
    0, // no outbound buffer
    0, // no inbound buffer
    0, // use default wait time
    NULL // use default security attributes
    );

if (Pipe.Out == INVALID_HANDLE_VALUE)
{
    // error creating pipe
}
else
{
    while (should wait for a client to connect)
    {
        if (!ConnectNamedPipe(Pipe.Out, NULL))
        {
            // if ERROR_PIPE_CONNECTED then a client is actually connected!
            if (GetLastError() != ERROR_PIPE_CONNECTED)
            {
                // error connecting a client
                break;
            }
        }

        DWORD timerinit = GetTickCount();
        while (should write to the client)
        {
            DWORD timer = GetTickCount();

            // GetTickCount() wraps back to 0 every 49.7 days, so account for that...
            DWORD elapsed = (timer >= timerinit) ? (timer - timerinit) : ((MAXDWORD - timerinit) + timer);

            if (elapsed >= 1000)
            {
                timerinit = timer;
                if (!WriteFile(
                    Pipe.Out, // handle to our outbound pipe
                    data, // data to send
                    wcslen(data) * sizeof(wchar_t), // length of data to send (bytes)
                    &numBytesWritten, // will store actual amount of data sent
                    NULL // not using overlapped IO
                )
                {
                    // error writing to client
                    break;
                }
            }
        }

        DisconnectNamedPipe(Pipe.Out);
    }

    CloseHandle(Pipe.Out);
}