Hi Guys

Am trying to use WSAAsyncSelect on my socket like below. I've asked WSAAsyncSelect to listen for - FD_ACCEPT + FD_READ. I believe it works... But will FD_READ event occur when we put a socket in listen mode? When the FD_READ event triggers am trying to read from the socket, will it work?

.386 
.model flat,stdcall
option casemap:none
include         \masm32\include\winmm.inc
include         \masm32\include\windows.inc
include         \masm32\include\masm32.inc
include         \masm32\include\wsock32.inc
include         \masm32\include\user32.inc
include         \masm32\include\kernel32.inc
include         \masm32\include\advapi32.inc
include         \masm32\include\shell32.inc

includelib      \masm32\lib\shell32.lib
includelib      \masm32\lib\user32.lib
includelib      \masm32\lib\kernel32.lib
includelib      \masm32\lib\wsock32.lib
includelib      \masm32\lib\masm32.lib
includelib      \masm32\lib\advapi32.lib
includelib      \masm32\lib\winmm.lib

CTEXT MACRO y:VARARG
LOCAL sym

CONST segment
IFIDNI <y>,<>
sym db 0
ELSE
sym db y,0
ENDIF
CONST ends

EXITM <OFFSET sym>
ENDM

WinMain proto :DWORD,:DWORD,:DWORD,:DWORD

.DATA                     ; initialized data
ClassName db "SimpleWinClass",0      
AppName db "Our First Window",0        
Port dd 80
WM_SOCKET equ WM_USER + 100

.DATA?                ; Uninitialized data
hInstance HINSTANCE ?        ; Instance handle of our program
CommandLine LPSTR ?
wsadata WSADATA <>
sin sockaddr_in <>
sock DWORD ?
buf db 200 DUP(?)

.CODE                ; Here begins our code
start:
invoke GetModuleHandle, NULL            
                                                                     
mov hInstance,eax
invoke GetCommandLine                        
                                                                     
mov CommandLine,eax
invoke WinMain, hInstance,NULL,CommandLine, SW_SHOWDEFAULT        
invoke ExitProcess, eax                          

WinMain proc hInst:HINSTANCE,hPrevInst:HINSTANCE,CmdLine:LPSTR,CmdShow:DWORD
   LOCAL wc:WNDCLASSEX                                          
   LOCAL msg:MSG
   LOCAL hwnd:HWND

   mov   wc.cbSize,SIZEOF WNDCLASSEX                  
   mov   wc.style, CS_HREDRAW or CS_VREDRAW
   mov   wc.lpfnWndProc, OFFSET WndProc
   mov   wc.cbClsExtra,NULL
   mov   wc.cbWndExtra,NULL
   push  hInstance
   pop   wc.hInstance
   mov   wc.hbrBackground,COLOR_WINDOW+1
   mov   wc.lpszMenuName,NULL
   mov   wc.lpszClassName,OFFSET ClassName
   invoke LoadIcon,NULL,IDI_APPLICATION
   mov   wc.hIcon,eax
   mov   wc.hIconSm,eax
   invoke LoadCursor,NULL,IDC_ARROW
   mov   wc.hCursor,eax
   invoke RegisterClassEx, addr wc                      
   invoke CreateWindowEx,NULL,\
               ADDR ClassName,\
               ADDR AppName,\
               WS_OVERLAPPEDWINDOW,\
               CW_USEDEFAULT,\
               CW_USEDEFAULT,\
               CW_USEDEFAULT,\
               CW_USEDEFAULT,\
               NULL,\
               NULL,\
               hInst,\
               NULL
   mov   hwnd,eax
   
   ;invoke ShowWindow, hwnd,CmdShow                
   ;invoke UpdateWindow, hwnd                                  

   invoke WSAStartup, 101h, ADDR wsadata

   invoke socket, AF_INET, SOCK_STREAM, 0
   mov sock, eax

   invoke WSAAsyncSelect, sock, hwnd, WM_SOCKET, FD_ACCEPT+FD_READ
   mov sin.sin_family, AF_INET

   invoke htons, Port
   mov sin.sin_port, ax
   mov sin.sin_addr, INADDR_ANY

   invoke bind, sock, addr sin, SIZEOF sin

   invoke listen, sock, 15
 
   .WHILE TRUE                                                          
       invoke GetMessage, ADDR msg,NULL,0,0
   .BREAK .IF (!eax)
       invoke TranslateMessage, ADDR msg
       invoke DispatchMessage, ADDR msg
  .ENDW

   mov     eax,msg.wParam                                            
   ret
   
