I'm trying to write an IPC with named pipe.
The server code : http://pastebin.com/tHyAv0e0
The client code : http://pastebin.com/Qd0yGBca
My question is about the server. Following a SO user, i'm trying to use BindIoCompletionCallback() in the server code. The server consists of the following functions:
- print_last_error : print a readable message of the last error
- IocpThreadProc : the callback passed to BindIoCompletionCallback(), it calls ConnectNamedPipe() and ReadFile()
- server_new : creates the named pipe and try to connect to the other end of the pipe (that is, the client), creates an exit event and call BindIoCompletionCallback()
- server_del : should release the resources
- the main function which has an infinite loop which wait on the exit event to be signaled.
When the client connects, it send the message "salut, c'est le client !". I have set the buffer of ReadFile() to 5, to test the case where I have to call ReadFile() several times. I have the following output:
connection pending...
waiting for client...
** 0, 0
reading data
* ReadFile : 0
** 0, 5
msg:
reading data
** 0, 5
* ReadFile : 5
reading data
msg: , c'e
* ReadFile : 5
** 0, 5
msg: st le
reading data
* ReadFile : 5
** 0, 5
msg: clie
reading data
* ReadFile : 5
** 0, 4
msg: nt !~
reading data
IO_PENDING
** -1073741493, 0
reading data
unexpected error failed with error 109: Le canal de communication a ÚtÚ fermÚ.
WaitForSingleObject : 0
the lines beginning with **: it prints the arguments of the callback
the lines beginning with 'msg' : it prints the message of the buffer filled by Readfile
As the length of the message sent by the client is 24, I should normally get these 5 messages (each of them being of 5 char, except the last one, being of 4 char) :
salut
, c'e
st le
clie
nt !
but I can't have the first part of the messge (that is : "salut"). The callback is called when an I/O operation is complete, maybe for this first part. But I have not succeded in calling ReadFile() in a way to get the first part of the message. I have tried to call ReadFile() in the main loop of the main function, in a thread, in server_new(), etc... Everything except the correct way.
Does someone know what to do to fix this issue ?
thank you
your code containing huge count of fundamental errors. more exactly all code - one complete error
look at code snippet (in
IocpThreadProc
andserver_new
)char buf[READ_BUFSIZE]
- this is local variable in function. after you exit from function - this become arbitrary address in stack. so when read operation complete - this faster of all corrupt your stack or will be undefinded result. so this is error. you must pass not stack memory as read buffer or not exit from function until read operation completeyou pass
IocpThreadProc
as argument toReadFileEx
but you never wait in alertable state !
later you use
but bind file to IOCP and use APC completion (lpCompletionRoutine ) is mutually exclusive. if say you call
BindIoCompletionCallback
beforeReadFileEx(.., IocpThreadProc)
- you will got errorERROR_INVALID_PARAMETER
from NtReadFile source code:
your code "work" ony because you bind IOCP after call
ReadFileEx(.., IocpThreadProc)
. but what happens when read operation is completed ? the APC (forIocpThreadProc
) will be inserted to thread and packet queued to IOCP. soIocpThreadProc
will be called twice with same data for single operation. it called once only because you never wait in alertable state and not pop APC from thread.you embedded
OVERLAPPED
to Server - this is error. you must have uniqueOVERLAPPED
per every asynchronous I/O. more exactly you must define own class, which inherit fromOVERLAPPED
. have in this class pointer to Server, operation code, may be some additional data. you need allocate this struct before every I/O operation and free it in completion.GetLastError()
inIocpThreadProc
!!!you need use
DWORD dwErrorCode
here,GetLastError()
no sense because here on another thread called, absolte unrelated to operation. and becase this is callback from kernel here reallyNTSTATUS
values is in dwErrorCode, but not win32 errors. say for example on read complete you can gotSTATUS_PIPE_BROKEN
but notERROR_BROKEN_PIPE
but this already big defect in MSDN docscode example:
and about
DWORD dwErrorCode
inin
BindIoCompletionCallback
documentation exist unclaritywhat is mean under The value returned is an NTSTATUS error code ? what return value ?
this is
DWORD dwErrorCode
inFileIOCompletionRoutine
really we pass to kernel mode pointer to
IO_STATUS_BLOCK
(first 2 members ofOVERLAPPED
isIO_STATUS_BLOCK
actually). when asynchronous operation complete - kernel fillIO_STATUS_BLOCK
and queue packet to IOCP (or APC to Thread). ntdll extractPIO_STATUS_BLOCK
from IOCP (so we got back pointer to ourOVERLAPPED
passed to I/O api), and fillsystem not do conversion
dwErrorCode = RtlNtStatusToDosError(Iosb->Status)
but direct assign
NTSTATUS
toDWORD dwErrorCode
- so inFileIOCompletionRoutine
we must comparedwErrorCode
not with wi32 error codes but with NTSTATUS codes (from"ntstatus.h"
)so we never seen
ERROR_BROKEN_PIPE
orERROR_PIPE_NOT_CONNECTED
in FileIOCompletionRoutine, butSTATUS_PIPE_BROKEN
orSTATUS_PIPE_DISCONNECTED
and code example by using new Thread Pool API instead
BindIoCompletionCallback
. here big advantage that inIoCompletionCallback
(PTP_WIN32_IO_CALLBACK
) callback function in placeULONG IoResult
already used win32 error, but not raw NTSTATUS (IoResult = RtlNtStatusToDosError(Iosb->Status)
and noteULONG_PTR NumberOfBytesTransferred
(vsULONG dwNumberOfBytesTransfered
fromFileIOCompletionRoutine
(LPOVERLAPPED_COMPLETION_ROUTINE
) callback function and compare this withULONG_PTR Information
fromIO_STATUS_BLOCK
. )