我寫了一個命名管道通信例子,但是現在有兩個疑問?
疑問1 : 為什麼我的程序只能由客戶端向服務器發送數據,服務端不能向客戶端發送數據呢?服務端顯示發送成功,但是客戶端相應的可讀事件不能被觸發。
疑問2 : 偶爾出現客戶端向服務器發送數據的時候 前一兩個包丟失。
服務端代碼:
#include "stdafx.h"
#include <windows.h>
#include <stdio.h>
#include <tchar.h>
#include <strsafe.h>
#define CONNECTING_STATE 3
#define READING_STATE 1
#define WRITING_STATE 2
#define INSTANCES 1
#define PIPE_TIMEOUT 5000
#define BUFSIZE 4096
typedef struct
{
OVERLAPPED oOverlap;
HANDLE hPipeInst;
} PIPEINST, *LPPIPEINST;
VOID DisconnectAndReconnect(DWORD);
BOOL ConnectToNewClient(HANDLE, LPOVERLAPPED);
bool PostRecvRequest(LPPIPEINST hInst)
{
if (NULL == hInst)
return false;
char buf[1] = {0};
DWORD dwBytesReaded = 0;
if (!ReadFile(hInst->hPipeInst, buf, 1, &dwBytesReaded, &hInst->oOverlap))
{
DWORD err = GetLastError();
if (err != ERROR_IO_PENDING)
{
printf("client ReadFile error : %d \n", err);
return false;
}
}
return true;
}
bool IsNamedPipeCanRecv(LPPIPEINST hInst)
{
if (NULL == hInst)
return false;
if (WAIT_OBJECT_0 ==WaitForSingleObject(hInst->oOverlap.hEvent, INFINITE))
{
return true;
}
ResetEvent(hInst->oOverlap.hEvent);
return false;
}
DWORD WINAPI RecvThread(LPVOID param)
{
LPPIPEINST hInst = (LPPIPEINST) param;
if (hInst)
{
while(1)
{
if (IsNamedPipeCanRecv(hInst))
{
char buf[1024] = {0};
DWORD dwBytesReaded = 0;
ReadFile(hInst->hPipeInst, buf, 1024, &dwBytesReaded, NULL);
printf("Server recv : %s\n", buf);
}
PostRecvRequest(hInst);
}
}
return 0;
}
PIPEINST Pipe[INSTANCES];
HANDLE hEvents[INSTANCES];
int _tmain(int argc, _TCHAR* argv[])
{
LPTSTR lpszPipename = TEXT("\\\\.\\pipe\\mynamedpipe");
for (int i = 0; i < INSTANCES; i++)
{
// Create an event object for this instance.
hEvents[i] = CreateEvent(
NULL, // default security attribute
TRUE, // manual-reset event
TRUE, // initial state = signaled
NULL); // unnamed event object
if (hEvents[i] == NULL)
{
printf("CreateEvent failed with %d.\n", GetLastError());
return 0;
}
Pipe[i].oOverlap.hEvent = hEvents[i];
Pipe[i].hPipeInst = CreateNamedPipe(
lpszPipename, // pipe name
PIPE_ACCESS_DUPLEX | // read/write access
FILE_FLAG_OVERLAPPED, // overlapped mode
PIPE_TYPE_MESSAGE | // message-type pipe
PIPE_READMODE_MESSAGE | // message-read mode
PIPE_WAIT, // blocking mode
INSTANCES, // number of instances
BUFSIZE*sizeof(TCHAR), // output buffer size
BUFSIZE*sizeof(TCHAR), // input buffer size
PIPE_TIMEOUT, // client time-out
NULL); // default security attributes
if (Pipe[i].hPipeInst == INVALID_HANDLE_VALUE)
{
printf("CreateNamedPipe failed with %d.\n", GetLastError());
return 0;
}
ConnectToNewClient(
Pipe[i].hPipeInst,
&Pipe[i].oOverlap);
}
HANDLE hThread = CreateThread(0, 0, RecvThread, &Pipe[0], 0, 0);
while (1)
{
char buf[1024];
scanf("%s", buf);
DWORD dw;
BOOL b = WriteFile(Pipe[0].hPipeInst, buf, strlen(buf), &dw, &Pipe[0].oOverlap);
printf("send(%d:%d) : %s\n", dw, b, buf);
}
return 0;
}
VOID DisconnectAndReconnect(DWORD i)
{
// Disconnect the pipe instance.
if (! DisconnectNamedPipe(Pipe[i].hPipeInst) )
{
printf("DisconnectNamedPipe failed with %d.\n", GetLastError());
}
// Call a subroutine to connect to the new client.
ConnectToNewClient(
Pipe[i].hPipeInst,
&Pipe[i].oOverlap);
}
BOOL ConnectToNewClient(HANDLE hPipe, LPOVERLAPPED lpo)
{
BOOL fConnected, fPendingIO = FALSE;
// Start an overlapped connection for this pipe instance.
fConnected = ConnectNamedPipe(hPipe, lpo);
// Overlapped ConnectNamedPipe should return zero.
if (fConnected)
{
printf("ConnectNamedPipe failed with %d.\n", GetLastError());
return 0;
}
switch (GetLastError())
{
// The overlapped connection in progress.
case ERROR_IO_PENDING:
fPendingIO = TRUE;
break;
// Client is already connected, so signal an event.
case ERROR_PIPE_CONNECTED:
if (SetEvent(lpo->hEvent))
break;
// If an error occurs during the connect operation...
default:
{
printf("ConnectNamedPipe failed with %d.\n", GetLastError());
return 0;
}
}
return fPendingIO;
}
客戶端代碼
#include "stdafx.h"
#include <windows.h>
#include <stdio.h>
#include <conio.h>
#include <tchar.h>
#define BUFSIZE 512
typedef struct
{
OVERLAPPED oOverlap;
HANDLE hPipeInst;
} PIPEINST, *LPPIPEINST;
bool PostRecvRequest(LPPIPEINST hInst)
{
if (NULL == hInst)
return false;
char buf[1] = {0};
DWORD dwBytesReaded = 0;
if (!ReadFile(hInst->hPipeInst, buf, 1, &dwBytesReaded, &hInst->oOverlap))
{
DWORD err = GetLastError();
if (err != ERROR_IO_PENDING)
{
// 109 管道已結束
printf("client ReadFile error : %d \n", err);
return false;
}
}
return true;
}
bool IsNamedPipeCanRecv(LPPIPEINST hInst)
{
if (NULL == hInst)
return false;
if (WAIT_OBJECT_0 ==WaitForSingleObject(hInst->oOverlap.hEvent, INFINITE))
{
return true;
}
ResetEvent(hInst->oOverlap.hEvent);
return false;
}
DWORD WINAPI RecvThread(LPVOID param)
{
LPPIPEINST hInst = (LPPIPEINST) param;
if (hInst)
{
while(1)
{
if (IsNamedPipeCanRecv(hInst))
{
char buf[1024] = {0};
DWORD dwBytesReaded = 0;
ReadFile(hInst->hPipeInst, buf, 1024, &dwBytesReaded, NULL);
printf("Client recv : %s\n", buf);
}
PostRecvRequest(hInst);
}
}
return 0;
}
int _tmain(int argc, _TCHAR* argv[])
{
PIPEINST Pipe;
Pipe.oOverlap.hEvent = CreateEvent(
NULL, // default security attribute
TRUE, // manual-reset event
FALSE, // initial state = signaled
NULL); // unnamed event object
LPTSTR lpvMessage=TEXT("Default message from client.");
BOOL fSuccess = FALSE;
DWORD dwMode;
LPTSTR lpszPipename = TEXT("\\\\.\\pipe\\mynamedpipe");
if( argc > 1 )
lpvMessage = argv[1];
// Try to open a named pipe; wait for it, if necessary.
while (1)
{
Pipe.hPipeInst = CreateFile(
lpszPipename, // pipe name
GENERIC_READ | GENERIC_WRITE, // read and write access
0, // no sharing
NULL, // default security attributes
OPEN_EXISTING, // opens existing pipe
FILE_FLAG_OVERLAPPED, // default attributes
NULL); // no template file
// Break if the pipe handle is valid.
if ( Pipe.hPipeInst != INVALID_HANDLE_VALUE)
break;
// Exit if an error other than ERROR_PIPE_BUSY occurs.
if (GetLastError() != ERROR_PIPE_BUSY)
{
_tprintf( TEXT("Could not open pipe. GLE=%d\n"), GetLastError() );
return -1;
}
// All pipe instances are busy, so wait for 20 seconds.
if ( ! WaitNamedPipe(lpszPipename, 20000))
{
printf("Could not open pipe: 20 second wait timed out.");
return -1;
}
}
// The pipe connected; change to message-read mode.
dwMode = PIPE_READMODE_MESSAGE | PIPE_WAIT;
fSuccess = SetNamedPipeHandleState(
Pipe.hPipeInst, // pipe handle
&dwMode, // new pipe mode
NULL, // don't set maximum bytes
NULL); // don't set maximum time
if ( ! fSuccess)
{
_tprintf( TEXT("SetNamedPipeHandleState failed. GLE=%d\n"), GetLastError() );
return -1;
}
// Send a message to the pipe server.
PostRecvRequest(&Pipe);
HANDLE hThread = CreateThread(0, 0, RecvThread, &Pipe, 0, 0);
while (1)
{
char buf[1024];
scanf("%s", buf);
DWORD dw;
WriteFile(Pipe.hPipeInst, buf, strlen(buf), &dw, NULL);
printf("send(%d) : %s\n", dw, buf);
}
return 0;
}
你客戶端也要像服務端那樣也創建一個對應事件的管道,然後讓服務端反向給你數據。