WinMain endp

WndProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM

    .IF uMsg==WM_DESTROY          
       invoke closesocket, sock
       invoke WSACleanup                
       invoke PostQuitMessage,NULL  

   .ELSEIF uMsg == WM_SOCKET
       mov eax, lParam

           .IF ax == FD_ACCEPT                
               invoke MessageBox, NULL, CTEXT("ACCEPTED"), CTEXT("ACCEPTED"), MB_OK
           .ENDIF
                         
           .IF ax == FD_READ
               invoke MessageBox, NULL, CTEXT("READ"), CTEXT("READ"), MB_OK
               invoke recv, sock, ADDR buf, SIZEOF buf, 0
               invoke MessageBox, NULL, ADDR buf, ADDR buf, MB_OK
           .ENDIF      

           .IF ax == FD_CLOSE
               invoke MessageBox, NULL, CTEXT("CLOSE"), CTEXT("CLOSE"), MB_OK
           .ENDIF
       
   .ELSE
       invoke DefWindowProc,hWnd,uMsg,wParam,lParam    
       ret
   .ENDIF

   xor eax,eax
   ret

WndProc endp

end start


Edit: Am trying to connect to the socket using telnet. Telnet localhost 80.
Posted on 2009-12-01 04:17:42 by karthikeyanck
I just realized that I had to accept the connection as well, but still searching answers for other questions   :sad:

Edit, I just figured out that when I use recv function I still get the same old data which was already received + the new data as well.. mind that I have cleared the buffers as well, but still the old data is there added to the new one's.

WinMain proto :DWORD,:DWORD,:DWORD,:DWORD 

.DATA                    ; initialized data
ClassName db "SimpleWinClass",0     
AppName db "Our First Window",0       
Port dd 80
WM_SOCKET equ WM_USER + 100
argpVal dd 0
lpFileName db "C:\sample.txt",0

.DATA?                ; Uninitialized data
hInstance HINSTANCE ?        ; Instance handle of our program
CommandLine LPSTR ?
wsadata WSADATA <>
sin sockaddr_in <>
sock DWORD ?
buf db 1024 DUP(?)
addrLen dd ?
sock1 DWORD ?
hwnd_file dd ?
lpNumberOfBytesWritten dd ?


.CODE                ; Here begins our code
start:

invoke CreateFile, ADDR lpFileName, GENERIC_WRITE, NULL, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL
mov hwnd_file, eax

invoke GetModuleHandle, NULL           
                                                                     
mov hInstance,eax
invoke GetCommandLine                       
                                                                     
mov CommandLine,eax
invoke WinMain, hInstance,NULL,CommandLine, SW_SHOWDEFAULT       
invoke ExitProcess, eax                         

