Thread: Re-enabling functions

  1. #1
    Carnivore ('-'v) Hunter2's Avatar
    Join Date
    May 2002
    Posts
    2,879

    Re-enabling functions

    I am dealing with asynchronous WinSock using WSAEventSelect(). According to MSDN, once certain network events have been recorded, they will not be recorded again until the re-enabling functions are called.

    However, I have observed that FD_READ is posted multiple times on the same socket without my ever calling any of the recv() variants. Specifically, what I am doing is connecting to a server I'm writing, using a telnet client. I accept the connection and immediately call WSAEventSelect() to associate the client socket with an event object and all network events. FD_WRITE is posted on the client socket. I then type something in the telnet client, and immediately a FD_READ event is posted on the client; I do NOT call any variant of recv() on the client socket. Each time I type something into telnet, I receive another FD_READ.

    Has anyone else observed this behaviour? It seems to me that MSDN almost explicitly states that this should not happen.
    Just Google It. √

    (\ /)
    ( . .)
    c(")(") This is bunny. Copy and paste bunny into your signature to help him gain world domination.

  2. #2
    Yes, my avatar is stolen anonytmouse's Avatar
    Join Date
    Dec 2002
    Posts
    2,544
    Are you using a manual-reset event or an auto-reset event? A manual-reset event, once signalled, will stay signalled until explicitly put into the non-signalled state with the ResetEvent, WSAResetEvent or WSAEnumNetworkEvents functions. An auto-reset event will be placed in the non-signalled state when it satisifies a wait function (eg. WaitForSingleObject). WSACreateEvent creates a manual-reset event. To create an auto-reset event you can use the CreateEvent function. WSAEVENTs and normal events are equivalent.

    If this is not the cause, could you post some code?

  3. #3
    Carnivore ('-'v) Hunter2's Avatar
    Join Date
    May 2002
    Posts
    2,879
    I've tried both, and I also pass the event as a parameter to WSAEnumNetworkEvents. Also, I don't get the endless-loop sort of thing happening, the message only appears once for each time I type something. I'll post some code when I get home later.

    It wouldn't be such a big deal to just run with it and process each FD_READ message as they come, but I'm probably going to be reusing this code a lot in the future and I don't want unexpected things happening all over the place.
    Just Google It. √

    (\ /)
    ( . .)
    c(")(") This is bunny. Copy and paste bunny into your signature to help him gain world domination.

  4. #4
    Carnivore ('-'v) Hunter2's Avatar
    Join Date
    May 2002
    Posts
    2,879
    Alright, well, there's a lot of code.. but here's what I believe is important:
    (part of AESocket class definition.. all member function definitions are here for the time being)
    Code:
    struct SockInfo
    {
    	SockInfo() :desc(INVALID_SOCKET), notify(false, false), defEventHandler(NULL)
    	{
    		for(int i = 0; i < FD_MAX_EVENTS; ++i)
    			eventHandlers[i] = NULL;
    	}
    	SOCKET desc;
    	Event notify;  //Event - simple wrapper. constructor: bool manualReset, bool initialState
    
    	//Callback routines, for each network event, and a user-specified 'default' handler
    	void (*eventHandlers[FD_MAX_EVENTS])(AESocket, int);
    	void (*defEventHandler)(AESocket, int, int);
    };
    RCPtr<SockInfo> si;  //Generic reference-counted pointer class I developed earlier
    
    void handleEvents()
    {
    	if(si->notify.poll())  //Poll the event object associated with the socket
    	{
    		MB("handleEvents(): Event found to be set.");  //MB is a macro for MessageBox()
    
    		WSANETWORKEVENTS n;
    		int res = WSAEnumNetworkEvents(si->desc, (HANDLE)(si->notify), &n);
    		if(res == SOCKET_ERROR)
    		{
    			MERR("handleEvents(): WSAEnumNetworkEvents error.");
    			return;
    		}
    		for(int i = 0; i < FD_MAX_EVENTS; ++i)
    		{
    			if(n.lNetworkEvents & (1 << i))
    			{
    				if(si->eventHandlers[i] != NULL)
    					si->eventHandlers[i](si, n.iErrorCode[i]);
    				else if(si->defEventHandler != NULL)
    					si->defEventHandler(si, (1 << i), n.iErrorCode[i]);
    			}
    		}
    	}
    }
    In main(), when accepting a new socket:
    Code:
    //v is a std::vector<AESocket>
    v.push_back(s.accept(&clientCallback));
    v.back().setEventHandler(FD_READ_BIT, &readCallback);
    v.back().setEventHandler(FD_WRITE_BIT, &writeCallback);
    readCallback() is defined as:
    Code:
    void readCallback(AESocket s, int err)
    {
    	MB("Got data to read!");
    }
    And I have a polling loop, which I plan in the future to change:
    Code:
    	for(;;)
    	{
    		MB("Listener");
    		s.handleEvents();  //s is the listening socket
    		MB("Client");
    		for(int i = 0; i < v.size(); ++i) //At this time, there should only ever be 1 element
    			v[i].handleEvents();
    
    		Sleep(1);
    	}
    I apologize for all the gaps in the code, but there's a lot of code and it's all uncommented, and it's somewhat difficult to pick and choose what's relevant. I've attached the full source too if you're willing (or interested) to go through it, though I know it's a big favour to ask. I've tested *successfully* as far as listen() and accept() on the server socket, which seem to work as expected.

    To reproduce the problem, compile and run the program, open telnet and connect to localhost, click through the messageboxes until it loops steadily between "client" and "listener", and then type something into telnet and begin clicking through the messageboxes again.

    Thanks in advance!
    Just Google It. √

    (\ /)
    ( . .)
    c(")(") This is bunny. Copy and paste bunny into your signature to help him gain world domination.

  5. #5
    Yes, my avatar is stolen anonytmouse's Avatar
    Join Date
    Dec 2002
    Posts
    2,544
    It seems that the documentation is incorrect. WSAEnumNetworkEvents seems to act as a reenabling function. If you comment it out the event is signalled only once. Nearly all applications will call recv or a variant when they get the FD_READ event. Therefore, this undocumented behaviour is unlikely to cause much harm. I couldn't find anything in usenet on it. I created a simplified mock up.
    Code:
    #include <winsock2.h>
    #include <windows.h>
    #include <stdio.h>
    #pragma comment(lib, "ws2_32.lib")
    
    int main(void)
    {
        WSADATA            wsad;
        SOCKET             sock, sock_client;
        struct sockaddr_in addr;
        HANDLE             hEvent            = CreateEvent(NULL, FALSE, FALSE, NULL);
    
        WSAStartup(MAKEWORD(2, 2), &wsad);
    
        sock = socket(AF_INET, SOCK_STREAM, IPPROTO_IP);
        
        addr.sin_family           = AF_INET;
        addr.sin_port             = htons(23);
        addr.sin_addr.S_un.S_addr = INADDR_ANY;
    
        bind(sock, (struct sockaddr*) &addr, sizeof(addr));
        listen(sock, SOMAXCONN);
    
        sock_client = accept(sock, NULL, NULL);
        WSAEventSelect(sock_client, hEvent, FD_READ);
    
        while (TRUE)
        {
            WSANETWORKEVENTS n = { 0 };
    
            WaitForSingleObject(hEvent, INFINITE);
    
            /* Comment out this line and we no longer get signalled. */
            WSAEnumNetworkEvents(sock_client, NULL, &n);
    
            printf("Event = %d\n", n.lNetworkEvents);
        }
    
        getchar();
        return 0;
    }
    If you'd like to get more information, try posting in the specialised usenet forum microsoft.public.win32.programmer.networks, or leave feedback on the WSAEventSelect documentation page.

    Code:
        bool reset()    {return (ResetEvent(obj) == TRUE);}
    As a side note, this construct is going to cause problems. You should never compare explicitly against TRUE. Any non-zero value must be considered true.
    Last edited by anonytmouse; 02-01-2005 at 03:56 PM.

  6. #6
    Carnivore ('-'v) Hunter2's Avatar
    Join Date
    May 2002
    Posts
    2,879
    Ok, thanks alot! I suspected WSAEnumNetworkEvents, but it was mentioned nowhere in the docs as a re-enabling function so I assumed it was a problem on my own part.

    As a side note, this construct is going to cause problems. You should never compare explicitly against TRUE. Any non-zero value must be considered true.
    You're right, I didn't notice this:

    >If the function succeeds, the return value is nonzero.
    >If the function fails, the return value is zero.

    I was under the impression that it returned TRUE for success and FALSE for failure. I suppose this means, then, that BOOL passed as a parameter will always be handled differently than a returned BOOL (almost as if they're completely different datatypes?)? An odd system of doing things if you ask me.
    Just Google It. √

    (\ /)
    ( . .)
    c(")(") This is bunny. Copy and paste bunny into your signature to help him gain world domination.

  7. #7
    Yes, my avatar is stolen anonytmouse's Avatar
    Join Date
    Dec 2002
    Posts
    2,544
    >> I suppose this means, then, that BOOL passed as a parameter will always be handled differently than a returned BOOL (almost as if they're completely different datatypes?)? <<

    Any BOOL value that is not zero should be considered true. This applies to input parameters, output parameters and return values. This is not a BOOL specific matter, the same applies to all integral and pointer data types when tested for truth.

    Of course, explicitly passing TRUE is best, for safety in the unlikely case that a function breaks this rule. A general programming rule is to be conservative with data that you pass to external code and liberal with data that you receive from external code.

    For example, CreateEvent handles BOOL values correctly:
    Code:
    #include <windows.h>
    #include <stdio.h>
    
    int main(void)
    {
    	HANDLE hEvent = CreateEvent(NULL, -10, 99, NULL);
    
    	if (hEvent)
    		printf("Event created successfully!\n");
    
    	if (WaitForSingleObject(hEvent, 100) == WAIT_OBJECT_0)
    		printf("Initial state was signalled.\n");
    
    	if (WaitForSingleObject(hEvent, 100) == WAIT_OBJECT_0)
    		printf("Event is manual reset.\n");
    
    	getchar();
    	return 0;
    }
    However, I would still make sure to explicitly use TRUE, as per the documentation.

  8. #8
    Carnivore ('-'v) Hunter2's Avatar
    Join Date
    May 2002
    Posts
    2,879
    >>the same applies to all integral and pointer data types when tested for truth.
    Yes, but the naming of the thing thew me off: BOOL. I assumed it was simply a typedef for bool (is it not?), or at least act like a boolean variable and therefore should only ever contain either 'true' or 'false' - and I figured that since TRUE and FALSE may for all we know be defined as 34567 and -2134, testing for TRUE would be safer and more correct than testing for true or != 0 (the thought never even crossed my mind to use != 0).

    Of course, since the functions are documented to return !=0 for success and ==0 for failure, that changes everything up. Thanks for pointing it out!
    Just Google It. √

    (\ /)
    ( . .)
    c(")(") This is bunny. Copy and paste bunny into your signature to help him gain world domination.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Void Functions Help
    By bethanne41 in forum C++ Programming
    Replies: 1
    Last Post: 05-09-2005, 05:30 PM
  2. Functions and Classes - What did I do wrong?
    By redmage in forum C++ Programming
    Replies: 5
    Last Post: 04-11-2005, 11:50 AM
  3. calling functions within functions
    By edd1986 in forum C Programming
    Replies: 3
    Last Post: 03-29-2005, 03:35 AM
  4. Factory Functions HOWTO
    By GuardianDevil in forum Windows Programming
    Replies: 1
    Last Post: 05-01-2004, 01:41 PM
  5. Shell functions on Win XP
    By geek@02 in forum Windows Programming
    Replies: 6
    Last Post: 04-19-2004, 05:39 AM