Every now and then I find myself asking if ANYONE has written a WORKING iocp server in asm, not matter how simple it may be.
I guess it's that time again :)
I can't seem to get even a basic iocp to work, and I suspect that my equate for WSA_FLAG_OVERLAPPED is wrong (I'm led to believe it's ==1).
Sending out an SOS :lol:
I guess it's that time again :)
I can't seem to get even a basic iocp to work, and I suspect that my equate for WSA_FLAG_OVERLAPPED is wrong (I'm led to believe it's ==1).
Sending out an SOS :lol:
patform SDK, WINSOCK2.h
/*
* WinSock 2 extension -- manifest constants for WSASocket()
*/
#define WSA_FLAG_OVERLAPPED 0x01
as for the rest - I don't know anything :P
/*
* WinSock 2 extension -- manifest constants for WSASocket()
*/
#define WSA_FLAG_OVERLAPPED 0x01
as for the rest - I don't know anything :P
EvilHomer2k, have you seen the FASt Server project? It is an interesting program that implements IOCP.
You can find information about that project in http://www.jamesladdcode.com (or in the masm forum too)
Kecol.-
You can find information about that project in http://www.jamesladdcode.com (or in the masm forum too)
Kecol.-
Thanks, I'll take a peek :)
This is perfect. I'm sure to see what I was doing wrong.
Thanks a bunch man 8)
Thanks a bunch man 8)
You are welcome.
Regards.
Kecol.-
Regards.
Kecol.-
EvilHomer2k,
Ill help if you want. Send me some code.
Rgs, James.
Ill help if you want. Send me some code.
Rgs, James.
James - I'm developing an iocp framework for the ObjAsm32 dev team.
Do you think you can stomach the source? It looks pretty weird at first :)
I'm also developing a banked socket framework using the Event objects model (the class defines a bank of 64 sockets plus selfcontained workerthread and abstract-static event handlers) as well as the various utility classes like Socket which are common to both socket models.
The banked socket framework currently does not use overlapped io because I see it as redundant under that model - can you change my mind?
If you still want to see the source and/or contribute, I welcome you aboard - PM me :)
If not, I understand completely, perhaps in time you will change your view regarding oopasm.
Do you think you can stomach the source? It looks pretty weird at first :)
I'm also developing a banked socket framework using the Event objects model (the class defines a bank of 64 sockets plus selfcontained workerthread and abstract-static event handlers) as well as the various utility classes like Socket which are common to both socket models.
The banked socket framework currently does not use overlapped io because I see it as redundant under that model - can you change my mind?
If you still want to see the source and/or contribute, I welcome you aboard - PM me :)
If not, I understand completely, perhaps in time you will change your view regarding oopasm.
EvilHomer2k,
Ill PM you about the oopasm stuff, but for the sake of others maybe you would like to explain
what you mean by banked sockets?
Rgs, james
Ill PM you about the oopasm stuff, but for the sake of others maybe you would like to explain
what you mean by banked sockets?
Rgs, james
Sure.. I'll explain it in 1000 words or less :)
Let's suppose we are using eventobject-driven socket model.
We know we can only monitor 64 sockets per worker thread (hanging on WSAGetMultipleEvents).
A bank of sockets is an array which stores up to 64 sockets.
In my case, I created Socket and SocketBank classes.
Each instance of SocketBank (an instance is an Object) manages two arrays of 64 elements.
One array contains pointers to instances of Socket object (contains hSocket and other stuff), and the other array contains hEvent handles which have been associated with the Socket whose pointer is stored at the same index,different array.
SocketBank ensures that the active elements (especially in the event handles array) are always stored in the lowest possible indices in the arrays, so that we basically have at least got an array of event handles which are A)associated with sockets and B)all valid, and together at the bottom of the array.
Now we can call WSAGetMultipleObjects on the event handles array, telling it to monitor N handles.
SocketBank instances each have their own WorkerThread, which can be paused and killed via event objects.
Just because I can, I defined the actual socketevent handler methods (procs) as "abstract methods" in SocketBank class... then derived two new classes from SocketBank called SocketClients and SocketServers.
SocketServers stores up to 64 listening server sockets.
Each of these owns an instance of SocketClients in which it stores Accepted clients.
So we have a (SocketServers) SocketBank worth of (Listening) Sockets which each own a (SocketClients) SocketBank worth of Client sockets.
We can handle 64 servers, and 64*64 clients, with a single instance of SocketServers.
Of course, these are artificial limitations imposed only by the implementation (you can make more instances, use an array or linkedlist of socketbanks to manage them, etc).
I should point out that whenever socket events are not occuring, the WorkerThread(s) are asleep and consume no cpu time and create no context switches.
Have a nice day :)
Let's suppose we are using eventobject-driven socket model.
We know we can only monitor 64 sockets per worker thread (hanging on WSAGetMultipleEvents).
A bank of sockets is an array which stores up to 64 sockets.
In my case, I created Socket and SocketBank classes.
Each instance of SocketBank (an instance is an Object) manages two arrays of 64 elements.
One array contains pointers to instances of Socket object (contains hSocket and other stuff), and the other array contains hEvent handles which have been associated with the Socket whose pointer is stored at the same index,different array.
SocketBank ensures that the active elements (especially in the event handles array) are always stored in the lowest possible indices in the arrays, so that we basically have at least got an array of event handles which are A)associated with sockets and B)all valid, and together at the bottom of the array.
Now we can call WSAGetMultipleObjects on the event handles array, telling it to monitor N handles.
SocketBank instances each have their own WorkerThread, which can be paused and killed via event objects.
Just because I can, I defined the actual socketevent handler methods (procs) as "abstract methods" in SocketBank class... then derived two new classes from SocketBank called SocketClients and SocketServers.
SocketServers stores up to 64 listening server sockets.
Each of these owns an instance of SocketClients in which it stores Accepted clients.
So we have a (SocketServers) SocketBank worth of (Listening) Sockets which each own a (SocketClients) SocketBank worth of Client sockets.
We can handle 64 servers, and 64*64 clients, with a single instance of SocketServers.
Of course, these are artificial limitations imposed only by the implementation (you can make more instances, use an array or linkedlist of socketbanks to manage them, etc).
I should point out that whenever socket events are not occuring, the WorkerThread(s) are asleep and consume no cpu time and create no context switches.
Have a nice day :)
Homer,
Thats an interesting approach.
Is there one thread per socketbank or one thread per connection ?
Do oyu change the handles in teh array based on the type of events you expect ?
ie: not all handles would be expecting an accept.
rgs, james
Thats an interesting approach.
Is there one thread per socketbank or one thread per connection ?
Do oyu change the handles in teh array based on the type of events you expect ?
ie: not all handles would be expecting an accept.
rgs, james
Forking per clientsock is a horribly inefficient approach, and begging to be resource-DoS'd.
One thread per socketbank.. the thread is actually written as "part of socketbank", so when we create an instance of socketbank, we automagically create an instance of the worker which is solely responsible for the 0-64 sockets in that bank. All these Workers can be individually paused and resumed and terminated from outside the thread.
Each socket in the bank has its own event object and is uniquely associated with any combination of the FD_XXX events.
You can decide when you create/add sockets to a bank, or at any later time, what events will trigger each eventobject.
Here's today's posting from the OA developers private board:
Progress Report:
Today I wrote a replacement for SocketServers class, which if you recall derives from SocketBank and acts as a container for 64 Listening Server Sockets, and also features a child instance of SocketClients for 64 Accepted Client Sockets.
The new version is called MyServer.
The difference is that MyServer doesn't have a child instance of SocketClients class. Instead, it has an instance of LinkedList, which stores any number of instances of SocketClients container objects, which each have their own Worker thread.
Theoretically speaking, one instance of MyServer is capable of listening on up to 64 ports simultaneously, and can handle infinite clients on each.
Have a nice day
One thread per socketbank.. the thread is actually written as "part of socketbank", so when we create an instance of socketbank, we automagically create an instance of the worker which is solely responsible for the 0-64 sockets in that bank. All these Workers can be individually paused and resumed and terminated from outside the thread.
Each socket in the bank has its own event object and is uniquely associated with any combination of the FD_XXX events.
You can decide when you create/add sockets to a bank, or at any later time, what events will trigger each eventobject.
Here's today's posting from the OA developers private board:
Progress Report:
Today I wrote a replacement for SocketServers class, which if you recall derives from SocketBank and acts as a container for 64 Listening Server Sockets, and also features a child instance of SocketClients for 64 Accepted Client Sockets.
The new version is called MyServer.
The difference is that MyServer doesn't have a child instance of SocketClients class. Instead, it has an instance of LinkedList, which stores any number of instances of SocketClients container objects, which each have their own Worker thread.
Theoretically speaking, one instance of MyServer is capable of listening on up to 64 ports simultaneously, and can handle infinite clients on each.
Have a nice day
James, I decided to experiment a bit with fastsvr, and some interesting "bug" probably came out. (I don't know much or anything about networking, so maybe it isn't a bug)
I run fastsvr.exe, then in Firefox1.04 type as address
localhost:9080
fastsvr's "echo" plugin shows the "GET ...." http headers correctly - twice . Doing a "refresh" doesn't cause any more echoes. But when I mark in Firefox the address, and press Enter again, fastsvr starts printing the header infinitely, at speed of >300lines/s . Exiting Firefox doesn't cease that loop of printing in fastsvr.
This "bug" comes up every time I do that sequence.
Also, I just noticed this also happens if I just quit Firefox after making the first query to localhost:9080 .
But maybe this report is ridiculous ^^"
P.S. I use Win2k SP4
I run fastsvr.exe, then in Firefox1.04 type as address
localhost:9080
fastsvr's "echo" plugin shows the "GET ...." http headers correctly - twice . Doing a "refresh" doesn't cause any more echoes. But when I mark in Firefox the address, and press Enter again, fastsvr starts printing the header infinitely, at speed of >300lines/s . Exiting Firefox doesn't cease that loop of printing in fastsvr.
This "bug" comes up every time I do that sequence.
Also, I just noticed this also happens if I just quit Firefox after making the first query to localhost:9080 .
But maybe this report is ridiculous ^^"
P.S. I use Win2k SP4
I didn't even get it to echo - but I have faith :)
I did get it to hang on accept, so thats something :P
I did get it to hang on accept, so thats something :P
Fastserv crashes when I try to run it. I'll debug when I have time, but I don't particularly like all the junk he has in the license.
Thanks for the comments.
Bug reports are welcome. Im looking into all of them.
I have updated the site with a plan and hopefully this will appeal.
FAStServer is a work in progress, so expect some changes and some bugs. However,
I do expect to have a plugin example which should stay constant regardless of the
changes in the server. So you should be able to confidently build a plugin and not
worry about the driving server. This will improve over time without requiring the
plugin to change. Which is what I would want.
If you have issues with the license then please let me know what they are.
Ultrano - The reason you get the headers twice is because when the acceptex is done, it also gets a buffer of data. Which is the
http get request. The second request isnt a new request but the first read. This is just done as an example as FASt Server is a
work in progress.
Thankyou for your support and comments.
rgs James.
Bug reports are welcome. Im looking into all of them.
I have updated the site with a plan and hopefully this will appeal.
FAStServer is a work in progress, so expect some changes and some bugs. However,
I do expect to have a plugin example which should stay constant regardless of the
changes in the server. So you should be able to confidently build a plugin and not
worry about the driving server. This will improve over time without requiring the
plugin to change. Which is what I would want.
If you have issues with the license then please let me know what they are.
Ultrano - The reason you get the headers twice is because when the acceptex is done, it also gets a buffer of data. Which is the
http get request. The second request isnt a new request but the first read. This is just done as an example as FASt Server is a
work in progress.
Thankyou for your support and comments.
rgs James.
It hadn't occurred to me until now, but is worth mentioning.
The banked model currently doesn't have any buffering system !!!
If we just think about a single Bank of sockets, we can say that socket operations on that Bank are synchronous, since they are being executed by a single worker thread.
Therefore it's reasonable to use a shared read buffer for all sockets in a Bank.
In fact, my receive buffer is a procedural local, so each receive indeed has its own buffer, allocated on the stack as part of the procedure's stack frame.
What I intend to do next is implement a perclient dynamic buffer (via LinkedList) instead of the crude but effective method I am currently using, so that I can handle "sandwich" and "incomplete" packets correctly, but I'm still trying to decide which way to implement Client.
As I see it, there's two flavours of Client - incoming and outgoing.
I'll probably end up with a ClientBank replacing the existing SocketBank - the SocketPointers array becomes a ClientPointers array, and the methods are modified to suit.
I'm actually ready to implement protocol plugins now, and again I'm unsure exactly how I intend to go about it.. I'm trying to identify a set of generic functions which would be common to any protocol handler, including "HaveCompleteProtocolMessage", but this leans towards the protocol handler being the "top class" which is a departure from the notion of "plugin code", and as such, I am unhappy with it :(
This is a "living project" - I have no example apon which to base this work, which makes it interesting... I'm deliberately trying not to mimick the standard example behaviours of asynchronous servers.
James - your license is hilarious - I love it - "this is free material - if you paid for software based on this material then you breach this agreement and have no right to use that software" - is how I interpret it.
Kick me if I'm wrong :)
The banked model currently doesn't have any buffering system !!!
If we just think about a single Bank of sockets, we can say that socket operations on that Bank are synchronous, since they are being executed by a single worker thread.
Therefore it's reasonable to use a shared read buffer for all sockets in a Bank.
In fact, my receive buffer is a procedural local, so each receive indeed has its own buffer, allocated on the stack as part of the procedure's stack frame.
What I intend to do next is implement a perclient dynamic buffer (via LinkedList) instead of the crude but effective method I am currently using, so that I can handle "sandwich" and "incomplete" packets correctly, but I'm still trying to decide which way to implement Client.
As I see it, there's two flavours of Client - incoming and outgoing.
I'll probably end up with a ClientBank replacing the existing SocketBank - the SocketPointers array becomes a ClientPointers array, and the methods are modified to suit.
I'm actually ready to implement protocol plugins now, and again I'm unsure exactly how I intend to go about it.. I'm trying to identify a set of generic functions which would be common to any protocol handler, including "HaveCompleteProtocolMessage", but this leans towards the protocol handler being the "top class" which is a departure from the notion of "plugin code", and as such, I am unhappy with it :(
This is a "living project" - I have no example apon which to base this work, which makes it interesting... I'm deliberately trying not to mimick the standard example behaviours of asynchronous servers.
James - your license is hilarious - I love it - "this is free material - if you paid for software based on this material then you breach this agreement and have no right to use that software" - is how I interpret it.
Kick me if I'm wrong :)
Homer,
No kick from me.
:)
No kick from me.
:)
I was wondering if using the NamedPipe api's would be an appropriate solution for thread pooling in a webserver type situation?
Flow chart following ...
-Create NamedPipe - Becomes WRITE Server
-Create Listen/Accept and Worker threads
-Loop Window Msg Handling
>
--WRITE WSAAsyncSelect window messages to NamedPipe
>>
---Loops listening for connections on port (???)
---Accept new connections AND use WSAAsyncSelect API to send a WindowMsg on FD_READABLE
>>
---Block READING WSAAsyncSelect window messages from Named Pipe
---Recv from socket in message and perform the request
//I apologize for using the flow chart
If not a named pipe even a Thread Safe Queue would work have the windows procedure push the messages onto the Queue and the worker threads pop the messages off.
WorkerThreadProc:
push ebp
mov ebp,esp
sub esp,16
.LoopOne:
push 0
push 0
CALL SleepEx ;put thread in low cpu loop if IsQEmpty keeps returning TRUE
CALL IsQEmpty
TEST eax,eax
JNZ .LoopOne
...call QPop / get the socket handle / Recv from socket / Process data recv'd
Flow chart following ...
-Create NamedPipe - Becomes WRITE Server
-Create Listen/Accept and Worker threads
-Loop Window Msg Handling
>
--WRITE WSAAsyncSelect window messages to NamedPipe
>>
---Loops listening for connections on port (???)
---Accept new connections AND use WSAAsyncSelect API to send a WindowMsg on FD_READABLE
>>
---Block READING WSAAsyncSelect window messages from Named Pipe
---Recv from socket in message and perform the request
//I apologize for using the flow chart
If not a named pipe even a Thread Safe Queue would work have the windows procedure push the messages onto the Queue and the worker threads pop the messages off.
WorkerThreadProc:
push ebp
mov ebp,esp
sub esp,16
.LoopOne:
push 0
push 0
CALL SleepEx ;put thread in low cpu loop if IsQEmpty keeps returning TRUE
CALL IsQEmpty
TEST eax,eax
JNZ .LoopOne
...call QPop / get the socket handle / Recv from socket / Process data recv'd
re Piping : my first impression is not good.
You are just adding a "notification mechanism" to something that already supports direct callbacks !!
re pseudocode for WorkerThread :
The thread should always use one of the various Wait functions to wait for network events.
In the case of IOCP, the GetQueuedCompletionStatus function is provided.
This function effectively puts the thread to sleep pending completion of a queued IO event, and I quote :
That is to say, if theres nothing queued, it will immediately return with error.
If theres events queued, but not completed, it will BLOCK until something completes, or until the specified timeout elapses (which can be infinite).
If theres at least one completed event, it will return some data regarding that event.
Using the underlying operating system's mechanisms for notification of completed events (and to perform a blocking poll) simply has to be the most efficient option.
You are just adding a "notification mechanism" to something that already supports direct callbacks !!
re pseudocode for WorkerThread :
The thread should always use one of the various Wait functions to wait for network events.
In the case of IOCP, the GetQueuedCompletionStatus function is provided.
This function effectively puts the thread to sleep pending completion of a queued IO event, and I quote :
If there is no completion packet queued, the function waits for a pending input/output operation associated with the completion port to complete
That is to say, if theres nothing queued, it will immediately return with error.
If theres events queued, but not completed, it will BLOCK until something completes, or until the specified timeout elapses (which can be infinite).
If theres at least one completed event, it will return some data regarding that event.
Using the underlying operating system's mechanisms for notification of completed events (and to perform a blocking poll) simply has to be the most efficient option.