WinMain proc hInst:HINSTANCE,hPrevInst:HINSTANCE,CmdLine:LPSTR,CmdShow:DWORD
    LOCAL wc:WNDCLASSEX                                         
    LOCAL msg:MSG
    LOCAL hwnd:HWND

    mov  wc.cbSize,SIZEOF WNDCLASSEX                 
    mov  wc.style, CS_HREDRAW or CS_VREDRAW
    mov  wc.lpfnWndProc, OFFSET WndProc
    mov  wc.cbClsExtra,NULL
    mov  wc.cbWndExtra,NULL
    push  hInstance
    pop  wc.hInstance
    mov  wc.hbrBackground,COLOR_WINDOW+1
    mov  wc.lpszMenuName,NULL
    mov  wc.lpszClassName,OFFSET ClassName
    invoke LoadIcon,NULL,IDI_APPLICATION
    mov  wc.hIcon,eax
    mov  wc.hIconSm,eax
    invoke LoadCursor,NULL,IDC_ARROW
    mov  wc.hCursor,eax
    invoke RegisterClassEx, addr wc                     
    invoke CreateWindowEx,NULL,\
                ADDR ClassName,\
                ADDR AppName,\
                WS_OVERLAPPEDWINDOW,\
                CW_USEDEFAULT,\
                CW_USEDEFAULT,\
                CW_USEDEFAULT,\
                CW_USEDEFAULT,\
                NULL,\
                NULL,\
                hInst,\
                NULL
    mov  hwnd,eax
   
    ;invoke ShowWindow, hwnd,CmdShow               
    ;invoke UpdateWindow, hwnd                                 

    invoke WSAStartup, 101h, ADDR wsadata

    invoke socket, AF_INET, SOCK_STREAM, 0
    mov sock, eax

    invoke WSAAsyncSelect, sock, hwnd, WM_SOCKET, FD_ACCEPT + FD_READ
    mov sin.sin_family, AF_INET

    invoke htons, Port
    mov sin.sin_port, ax
    mov sin.sin_addr, INADDR_ANY

    invoke bind, sock, addr sin, SIZEOF sin

    mov addrLen, SIZEOF sin

    invoke listen, sock, 15

    .WHILE TRUE                                                         
        invoke GetMessage, ADDR msg,NULL,0,0
    .BREAK .IF (!eax)
        invoke TranslateMessage, ADDR msg
        invoke DispatchMessage, ADDR msg
  .ENDW

    mov    eax,msg.wParam                                           
    ret
   
WinMain endp

WndProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM

    .IF uMsg==WM_DESTROY         
        invoke closesocket, sock
        invoke closesocket, sock1
        invoke WSACleanup               
        invoke PostQuitMessage,NULL 

    .ELSEIF uMsg == WM_SOCKET
        mov eax, lParam

            .IF ax == FD_ACCEPT 
                invoke accept, sock, ADDR sin, ADDR addrLen
                mov sock1, eax           
                invoke MessageBox, NULL, CTEXT("ACCEPTED"), CTEXT("ACCEPTED"), MB_OK
            .ENDIF
                         
            .IF ax == FD_READ
                invoke ioctlsocket, sock1, FIONREAD, ADDR argpVal

                    .IF argpVal > 0
                        invoke recv, sock1, ADDR buf, SIZEOF buf, MSG_PEEK
                        invoke WriteFile, hwnd_file, ADDR buf, eax, ADDR lpNumberOfBytesWritten, NULL
                        invoke MessageBox, NULL, ADDR buf, ADDR buf, MB_OK
                        mov buf, 0
                        mov argpVal, 0
                    .ENDIF
            .ENDIF
       
    .ELSE
        invoke DefWindowProc,hWnd,uMsg,wParam,lParam   
        ret
    .ENDIF

    xor eax,eax
    ret

WndProc endp

end start

Posted on 2009-12-01 07:06:22 by karthikeyanck
Your "invoke ioctlsocket, sock1, FIONREAD, ADDR argpVal" isn't used, and is generally something you shouldn't be doing. Also, you specify MSG_PEEK for your recv() call, no wonder that the old data is still there, then :)

And finally, apart perhaps for learning, stay away from WSAAsyncSelect() - it's OK for a small amount of connections and limited transfer rate, but it's performance scales abysmally.
Posted on 2009-12-01 18:07:33 by f0dder
Expect multiple FD messages at once, and handle it.
For example, FD_READ+FD_CLOSE

Soon as you are ready, move away from this networking model, it sucks, because under load, your window message pump will drown under the weight, and your application GUI will HANG.. there are other ways.
Come to the dark side...we have cookies!!
Posted on 2009-12-02 01:35:53 by Homer
there are other ways.
Come to the dark side...we have cookies!!
:D

WSAEventSelect or I/O Completion Ports, basically. Or, if you know you won't have loads of concurrent connections, even a thread+blockingsocket per connection :)
Posted on 2009-12-02 01:55:08 by f0dder

