Author | Message | Time |
---|---|---|
tA-Kane | I'm trying to write a DLL (BCP actually, but that's not really important) which creates and uses sockets. I don't want it to hog the owning executable's processing time, so I of course would rather use asynchronous calls. But from the looks, they require a window to be passed to them in order to receive events such as data available. I would rather not create a window (it's a DLL, it shouldn't need an interface!), so how might I go around this? | January 25, 2005, 9:08 AM |
Kp | Easy, don't use those functions! In more detail: you could use overlapped I/O or completion ports (but OIO works poorly on Win9x and completion ports don't exist there). You could create an extra thread and do blocking reads (though that's a somewhat inelegant solution). IIRC, BCPs are guaranteed that the host process will periodically execute alertable sleeps, which have as a side effect that any completed overlapped operations are delivered to you. So, I would recommend overlapped I/O for your case. Look up ReadFile(Ex), WriteFile(Ex), WSASend, WSARecv. | January 25, 2005, 9:30 PM |
tA-Kane | hmmk thx Edit: With overlapped I/O, would I still need to use select() (or a variant thereof) to know that data is ready to be received with WSARecv()? If so, how would that *not* hog the owning processes' CPU time? If not, how am I informed that the socket has data available to be received? Also with overlapped I/O, suppose I am using multiple sockets. From the looks of the completion routines, there is no way of telling which socket completed the WSARecv(). Except, it looks like if there *is* a completion routine specified, then the hEvent of the lpOverlapped which is passed to WSARecv is unused. Would using that to hold a pointer to the socket be a good idea? | January 25, 2005, 9:35 PM |
Kp | You don't need select, which is good. Microsoft got the implementation of it all wrong anyway. Yes, you can pass the socket in the hEvent of the OVERLAPPED structure. Alternately, you could create your own structure which has OVERLAPPED as a member; the OS does not copy/move the structure you supply, which means you must ensure it stays valid (i.e. not on the stack of a routine that will return before I/O completes!). Also, it grants you the ability to make assumptions about where your other data structures are in relation to the OVERLAPPED that the OS gives your callback. | January 26, 2005, 12:12 AM |
tA-Kane | [quote author=tA-Kane link=topic=10300.msg96611#msg96611 date=1106688908]With overlapped I/O, would I still need to use select() (or a variant thereof) to know that data is ready to be received with WSARecv()? If not, how am I informed that the socket has data available to be received?[/quote][quote author=Kp link=topic=10300.msg96653#msg96653 date=1106698324]You don't need select, which is good. Microsoft got the implementation of it all wrong anyway.[/quote]Could you answer my question a little more in depth? I'm still failing to understand how my DLL will be informed of when the socket is ready to call WSARecv(). | January 26, 2005, 2:22 AM |
Kp | You call WSARecv as soon as the socket could possibly have data, thereby turning loose an overlapped read which will complete when the socket finally has data (which could be immediately or it could take hours). When the receive completes, you'll be notified during the next alertable sleep. If you need more data from the socket, your completion routine should call WSARecv again to start another asynchronous read to fetch more data. | January 26, 2005, 3:44 PM |
Arta | ("notified" == the system calls your completion routine) | January 27, 2005, 2:12 PM |
tA-Kane | Thanks Arta. I have one more (possibly my last) question; can I reuse the WSAOVERLAPPED structure from the completion routine? That is, can I pass it in my initial call to WSARecv() or WSASend(), and then pass it again to another call to WSARecv() or WSASend() from within the completion routine of such? If not, then what would strategy would you recommend to easily keep track of an arbitrary number of WSAOVERLAPPED structures? | January 27, 2005, 10:46 PM |
Kp | Yes, you can reuse it. Once the OS calls your completion routine, it's done with that OVERLAPPED and you can have it back to be reused or deallocated as you see fit. Just remember you need one OVERLAPPED per outstanding I/O operation (i.e. one per socket). | January 28, 2005, 2:37 AM |
tA-Kane | Actually, you'd need no less than two per socket; one to send data, another to simultaneously receive data, and possibly a third for other simultaneous miscellaneous overlapped I/O with the same socket. That's excellent news, it'll simplify things greatly! :) Edit: Hmm, I do have one more question: [quote]The array of WSABUF structures pointed to by the lpBuffers parameter is transient. If this operation is completed in an overlapped manner, it is the service provider's responsibility to capture these WSABUF structures before returning from this call. This enables applications to build stack-based WSABUF arrays.[/quote]Would that indicate that the data pointed to by buf is "captured" by the service provider as well? Edit2: It seems as the the answer to my question is yes... but it would still be nice to have someone else confirm (or deny) it. As for your earlier statement: [quote author=Kp link=topic=10300.msg96610#msg96610 date=1106688616]IIRC, BCPs are guaranteed that the host process will periodically execute alertable sleeps, which have as a side effect that any completed overlapped operations are delivered to you.[/quote]It seems that is not so true in the case of SphtBotv3. I was not getting my callback routine called, so I installed a timer which executes every half of a second and calls SleepEx(0, TRUE), and suddenly ... my completion routine is called! On a different (rather sad) note, it seems that if you load the DLL, call WSASend() with a completion routine installed, call WSACleanup(), unload and reload the DLL, and reinitialize WinSock with WSAStartup(), then the subsequent call to WSAConnect() results in the completion routine from the previous instance getting called. That's a major fuckup on Windows' part, I think. | January 28, 2005, 3:30 AM |
Kp | In order: I don't use that family of calls, so I don't know. I prefer to do the work with WriteFile / WriteFileEx. That's an SphtBotv3 bug (unless I misremember the BCP spec., which is possible - atm, I can't find anything about whether it's guaranteed or not). Re: previous completion routine: that's arguable. Try canceling the overlapped operation before you unload your DLL (use CancelIo), which will cause your completion routine to be called with a notice that the operation was aborted. After that, you can safely unload your DLL. Of course, if SphtBot doesn't provide a way to let you refuse/defer an unload, you're still stuck. :) | January 28, 2005, 11:00 PM |
tA-Kane | Do you use WriteFile(Ex) with sockets? Even without calling CancelIo, called WSACleanup() should close all sockets and in doing so, cancel all operations on those sockets. If there's any pending completion routines, it should call them before returning, since it should basically be deinitializing the WinSock library, which should allow for zero WinSock use in that context's address space. | January 28, 2005, 11:37 PM |
Kp | Yes, I do. It might be closing the handles (which would indeed interrupt/cancel the routine), but it might not be doing an alertable wait. Try just adding a SleepEx(0, 1); after the WSACleanup. | January 29, 2005, 2:42 AM |
Adron | [quote author=tA-Kane link=topic=10300.msg97167#msg97167 date=1106955457] Even without calling CancelIo, called WSACleanup() should close all sockets and in doing so, cancel all operations on those sockets. If there's any pending completion routines, it should call them before returning, since it should basically be deinitializing the WinSock library, which should allow for zero WinSock use in that context's address space. [/quote] Are you using this in an application you control entirely yourself or is it possible that the program loading your DLL, or some DLL that it has loaded for other reasons has also called WSAStartup? Multiple calls would mean that it's actually not uninitialized when you think it is. | January 29, 2005, 9:11 AM |
tA-Kane | Hmm... I am using this in a Battle.net client (are BCPs used anywhere else though?), so I assume it's already called WSAStartup(). I would think that being a DLL, I would get my own address space and as such, would also need to initialize WinSock before calling any WinSock functions. ...Unless WinSock only needs to be initialized for separate threads, in which case, I don't think my host application (SphtBotv3) creates separate threads for each plugin loaded. | January 29, 2005, 9:53 AM |
Adron | You don't get your own address space as a DLL; you share address space with the rest of the process. And Winsock only needs to be initialized once for the entire process. | January 29, 2005, 9:58 AM |
tA-Kane | Probably a dumb question, but can anything *bad* happen from nested calls to WSAStartup()/WSACleanup? My DLL seems to be working fine right now, for how far I've implemented it (it's able to connect to a server and send data; packetlogged it so I know it's working). | January 29, 2005, 10:21 AM |
Kp | My understanding is that WinSock must be explicitly initialized on a per-process basis (oppose the Unix model, where socket libraries don't require any warning before you use them). So the only case in which you might need to call WSAStartup is if you can get loaded before SphtBot performs its WSAStartup (which it must do before connecting to battle.net). :) As for nesting: I think it just increments/decrements a reference counter, so no, nothing bad will happen as long as you're careful to call WSACleanup exactly as many times as you called WSAStartup. Calling it too many times might cut off the host process's use of winsock prematurely. | January 29, 2005, 4:27 PM |