我已经实现了命名管道服务器和客户端。管道处于PIPE_READMODE_MESSAGE
模式。我试图使用WriteFile
从客户端向服务器发送消息。操作失败,GetLastError
返回错误232 ERROR_NO_DATA
。如果我连接第二个客户端,则操作成功并发送消息。
我尝试使用SetNamedPipeHandleState
在同一管道句柄上进行其他操作,操作成功,所以我认为管道没问题。
从服务器发送到客户端的消息也是成功的。
我不认为这是一个缓冲区大小问题,因为我试图发送4个字节,并为管道定义512字节缓冲区。
有一个类似的主题:WriteFile on a named pipe sometimes returns ERROR_NO_DATA 但它并没有解决我的问题。 我在网上搜索了答案,但我找不到这样的东西。感谢任何帮助
服务器代码:
int CServer::Run(){
BOOL fConnected = FALSE;
DWORD dwThreadId = 0;
HANDLE hPipe = INVALID_HANDLE_VALUE, hThread = NULL;
//LPTSTR lpszPipename = TEXT("\\\\.\\pipe\\IlyaPipe");
LPTSTR lpszPipename = TEXT("IlyaPipe");
CSingleLock ConnListLock(&server.ConnManager.ConnListMutex);
CConnection* NewConn;
CWnd* pWnd = AfxGetMainWnd();
ASSERT_VALID(pWnd);
// The main loop creates an instance of the named pipe and
// then waits for a client to connect to it. When the client
// connects, a thread is created to handle communications
// with that client, and this loop is free to wait for the
// next client connect request. It is an infinite loop.
//start connection manager thread.
server.ConnManager.CreateThread();
SendMessage(pWnd->m_hWnd, WM_SERVER_ONLINE, 0, 0);
for (;;)
{
NewConn = new CConnection;
NewConn->Create(
lpszPipename, // 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
BUFSIZE, // output buffer size
BUFSIZE, // input buffer size
0, // client time-out
NULL);
// Wait for the client to connect; if it succeeds,
// the function returns a nonzero value. If the function
// returns zero, GetLastError returns ERROR_PIPE_CONNECTED.
NewConn->ConnectClient(NULL);
//after succesful initialization, set read/write mode to blocking message mode.
NewConn->SetMode(FALSE, TRUE);
NewConn->ClientProcessID = (ULONG)NewConn->m_hPipe; //temporary value
if (ConnListLock.Lock(INFINITE))
{
server.ConnManager.AddConnection((*NewConn));
NewConn->SendConnectionID();
ConnListLock.Unlock();
}
SendMessage(pWnd->m_hWnd, WM_CLIENT_ADDED, 0, 0);
}
//notify of server shut down
SendMessage(pWnd->m_hWnd, WM_SERVER_OFFLINE, 0, 0);
return (0);
}
void CNamedPipe::Create(LPCTSTR lpszName, DWORD dwOpenMode, DWORD dwPipeMode, DWORD dwMaxInstances, DWORD dwOutBufferSize,
DWORD dwInBufferSize, DWORD dwDefaultTimeOut, LPSECURITY_ATTRIBUTES lpSecurityAttributes)
{
//Validate our parameters
ASSERT(!IsOpen());
ASSERT(_tcslen(lpszName));
//the class encapsulates creating the pipe name, all that is required is
//a simple name for the pipe e.g. lpName = PJPIPE will create the pipe
//name "\\.\PIPE\PJPIPE"
CString sPipeName;
sPipeName.Format(_T("\\\\.\\PIPE\\%s"), lpszName);
m_hPipe = ::CreateNamedPipe(sPipeName, dwOpenMode, dwPipeMode, dwMaxInstances, dwOutBufferSize, dwInBufferSize, dwDefaultTimeOut, lpSecurityAttributes);
if (m_hPipe == INVALID_HANDLE_VALUE)
ThrowNamedPipeException();
}
客户:
int CServerConnection::Run(){
BOOL fSuccess = FALSE;
DWORD cbRead, cbToWrite, cbWritten, dwMode, dwEvent, dwError;
LPTSTR lpszPipename = TEXT("\\\\.\\pipe\\IlyaPipe");
// Try to open a named pipe; wait for it, if necessary.
while (1)
{
hConnPipe = CreateFile(
lpszPipename, // pipe name
GENERIC_READ | // read and write access
GENERIC_WRITE,
0, // no sharing
NULL, // default security attributes
OPEN_EXISTING, // opens existing pipe
0, // default attributes
NULL); // no template file
// Break if the pipe handle is valid.
if (hConnPipe != INVALID_HANDLE_VALUE)
{
// The pipe connected; change to message-read mode.
dwMode = PIPE_READMODE_MESSAGE;
fSuccess = SetNamedPipeHandleState(
hConnPipe, // 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 (0);
}
//get ID from server, for creating 'message from server' event
if (!ReadMessage())
return (0);
//create event name string for message from server event.
CString EventName;
EventName.Format(_T("ILYA_PIPE_SERVER_SENT_"));
EventName.AppendFormat(_T("%s"), m_ReplyBuff);
//register to the event signaling a message was sent from server to client. should be created by server.
hSendMsgEvent[1] = CreateEvent(NULL, TRUE, FALSE, EventName);
if (GetLastError() != ERROR_ALREADY_EXISTS)
return (0);
//register to the event signaling a message was sent from server to client. should be created by server.
EventName.Format(_T("ILYA_PIPE_CLIENT_SENT_"));
EventName.AppendFormat(_T("%s"), m_ReplyBuff);
m_hAlertServerEvent = CreateEvent(NULL, TRUE, FALSE, EventName);
if (GetLastError() != ERROR_ALREADY_EXISTS)
return (0);
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 (0);
//ExitInstance();
}
// 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 (0);
//ExitInstance();
}
}
//notify main window that connection to server established
CWnd* pWnd = AfxGetMainWnd();
ASSERT_VALID(pWnd);
SendMessage(pWnd->m_hWnd, WM_CONNECTION_ESTABLISHED, 0, 0);
//SendMessage(pWnd->m_hWnd, WM_CONNECTION_ESTABLISHED, 0, 0);
//message send and receive loop
while (1){
dwEvent = WaitForMultipleObjects(1, hSendMsgEvent, FALSE, INFINITE);
switch (dwEvent){
//The application wants to send a request.
case SEND_MSG_EVENT:
if (SendMsg2Server())
//set event notifing the server of sent message
SetEvent(m_hAlertServerEvent);
else
dwError = GetLastError();
ResetEvent(hSendMsgEvent[0]);
break;
//A message is waiting for you at the server
case RECEIVE_MSG_EVENT:
ReadMessage();
ResetEvent(hSendMsgEvent[1]);
break;
//Invalid return from wait
default :
ExitProcess(0);
}
}
//connectionStatus = true;
// Write the reply to the pipe.
//CloseHandle(hPipe);
return (0);
}
int CServerConnection::DisconnectFromServer()
{
return 0;
}
void CServerConnection::PackMessage(const uint8 ActionType, const uint8 DeviceAddress, const uint32 FileId,const uint32 Offset ){
msg.ActionType = ActionType;
msg.bSent = 1;
msg.bReady = 0;
msg.DeviceAddress = DeviceAddress;
msg.FileId = FileId;
msg.Offset = Offset;
}
BOOL CServerConnection::SendMsg2Server(){
BOOL fSuccess = FALSE;
DWORD cbWritten = 0, cbReplyBytes = 0, cbToWrite, dwMode;
CString MsgStr;
LPTSTR lpvMessage = TEXT("Default");
//MsgStr.Format(_T("%d*%d"), msg.FileId, msg.ActionType);
//cbToWrite = 2*sizeof(TCHAR)*(MsgStr.GetLength() + 1);
cbToWrite = (lstrlen(lpvMessage) + 1)*sizeof(TCHAR);
MsgStr.Format(TEXT("%d"), 1);
cbToWrite = sizeof(MsgStr);
dwMode = PIPE_READMODE_MESSAGE;
fSuccess = SetNamedPipeHandleState(
hConnPipe, // pipe handle
&dwMode, // new pipe mode
NULL, // don't set maximum bytes
NULL); // don't set maximum time
fSuccess = WriteFile(
hConnPipe, // pipe handle
MsgStr,
cbToWrite, // message length
&cbWritten, // bytes written
NULL);
return fSuccess;
}
//Read message from server
BOOL CServerConnection::ReadMessage(){
BOOL fSuccess = FALSE;
DWORD cbBytesRead = 0;
fSuccess = ReadFile(
hConnPipe, //pipe handle
(LPVOID)m_ReplyBuff, //Message buffer
BUFSIZE, //Maximal message size
&cbBytesRead, //actual bytes read
NULL);
return fSuccess;
}