Your "invoke ioctlsocket, sock1, FIONREAD, ADDR argpVal" isn't used, and is generally something you shouldn't be doing. Also, you specify MSG_PEEK for your recv() call, no wonder that the old data is still there, then :)

And finally, apart perhaps for learning, stay away from WSAAsyncSelect() - it's OK for a small amount of connections and limited transfer rate, but it's performance scales abysmally.


Thanks guys for the replies,

f0dder, I didn't understand - Your "invoke ioctlsocket, sock1, FIONREAD, ADDR argpVal" isn't used, and is generally something you shouldn't be doing.

I degbugged this with Olly and found that ioctlsocket returns 0 and also returns the number of bytes available to be read in argpVal. Also what should I do to ignore the previously read data. Am a bit confused.
Posted on 2009-12-02 02:08:35 by karthikeyanck
Oh sorry, you *are* using the return value from the FIONREAD (.IF argpVal > 0) - my bad, I blame these tired old eyes ;). Thing is, it still shouldn't be necessary to do when you're acting on a FD_READ.

As for "ignore previous data", remove the MSG_PEEK from your recv() call, since this specifies "grab data from socket, but don't remove it from the socket buffer".
Posted on 2009-12-02 02:31:59 by f0dder
ah, thanks again f0dder.... I changed MSG_PEEK to 0, works like charm... but another question here, I wonder how would I receive data from the socket buffer after it is filled with certain amount of data say 50 bytes.... I thought ioctlsocket is the only way, is there another way?

There lies another challenge. What happens if the last chunk of data received is less than 50 bytes, will I miss it? I think I should add another check as well for that too.
Posted on 2009-12-02 04:40:14 by karthikeyanck
You should avoid coding with those kind of assumptions - TCP is stream-based, you can basically get any amount of bytes in your recv() call... so you much check the recv() amount-of-bytes-read return value. Remember that even if FIONREAD says there's 1000 bytes available, recv() might return less.

Basically, don't enforce a "packet mindset" on top of a "stream connection" :)
Posted on 2009-12-02 04:51:36 by f0dder
Great, thanks for the tip...

So should be using ioctlsocket to check the amount of data received and then do a receive based upon the it (but again receive may return data less that what ioctlsocket returned)... so basically do a check cmp icotlsocket_returned, recv_returned... and based upon that receive the data again? /:)
Posted on 2009-12-02 05:28:11 by karthikeyanck
Nope - simply drop the ioctlsocket() and do a recv() with "max data you can handle". Then process the amount of data recv() indicates it returned. How you process the data depends on the protocol you're implementing... often you'll need to buffer data before you can process fully.

In your case, simply writing received data to file, you don't need to bother with all that - just grab whatever data is available and write that :)

And finally, remember to check return values for errors. Even though you get FD_READ, you can theoretically still get  WSAEWOULDBLOCK on your recv() call. And please, do the proper error-handling code rather then setting the socket to blocking mode :)


Expect multiple FD messages at once, and handle it.
For example, FD_READ+FD_CLOSE
Hmm?
Although WSAAsyncSelect can be called with interest in multiple events, the application window will receive a single message for each network event.
Posted on 2009-12-02 05:58:44 by f0dder
With absolute respect to microsoft, I have found that it is quite normal to receive multiple FD messages at once, at the eventing level its just a set of bit masks.
You need to be a little careful in your handling of WM_SOCKET user messages.
This is not true in any other windows networking schemes that I know of.

The example I gave is a common one - data was received, and the socket was closed - it was the last data in the stream.
Posted on 2009-12-02 07:15:00 by Homer
Oops, I'd to take off that MessageBox when I'm checking for the FD_ACCEPT event. Clicking in OK in the MessageBox sets the AX and it's affecting the next routine of checking FD_READ (who's value is also 1)... as a result on every first instant of accepting a connection, the code falls into the receive function and returns WSAEWOULDBLOCK..
Posted on 2009-12-02 07:57:28 by karthikeyanck
Homer: report documentation bug to MS? :) - funny thing if this happens when they clearly state you should only receive a single message at a time. Does mirror how WSAEventSelect works, though.

karthikeyanck: push/pop :)
Posted on 2009-12-02 11:18:15 by f0dder