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
IocpThreadProcandserver_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
IocpThreadProcas argument toReadFileExbut 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
BindIoCompletionCallbackbeforeReadFileEx(.., IocpThreadProc)- you will got errorERROR_INVALID_PARAMETERfrom 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. soIocpThreadProcwill 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
OVERLAPPEDto Server - this is error. you must have uniqueOVERLAPPEDper 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 dwErrorCodehere,GetLastError()no sense because here on another thread called, absolte unrelated to operation. and becase this is callback from kernel here reallyNTSTATUSvalues is in dwErrorCode, but not win32 errors. say for example on read complete you can gotSTATUS_PIPE_BROKENbut notERROR_BROKEN_PIPEbut this already big defect in MSDN docscode example:
and about
DWORD dwErrorCodeinin
BindIoCompletionCallbackdocumentation exist unclaritywhat is mean under The value returned is an NTSTATUS error code ? what return value ?
this is
DWORD dwErrorCodeinFileIOCompletionRoutinereally we pass to kernel mode pointer to
IO_STATUS_BLOCK(first 2 members ofOVERLAPPEDisIO_STATUS_BLOCKactually). when asynchronous operation complete - kernel fillIO_STATUS_BLOCKand queue packet to IOCP (or APC to Thread). ntdll extractPIO_STATUS_BLOCKfrom IOCP (so we got back pointer to ourOVERLAPPEDpassed to I/O api), and fillsystem not do conversion
dwErrorCode = RtlNtStatusToDosError(Iosb->Status)but direct assign
NTSTATUStoDWORD dwErrorCode- so inFileIOCompletionRoutinewe must comparedwErrorCodenot with wi32 error codes but with NTSTATUS codes (from"ntstatus.h")so we never seen
ERROR_BROKEN_PIPEorERROR_PIPE_NOT_CONNECTEDin FileIOCompletionRoutine, butSTATUS_PIPE_BROKENorSTATUS_PIPE_DISCONNECTEDand code example by using new Thread Pool API instead
BindIoCompletionCallback. here big advantage that inIoCompletionCallback(PTP_WIN32_IO_CALLBACK) callback function in placeULONG IoResultalready used win32 error, but not raw NTSTATUS (IoResult = RtlNtStatusToDosError(Iosb->Status)and noteULONG_PTR NumberOfBytesTransferred(vsULONG dwNumberOfBytesTransferedfromFileIOCompletionRoutine(LPOVERLAPPED_COMPLETION_ROUTINE) callback function and compare this withULONG_PTR InformationfromIO_STATUS_BLOCK. )