Announcement

Collapse
No announcement yet.

Using TCP w/o a DIALOG

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

  • Using TCP w/o a DIALOG

    Heya,

    I'm attempting to produce a small socket server, is there anyway to produce one that does not require a window to be created in order to receive the tcp notify messages, is there another way to create a socket server. In advance I do know you can simply %SW_HIDE the window, but I'm looking to get around this period.

    On a side not, I'm wondering if anyone has any documentation/source on using .dll's dynamically, for example: A textbox would contain the name of the dll, and another text box would contain the function to call, and so forth with the paramaters?


    Thanks in advance,
    Matthiaus Grace

    ------------------

  • #2
    You don't need a window.

    This question seems to pop up a lot, so let me repeat.

    You don't need a window or dialog. You never did.

    If you don't build it, it won't be there, and your program will behave just fine.

    Since you don't have a window, remember to use SLEEP instead of DoEvents in
    your loops.


    ------------------
    Thanks,

    John Kovacich
    Thanks,

    John Kovacich
    Ivory Tower Software

    Comment


    • #3
      John im not sure I understand you reply.

      It sounds like you are saying you do not need a Window in a DLL in order for the DLL to work (ie execute the function contained within it). Is this what you mean?

      The question that is being asked here I think is the same one I was asking which is how do you accomplish certain tasks that are commonly handled by a callback function without a callback function - ie without a Dialog in the first place.

      Why would we want to use a SLEEP statement in a loop? The idea is to remove the whole blocking function of a dialog alltogether to allow the code to execute from top to bottom without interuption.

      On the other hand, If you are saying you do not need a dialog to to act as a message pump or thread then please explain how you do things like WM_WHATEVER without an hDlg parameter to feed it?



      ------------------
      Kind Regards
      Mike

      Comment


      • #4
        I'm with John on this one,

        What do you need to use dialog callbacks for if there are no windows for user input?, a TCP server only needs to respond to TCP events, sending and receiving and maybe logging etc.

        Just have a pbmain function, your own functions and DLL calls etc, what's the issue.

        You use SLEEP in loops that have intensive processing in them to pass control back to windows so that other programs don't lock up, same as doevents on a dialog, I think that is what john was saying, that doevents is not going to work for you.



        ------------------

        Paul Dwyer
        Network Engineer
        Aussie in Tokyo
        (Paul282 at VB-World)

        Comment


        • #5
          As to using DLLs dynamically-- try searching the forums for "LoadLibrary".

          ------------------
          Tom Hanlin
          PowerBASIC Staff

          Comment


          • #6
            Thanks Paul. I only mentioned sleep, because doevents is DDT, and
            requires a dialog. I thought maybe that was making people think a
            dialog was mandatory.

            A programs natural state is without a dialog.

            Old programmers like myself grew up in a world where I/O meant
            reading and writing to your card punch, printer, pkb or hard disk
            (if you were fortunate enough to have one). Twenty five years ago
            interactive programming meant asking the operator how many copies
            to print. For us, the concept of programs without screens is a
            natural and comfortable place to be, unlike activex, oop, com and
            all those other crazy concepts that todays younger programmers are
            so comfortable with.

            Unfortunately, so many programmers today never experienced that
            world where data was processed and you got your results much later.
            It has become far too easy to overlook the simple task of call a
            routine, and recieve its results.

            Today, you can simply call a DLL, you don't need a callback, it will
            return results when it is done. If you use loadlibrary, or if more
            processes are sharingf the DLL, it will stay resident for you, JUST
            LIKE A SUBROUTINE. You can call it over and over, it can maintain
            static data from the previous call. (I think each process gets its
            own address space for data, so your static variables won't interfere
            with another processes). I have at least half a dozen DLL's with no
            human interfaces or callbacks. They can be used by PB or VB programs
            without modifications. You just have to get used to looking at them as
            small callable subroutines.

            ------------------
            Thanks,

            John Kovacich
            Thanks,

            John Kovacich
            Ivory Tower Software

            Comment


            • #7
              I don't follow how any of these replies answer the original question. When you create a TCP server, don't you have to specify a windows handle and an message that process the tcp events? How do you create a server without a dialog to process the messages? Sorry if I'm being obtuse, I've never created a tcp server without a dialog (regular applications, obviously, but not a tcp server).
              --Don

              ------------------
              www.basicguru.com/dickinson
              Don Dickinson
              www.greatwebdivide.com

              Comment


              • #8
                I don't have an example to show you, but the server merely monitors the
                incoming data, performs the requested task (presumably) and writes results
                back to the port.

                The only TCP command which even has a window handle is the TCP notify,
                (which by the way, is not individually covered in my copy of the help files).
                If I understand it correctly, it is used to notify your dialog (if
                you have one) that an event was transmitted.

                The TCP and UDP ports are like virtual serial ports. You can input, and you
                can output. They don't involve dialogs, windows, or handles. They just
                have IP addresses and/or port numbers.

                BTW. The same goes for the client too. I have a client program sending commands
                to the FTP server on an AS/400, and it doesn't require a dialog box
                to operate.


                ------------------
                Thanks,

                John Kovacich
                Thanks,

                John Kovacich
                Ivory Tower Software

                Comment


                • #9
                  Yes, you can input and output without a dialog. I know that, I do that. The question, as I understand it anyways is building a server
                  Enlighten me! the only way I know to build an application that listens and responds (TCP OPEN SERVER) is to use a window.
                  Are you building a server by calling TCP RECV on an opened socket (inside a thread, I assume because tcp recv is a blocking call)?
                  TIA,
                  Don

                  ------------------
                  www.basicguru.com/dickinson
                  Don Dickinson
                  www.greatwebdivide.com

                  Comment


                  • #10
                    One more thing ... If you build a server without a message event, how do you handle simultaneous connnections? How do you get info about the client that's talking to the server (that info is in the wparam/lparam of the message).
                    Thanks,
                    Don

                    ------------------
                    www.basicguru.com/dickinson
                    Don Dickinson
                    www.greatwebdivide.com

                    Comment


                    • #11
                      Hi Don

                      I have built numerous socket servers that operate without a
                      dialog. I always use the winsock API directly instead of the
                      built-in PB functions since it gives me a higher degree of
                      control over my functions.

                      To build a TCP server without using a dialog you have the
                      following possibilities:

                      - Use I/O completion ports (available on NT and Win2000) - this
                      is the method I usually use: it is very fast, highly scalable
                      and uses very little CPU. In essence I create a completion
                      port and associate it with my listening socket. I then create
                      a certain amount of threads which use GetQueuedCompletionStatus()
                      to wait on socket activity. Look at the OVERLAPPED structure and
                      the AcceptEx() functions.

                      - Use the Select() function - this is useful for creating portable
                      single-threaded socket servers. Create a passive (listening) socket
                      and bind it to a port. Use the fd_setstruc type as arguments to
                      Select(). When IO occurs test the structures (read, write or error)
                      depending on how many fd_setstruc you are using in the Select()
                      functions by using the FD_ISSET functions. To use the Select()
                      function you'll need a couple of functions (macros in the C api)
                      which are not included in Wsock32.inc. I can mail them to you if
                      you're interested.


                      - Use the WsaEventSelect, WsaWaitForMultipleEvents, WsaCreateEvent
                      and WsaEnumNetworkEvents calls to wait on socket IO. You tell
                      WsaEventSelect which IO you're interested in as in the following
                      pseudo-code:

                      Code:
                      lEvent = WSACreateEvent()
                      
                      lResult = WSAEventSelect( lSock, lEvent, %FD_ACCEPT OR %FD_CONNECT OR %FD_READ OR %FD_WRITE OR %FD_CLOSE )
                      
                      DO
                          lResult = WSAWaitForMultipleEvents( 1???, VARPTR(lEvent), %TRUE, %INFINITE, %FALSE)
                      
                          lResult = WSAEnumNetworkEvents( lSock, lEvent, VARPTR(tWSANetEvents) )
                      
                          SELECT CASE tWSANetEvents.lNetEvents
                                 CASE %FD_ACCEPT
                                 ...
                      
                      LOOP

                      Cheers

                      Florent

                      ------------------

                      Comment


                      • #12
                        I had a little time on my hands so I wrote an example
                        echo server using the Select Winsock API.

                        Code:
                        'Simple Echo server example using Select() calls
                        'Written by Florent Heyworth - 10 Feb 2001
                         
                        #INCLUDE "wsock32.inc"
                         
                        DECLARE FUNCTION FD_ISSET LIB "ws2_32.dll" ALIAS "__WSAFDIsSet" ( _
                                                                    BYVAL lSock AS LONG, _
                                                                    fdset AS fd_setstruc _
                                                                    ) AS LONG
                         
                        DECLARE FUNCTION MoveMemory LIB "KERNEL32.DLL" ALIAS "RtlMoveMemory" ( _
                                                                    BYVAL lpDest AS LONG, _
                                                                    BYVAL lpSource AS LONG, _
                                                                    BYVAL cbMove AS LONG _
                                                                    ) AS LONG
                         
                        DECLARE FUNCTION SetConsoleCtrlHandler LIB "KERNEL32.DLL" ALIAS "SetConsoleCtrlHandler" ( _
                                                                    BYVAL HandlerRoutine AS LONG, _
                                                                    BYVAL nAdd AS LONG _
                                                                     ) AS LONG
                        GLOBAL g_lExit AS LONG 'Exit flag
                         
                        #IF NOT %DEF(%NULL)
                        %NULL = 0
                        #ENDIF
                         
                        #IF NOT %DEF(%FALSE)
                        %FALSE = 0
                        #ENDIF
                         
                        #IF NOT %DEF(%TRUE)
                        %TRUE = 1
                        #ENDIF
                         
                        %CTRL_BREAK_EVENT                            = 1
                        %CTRL_CLOSE_EVENT                            = 2
                         
                        SUB SOCK_FD_CLR( BYVAL lSock AS LONG, fdset AS fd_setstruc )
                         
                           DIM i AS LONG
                         
                           FOR i = 0 TO fdset.fd_count
                               IF fdset.fd_array(i) = lSock THEN
                                  DO WHILE ( i <= fdset.fd_count -1 )
                                     fdset.fd_array(i) = fdset.fd_array(i+1)
                                     INCR i
                                  LOOP
                                  DECR fdset.fd_count
                                  EXIT FOR
                               END IF
                           NEXT
                         
                        END SUB
                         
                        SUB SOCK_FD_SETSOCK( BYVAL lSock AS LONG, fdset AS fd_setstruc )
                           ' FD_SET in wsock32.inc is incorrect
                         
                           DIM i AS LONG
                         
                           DO WHILE i < fdset.fd_count
                              IF fdset.fd_array(i) = lSock THEN
                                  EXIT DO
                              END IF
                              INCR i
                           LOOP
                         
                           IF i = fdset.fd_count THEN
                              IF fdset.fd_count < %FD_SETSIZE THEN
                                 fdset.fd_array(i) = lSock
                                 INCR fdset.fd_count
                              END IF
                           END IF
                         
                        END SUB
                         
                        FUNCTION serversock( szService AS ASCIIZ , szTransport AS ASCIIZ, BYVAL lLen AS LONG) AS LONG
                           DIM ptServent AS servent PTR
                           DIM minsock_sa AS sockaddr_in
                           DIM lSock AS LONG
                           DIM lSockType AS LONG
                         
                           minsock_sa.sin_family = %AF_INET
                           minsock_sa.sin_addr.s_addr = %INADDR_ANY 'listen on all interfaces
                         
                           'map service to port
                           ptServent = getservbyname( szService, szTransport )
                           IF ISTRUE( ptServent ) THEN
                              minsock_sa.sin_port = htons( ntohs( @ptServent.s_port ) )
                           ELSE
                              minsock_sa.sin_port = htons( BYVAL VAL( szService ) )
                              IF ISFALSE( minsock_sa.sin_port ) THEN
                                 PRINT "Can't retrieve service: " + szService
                                 CALL WSACleanUp()
                                 FUNCTION = %INVALID_SOCKET
                                 EXIT FUNCTION
                              END IF
                           END IF
                         
                           IF UCASE$(szTransport) = "UDP" THEN
                              lSockType = %SOCK_DGRAM
                           ELSE
                              lSockType = %SOCK_STREAM
                           END IF
                         
                           'socket allocation
                           lSock = socket( %PF_INET, lSockType, 0 )
                         
                           IF lSock = %INVALID_SOCKET THEN
                              PRINT "Can't create socket: " + FORMAT$(WsaGetLastError())
                              FUNCTION = %INVALID_SOCKET
                              CALL WSACleanUp()
                              EXIT FUNCTION
                           END IF
                         
                           'bind socket
                           IF bind( lSock, minsock_sa, SIZEOF( minsock_sa) ) = %SOCKET_ERROR THEN
                              PRINT "Can't bind to port " + szService  + " - last error: " + FORMAT$(WSAGetLastError() )
                              FUNCTION = %INVALID_SOCKET
                              CALL WSACleanUp()
                              EXIT FUNCTION
                           END IF
                         
                           IF ( lSock = %SOCK_STREAM AND listen( lSock, lLen ) = %SOCKET_ERROR ) THEN
                              PRINT "Can't listen on port: " + szService
                              FUNCTION = %INVALID_SOCKET
                              CALL WSACleanUp()
                              EXIT FUNCTION
                           END IF
                         
                           FUNCTION = lSock
                        END FUNCTION
                         
                        FUNCTION GetPeerAddress( BYVAL lSock AS LONG ) AS STRING
                           DIM lReturn AS LONG
                           DIM sock_sa AS sockaddr_in
                           DIM szIp AS ASCIIZ PTR
                         
                           lReturn = getpeername( lSock, sock_sa, SIZEOF( sock_sa ) )
                           IF lReturn <> %SOCKET_ERROR THEN
                              szIp = inet_ntoa(sock_sa.sin_addr.s_addr)
                              IF szIp <> %NULL THEN
                                 FUNCTION = @szIp
                              END IF
                           END IF
                         
                        END FUNCTION
                         
                        FUNCTION Echo( BYVAL lSock AS LONG ) AS LONG
                            LOCAL lSize AS LONG
                            LOCAL szBuf AS ASCIIZ * 1024
                         
                            lSize = rrecv( lSock, szBuf , SIZEOF( szBuf ), 0 )
                            IF lSize = 0  OR lSize = %SOCKET_ERROR THEN
                                'no data
                                FUNCTION = 0
                                EXIT FUNCTION
                            END IF
                          
                            'get address of peer
                            PRINT DATE$ + " " + TIME$ + " Received from " + GetPeerAddress( lSock ) + ": " + szBuf
                         
                            lSize = ssend( lSock, szBuf, LEN(szBuf), 0 )
                            IF lSize = 0 OR lSize = %SOCKET_ERROR THEN
                                FUNCTION = 0
                                EXIT FUNCTION
                            END IF
                         
                            FUNCTION = lSize
                         
                        END FUNCTION
                        
                         
                        FUNCTION ConsoleHandler( BYVAL dwEvent AS DWORD )  AS LONG
                            'registers a console control handler
                            SELECT CASE dwEvent
                                CASE %CTRL_BREAK_EVENT, %CTRL_CLOSE_EVENT
                                    g_lExit = %TRUE
                                    FUNCTION = %TRUE
                                    EXIT FUNCTION
                                CASE ELSE
                                    FUNCTION = %FALSE
                                END SELECT
                         
                        END FUNCTION
                         
                        FUNCTION PBMAIN() AS LONG
                            LOCAL lServerSock AS LONG
                            LOCAL lAcceptSock AS LONG
                            LOCAL lFd AS LONG
                            LOCAL i AS LONG
                            LOCAL lResult AS LONG
                            LOCAL tWsaData AS WSADATA
                            LOCAL minsock_sa AS sockaddr_in
                            LOCAL rfds AS fd_setstruc 'read_file descriptor set
                            LOCAL afds AS fd_setstruc 'active file descriptor set
                         
                            'install a console handler to catch control_break event (exit the loop)
                            CALL SetConsoleCtrlHandler( CODEPTR(ConsoleHandler), %TRUE )
                         
                            'ask for Winsock 2.0
                            IF WSAStartup( MAKLNG(2,0), tWSAData ) THEN
                                'unable to initialize winsock
                                PRINT "Unable to initialize Winsock"
                                EXIT FUNCTION
                            END IF
                         
                            IF tWSAData.wVersion < 2 THEN
                                PRINT "Winsock version too low"
                                EXIT FUNCTION
                            END IF
                         
                            'Port 7, TCP protocol, 5 backlogs in listen
                            lServerSock = serversock( "7", "TCP", 5 )
                            IF lServerSock = %INVALID_SOCKET THEN EXIT FUNCTION
                         
                            CALL FD_ZERO( afds )
                            CALL SOCK_FD_SETSOCK( lServerSock, afds )
                         
                            DO WHILE ISFALSE( g_lExit )
                                CALL MoveMemory( VARPTR(rfds), VARPTR(afds), SIZEOF( rfds ) )
                         
                                'we only handle read-ready sockets
                                IF sselect( %FD_SETSIZE, rfds, BYVAL %NULL, BYVAL %NULL, BYVAL %NULL ) = %SOCKET_ERROR THEN
                                    PRINT "select error: " + FORMAT$( WSAGetLastError() )
                                    FUNCTION = %FALSE
                                    EXIT FUNCTION
                                END IF
                        
                         
                                IF ISTRUE( FD_ISSET( lServerSock, rfds ) ) THEN
                                    lAcceptSock = aaccept( lServerSock, minsock_sa, SIZEOF( minsock_sa) )
                                    IF lAcceptSock = %INVALID_SOCKET THEN
                                        PRINT "accept failed - error: " + FORMAT$( WSAGetLastError() )
                                        FUNCTION = %FALSE
                                        EXIT FUNCTION
                                    END IF
                                    CALL SOCK_FD_SETSOCK( lAcceptSock, afds )
                                END IF
                         
                                i = 0
                                DO WHILE i < rfds.fd_count
                                    lFd = rfds.fd_array(i)
                                    IF lFd <> lServerSock AND ISTRUE( FD_ISSET( lFd, rfds ) ) THEN
                                        'call the function to deal with incoming IO
                                        lResult = Echo( lFd )
                                        IF lResult = 0 THEN 'done with connection - close it
                                            'depends on the protocol you're using to communicate
                                            'is this a one off transaction or not?
                                            CALL closesocket( lFd )
                                            CALL SOCK_FD_CLR( lFd, afds )
                                        END IF
                         
                                    END IF
                                  INCR i
                                LOOP
                         
                           LOOP
                         
                           CALL closesocket( lServerSock )
                           CALL WSACleanUp()
                         
                           CALL SetConsoleCtrlHandler( CODEPTR(ConsoleHandler), %FALSE )
                         
                        END FUNCTION

                        Cheers

                        Florent



                        ------------------

                        Comment


                        • #13
                          Wow, thanks for the code Florent. I never doubted that a server could be built without a dialog to handle the messages if you use the api directly. I don't know how to do it with built-in PB commands (that was the guy's original question, I think). Thanks again though - very useful code.
                          Best Regards,
                          Don

                          ------------------
                          www.basicguru.com/dickinson
                          Don Dickinson
                          www.greatwebdivide.com

                          Comment


                          • #14
                            Thanks for the kind words Don

                            I'm not aware of any way to build server apps using PB TCP
                            built-in commands apart from the TCP NOTIFY dialog way
                            either...

                            Cheers

                            Florent

                            ------------------

                            Comment


                            • #15
                              There has to be a way to do it. There is an example in the PB/CC
                              manual. If PB/CC can do it you would think that you could do it
                              in PB/DLL as well.

                              ------------------
                              ATB

                              Charles Kincaid

                              Comment


                              • #16
                                Charles

                                writing TCP servers in PBCC and PBDLL is exactly the same:
                                you create a hidden window which is used to receive %FD_READ,
                                %FD_WRITE etc, messages.

                                The question was: is there a way to write a TCP server using PB
                                build-in commands which does not involve using a hidden dialog...


                                Cheers

                                Florent




                                ------------------

                                Comment

                                Working...
                                X