Announcement

Collapse
No announcement yet.

Working Socket Example! Overlapped and using Completion Port

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

  • Working Socket Example! Overlapped and using Completion Port

    Working example of socket communications using CreateIoCompletionPort - according to what I have read "This model is well suited to handling hundreds or thousands of sockets."
    A work in progress.

    Let me know if you see any glaring errors - I am sure there are some!


    This is the page that gave me the idea.
    https://www.winsocketdotnetworkprogr...omethod5i.html


    Code:
    #COMPILE EXE
    #DIM ALL
    
    #INCLUDE "Win32API.inc"
    #INCLUDE "MSWSock.inc"
    
    GLOBAL GL_hDlg AS DWORD
    GLOBAL GL_LOG_FILE_NUMBER AS LONG
    GLOBAL GL_QUIT_FLAG AS LONG
    
    GLOBAL gServerInfo AS SYSTEM_INFO 'ServerInfo
    
    GLOBAL GL_FILE_NAME_ASCIIZ AS ASCIIZ * 256
    GLOBAL hReadFile AS DWORD
    GLOBAL GLOBAL_EVENT_WAIT_FOR_OVERLAPPED_READ AS DWORD
    GLOBAL GLOBAL_QUIT AS LONG
    GLOBAL GLOBAL_FILE_HANDLE AS DWORD
    
    GLOBAL DEMO_MODE AS LONG
    
    ENUM CONTROLS SINGULAR
    ENUM_STARTING_NUMBER = 100
    TIMER_1
    LISTBOX_VIEWER
    BUTTON_1
    BUTTON_2
    BUTTON_3
    BUTTON_4
    BUTTON_5
    BUTTON_6
    END ENUM
    
    '------------------------------------------------------------------------------
    FUNCTION PBMAIN () AS LONG
    
    GL_QUIT_FLAG = 0
    MAIN_DIALOG()
    
    END FUNCTION
    '------------------------------------------------------------------------------
    FUNCTION MAIN_DIALOG() AS LONG
    
    LOCAL RESULT AS LONG
    LOCAL DIALOG_HEIGHT AS LONG
    LOCAL DIALOG_WIDTH AS LONG
    LOCAL DIALOG_STYLE AS LONG
    LOCAL DIALOG_STYLE_EX AS LONG
    LOCAL LISTBOX_STYLE AS LONG
    LOCAL LISTBOX_STYLE_EX AS LONG
    
    DIALOG_HEIGHT = 320
    DIALOG_WIDTH = 560
    
    DIALOG_STYLE = %WS_POPUP OR %WS_CAPTION OR %WS_SYSMENU OR %WS_MINIMIZEBOX OR %WS_VISIBLE OR %DS_MODALFRAME OR %DS_CENTER OR %DS_3DLOOK OR %DS_NOFAILCREATE OR %DS_SETFONT
    DIALOG_STYLE_EX = %WS_EX_CONTROLPARENT OR %WS_EX_LEFT OR %WS_EX_LTRREADING OR %WS_EX_RIGHTSCROLLBAR
    
    LISTBOX_STYLE = %WS_CHILD OR %WS_VISIBLE OR %WS_BORDER OR %WS_TABSTOP OR %WS_VSCROLL OR %LBS_NOTIFY OR %LBS_DISABLENOSCROLL
    LISTBOX_STYLE_EX = %WS_EX_LEFT OR %WS_EX_LTRREADING OR %WS_EX_RIGHTSCROLLBAR
    
    DIALOG NEW %HWND_DESKTOP, "CreateIoCompletionPort", 0, 0, DIALOG_WIDTH, DIALOG_HEIGHT, DIALOG_STYLE, DIALOG_STYLE_EX, TO GL_hDlg
    
    CONTROL ADD LISTBOX, GL_hDlg, %LISTBOX_VIEWER,, 10, 10, 560-20, 320-55, LISTBOX_STYLE, LISTBOX_STYLE_EX
    CONTROL ADD BUTTON, GL_hDlg, %BUTTON_1, "1", 10,290,100,20
    CONTROL ADD BUTTON, GL_hDlg, %BUTTON_2, "2", 120,290,100,20
    CONTROL ADD BUTTON, GL_hDlg, %BUTTON_3, "3", 230,290,100,20
    CONTROL ADD BUTTON, GL_hDlg, %BUTTON_4, "4", 340,290,100,20
    CONTROL ADD BUTTON, GL_hDlg, %BUTTON_5, "5", 450,290,100,20
    DIALOG SHOW MODAL GL_hDlg, CALL MAIN_DIALOG_CALLBACK
    
    END FUNCTION
    
    '------------------------------------------------------------------------------
    CALLBACK FUNCTION MAIN_DIALOG_CALLBACK
    
    SELECT CASE AS LONG CB.MSG
    
    CASE %WM_INITDIALOG
    SETTIMER(GL_hDlg, %TIMER_1, 200)
    
    CASE %WM_TIMER
    KILLTIMER(GL_hDlg, %TIMER_1)
    START_TCP_LISTEN_ACCEPT_THREAD_IN_COMPLETION_PORT_MODE
    
    CASE %WM_DESTROY
    GL_QUIT_FLAG = %TRUE
    
    CASE %WM_COMMAND
    
    SELECT CASE CB.CTL
    
    CASE %BUTTON_1
    TEXT_TO_SCREEN "1"
    'GL_QUIT_FLAG = %TRUE
    'DIALOG END GL_hDlg
    
    CASE %BUTTON_2
    TEXT_TO_SCREEN "2"
    
    CASE %BUTTON_3
    TEXT_TO_SCREEN "3"
    
    CASE %BUTTON_4
    TEXT_TO_SCREEN "4"
    
    CASE %BUTTON_5
    TEXT_TO_SCREEN "5"
    
    CASE %BUTTON_6
    TEXT_TO_SCREEN "6"
    
    END SELECT
    
    END SELECT
    
    END FUNCTION
    
    TYPE COMPLETION_PORT_KEY
    ACCEPTING_SOCKET AS DWORD
    WSA_BUFER AS WSABUF
    END TYPE
    '------------------------------------------------------------------------------
    FUNCTION START_TCP_LISTEN_ACCEPT_THREAD_IN_COMPLETION_PORT_MODE() AS LONG
    
    LOCAL THREAD_HANDLE AS LONG
    LOCAL THREAD_RESULT AS LONG
    
    THREAD CREATE THREADED_START_TCP_LISTEN_ACCEPT_THREAD(0) TO THREAD_HANDLE
    THREAD CLOSE THREAD_HANDLE TO THREAD_RESULT
    
    END FUNCTION
    '------------------------------------------------------------------------------
    THREAD FUNCTION THREADED_START_TCP_LISTEN_ACCEPT_THREAD(BYVAL NO_VAL AS LONG)AS LONG
    ON ERROR GOTO ERROR_CONTROL
    
    LOCAL RESULT_LONG AS LONG
    LOCAL TYPE_SOCKET_ADDRESS AS SOCKADDR_IN
    LOCAL LISTENING_SOCKET AS DWORD
    LOCAL ACCEPTING_SOCKET AS DWORD
    LOCAL PTR_IP_ADDRESS AS BYTE POINTER
    LOCAL MY_WSADATA AS WSADATA
    LOCAL FILE_NUMBER AS LONG
    LOCAL AcceptBuffer AS ASCIIZ *4096
    LOCAL my_bytes AS DWORD
    LOCAL MY_COMPLETION_PORT_KEY AS COMPLETION_PORT_KEY
    LOCAL my_pointer AS ASCIIZ POINTER
    LOCAL my_asciiz AS ASCIIZ *128'4096
    LOCAL wsBuf AS WSABUF
    
    LOCAL INVALID_HANDLE_CreateIoCompletionPort AS DWORD
    LOCAL HANDLE_CreateIoCompletionPort AS DWORD
    LOCAL TEMP_THREAD_HANDLE AS DWORD
    LOCAL THREAD_INDEX AS LONG
    
    'The WSAStartup function initiates use of the Winsock DLL by a process.
    RESULT_LONG = WSAStartup(MAKWRD(2, 2), MY_WSADATA)
    
    'Create an I/O completion port
    'The first call is to "prime" the function using
    INVALID_HANDLE_CreateIoCompletionPort = CreateIoCompletionPort(%INVALID_HANDLE_VALUE, 0, 0, 4)
    
    'Start the worker threads
    'Microsoft suggests 2 per CPU
    FOR THREAD_INDEX = 1 TO 1
    TEMP_THREAD_HANDLE = CreateThread($NUL, 0, CODEPTR(ServerWorkerThread), INVALID_HANDLE_CreateIoCompletionPort, 0, 0)
    CloseHandle(TEMP_THREAD_HANDLE)
    NEXT
    
    'The WSASocket function creates a socket that is bound to a specific transport-service provider.
    LISTENING_SOCKET = WSASocket(%AF_INET, %SOCK_STREAM, %IPPROTO_TCP, BYVAL 0, 0, %WSA_FLAG_OVERLAPPED)
    
    
    'Set up the sock addr structure that the listening socket will be bound to. In this case, the structure holds the local IP address and the port.
    TYPE_SOCKET_ADDRESS.sin_family = %AF_INET
    TYPE_SOCKET_ADDRESS.sin_port = htons(4070)
    TYPE_SOCKET_ADDRESS.sin_addr.s_addr = inet_addr("192.168.1.193") 'or htonl(%INADDR_ANY)
    
    
    'The bind function associates a local address with a socket. Bind the listening socket to the IP address and port.
    RESULT_LONG = bind(LISTENING_SOCKET, BYVAL VARPTR(TYPE_SOCKET_ADDRESS), SIZEOF(TYPE_SOCKET_ADDRESS))
    
    'The listen function places a socket in a state in which it is listening for an incoming connection.
    RESULT_LONG = listen(LISTENING_SOCKET, 5)
    
    
    DO WHILE ISTRUE ISWIN(GL_hDlg)
    
    my_pointer = VARPTR(my_asciiz)
    wsBuf.dLen = 128'4096
    wsBuf.buf = my_pointer
    MY_COMPLETION_PORT_KEY.WSA_BUFER = wsBuf
    
    
    TEXT_TO_SCREEN "LISTENING SOCKET TO BE CONTACTED BY SOMEONE WANTING TO COMMUNICATE"
    
    ACCEPTING_SOCKET = WSAAccept(LISTENING_SOCKET, BYVAL VARPTR(TYPE_SOCKET_ADDRESS), SIZEOF(TYPE_SOCKET_ADDRESS), 0, 0)
    MY_COMPLETION_PORT_KEY.ACCEPTING_SOCKET = ACCEPTING_SOCKET
    
    HANDLE_CreateIoCompletionPort = CreateIoCompletionPort(ACCEPTING_SOCKET, INVALID_HANDLE_CreateIoCompletionPort, VARPTR(MY_COMPLETION_PORT_KEY), 0)
    
    TEXT_TO_SCREEN GET_IP_ADDRESS(TYPE_SOCKET_ADDRESS)
    
    POST_WSARecv(ACCEPTING_SOCKET, wsBuf)
    
    LOOP
    
    
    EXIT FUNCTION
    
    ERROR_CONTROL:
    MSGBOX ERROR$
    
    END FUNCTION
    '------------------------------------------------------------------------------
    FUNCTION POST_WSARecv(ACCEPTING_SOCKET AS DWORD, wsBuf AS WSABUF)AS LONG
    
    LOCAL RESULT_LONG AS LONG
    LOCAL GL_MY_OVERLAPPED AS OVERLAPPED
    LOCAL nFlags AS LONG
    
    RESULT_LONG = WSARecv(ACCEPTING_SOCKET, wsBuf, 1, 0, nFlags, GL_MY_OVERLAPPED, 0)
    
    END FUNCTION
    '------------------------------------------------------------------------------
    THREAD FUNCTION ServerWorkerThread(BYVAL CompletionPortID AS DWORD)AS LONG
    ON ERROR GOTO ERROR_CONTROL
    
    LOCAL RESULT AS LONG
    LOCAL pNumberOfBytes AS LONG
    LOCAL pCompletionKey AS DWORD
    LOCAL COMPLETION_PORT_KEY_POINTER AS COMPLETION_PORT_KEY POINTER
    
    LOCAL lpOVERLAPPED AS OVERLAPPED PTR
    
    LOCAL CONNECTED_SOCKET AS DWORD
    LOCAL DATA_STRING AS STRING
    
    LOCAL FILE_NUMBER AS LONG
    FILE_NUMBER = FREEFILE
    
    OPEN "ServerWorkerThread.txt" FOR OUTPUT SHARED AS file_number
    
    
    DO 'WHILE ISTRUE ISWIN(GL_hDlg)
    
    IF ISTRUE GL_QUIT_FLAG THEN
    MSGBOX "?"
    PRINT #FILE_NUMBER, "CLOSE"
    CLOSE FILE_NUMBER
    EXIT FUNCTION
    END IF
    
    RESULT = GetQueuedCompletionStatus(CompletionPortID, pNumberOfBytes, pCompletionKey, VARPTR(lpOVERLAPPED), %INFINITE)
    IF ISFALSE RESULT THEN
    MSGBOX "ERROR IN ServerWorkerThread - " + FORMAT$(GetLastError)
    END IF
    
    COMPLETION_PORT_KEY_POINTER = pCompletionKey
    CONNECTED_SOCKET = @COMPLETION_PORT_KEY_POINTER.ACCEPTING_SOCKET
    
    'TEXT_TO_SCREEN "CONNECTED SOCKET = " + FORMAT$(CONNECTED_SOCKET)
    
    DATA_STRING = PEEK$(@COMPLETION_PORT_KEY_POINTER.WSA_BUFER.buf, @COMPLETION_PORT_KEY_POINTER.WSA_BUFER.dLen)
    
    
    PRINT #FILE_NUMBER, "[" + FORMAT$(CONNECTED_SOCKET,"00000") + "] " + DATA_STRING
    PRINT #FILE_NUMBER, ""
    
    TEXT_TO_SCREEN "[" + FORMAT$(CONNECTED_SOCKET,"00000") + "] " + DATA_STRING
    
    LOOP
    
    
    
    EXIT FUNCTION
    
    ERROR_CONTROL:
    MSGBOX ERROR$
    
    END FUNCTION
    '------------------------------------------------------------------------------
    FUNCTION TEXT_TO_SCREEN (TEXT_STRING AS STRING) AS LONG
    
    LOCAL LISTBOX_COUNT AS LONG
    
    LISTBOX ADD GL_hDlg, %LISTBOX_VIEWER, " " + TEXT_STRING
    LISTBOX GET COUNT GL_hDlg, %LISTBOX_VIEWER TO LISTBOX_COUNT
    CONTROL SEND GL_hDlg, %LISTBOX_VIEWER, %LB_SETTOPINDEX, LISTBOX_COUNT-1, 0
    
    END FUNCTION

  • #2
    Got to compile using Jose Roca includes by remarking line 200.

    TYPE_SOCKET_ADDRESS.sin_addr.s_addr = inet_addr("192.168.1.193") 'or htonl(%INADDR_ANY) 'LINENUMBER175
    TYPE_SOCKET_ADDRESS.sin_addr.s_addr = inet_addr($host) 'or htonl(%INADDR_ANY)

    127.0.0.1 doesn't work on all machines so used actual ip address on another machine.

    Port is 4070 if you need to port forward to $host

    It is definitely working. Not sure how to write a proper client to take advantage of it.
    Last edited by Mike Doty; 15 Jan 2020, 03:41 AM.
    https://duckduckgo.com instead of google

    Comment


    • #3
      David,

      Extremely interested in this.

      Don't know if clients have to be differently written?
      Is asynchronous also needed with clients?

      Lines displayed in listbox are not truncated to their length (have residue of longest buffer.)

      Incorrect result on line 2:
      1 "Now is the time"
      2 "Aow is the time"

      Code:
      '$host = "192.168.0.3"      'port 4070 forwarded in router and above server program allowed through firewall
      $host = "192.168.1.193"
      
      FUNCTION PBMAIN AS LONG  'this program client3.bas
      
      TCP OPEN PORT 4070 AT $host AS #1
      TCP SEND #1, "Now is the time"
      SLEEP 150  'just making sure while testing
      TCP CLOSE #1
      
      TCP OPEN PORT 4070 AT $host AS #1
      TCP SEND #1, "A"
      SLEEP 150
      TCP CLOSE #1
      
      END FUNCTION
      https://duckduckgo.com instead of google

      Comment


      • #4
        Hi Mike

        Yes, I am using the Jose includes...

        My clients are fairly simple.
        They "call home" and the above listed program answers the call. I have more work to do on it to deal with keeping the conversation going.
        I have static IP addresses and two interconnect accounts so I can actually test this over the internet. My old version using TCP open and one thread per connection has been working fine for years. But I want to see how far this can scale. One thread per connection is not the way to go.

        Clients do not need to be asynchronous. This could be the beginning / comm functions of any TCP server that listens for clients.

        Here is a sample of what is going on in my system:

        Clients calls home.

        Server answers.

        Client sends an ID message, looks like this: "0100,0027,00:06:8E:02:9F:8F" where first data is the command code of the message, second data is the message length, third data (in this case) is the MAC Address.

        I look up the MAC Address in a database to authenticate the caller and get its "device unique id" once authenticated I send the client a "accepted message" like: 00101;0010 - then we can chat back and forth.

        There is a version of this where I encrypt the data on both sides.

        I am going to write a test client to see if I can load test the server. My client computer (the simulator) will have lots of IP Addresses so it can call home on the same port.

        There are a few things I need to check on, aside from making sure it doesn't blow up. A critical one is the THREAD FUNCTION ServerWorkerThread. There is some ambiguity regarding what happens if a bunch of clients call in at the same time and the function is busy. Microsoft says run 2 threads for each cpu up to the max number of cpu's you have. I want to stress test it with just one server thread and figure out what happens when a bunch of are asking to chat.

        I will post more code as it gets done.

        Happy to share! The PB community has been very helpful over the years!



        Comment


        • #5
          If server doesn't get a close it will error 64 (which is trapped.)
          I've used a thread per client for years and would definitely like to see that go away.

          I've always wondered how VPN's use UDP without data loss?
          If you need a tester, count me in.

          'Updated 11:05 AM run twice
          'You responded so quickly wondering if you have a way to synch to this 11:10 AM (probably a coincidence.)

          Code:
          #DIM ALL
          #INCLUDE "win32api.inc"
          
          '$host = "192.168.0.3"
          $host = "192.168.1.193"
          
          GLOBAL gCount AS LONG
          
          FUNCTION PBMAIN AS LONG
           LOCAL x AS LONG
           FOR x = 1 TO 10
            mythread(1)
           NEXT
           ? "Done",%MB_SYSTEMMODAL
          END FUNCTION
          
          FUNCTION mythread(BYVAL x AS LONG) AS LONG
           LOCAL hfile AS LONG
           hfile = FREEFILE
           INCR gCount
           TCP OPEN PORT 4070 AT $host AS #hfile TIMEOUT 5000
           TCP SEND #hfile,STR$(gcount)
           IF ERR THEN ? "open failed":EXIT FUNCTION
           SLEEP 20 'minimum before things go wrong
           'TCP CLOSE #hfile 'intentional
          END FUNCTION
          https://duckduckgo.com instead of google

          Comment


          • #6
            I have to add error checking and house keeping like closing ports etc.
            There is also another function that should be part of the party to check for status...

            I will keep you in the loop Mike!

            Comment


            • #7
              Dr Dobbs - Multithreaded Asynchronous I/O & I/O Completion Ports


              https://www.drdobbs.com/cpp/multithr...mple/201202921

              Comment


              • #8
                Found these links:
                https://forum.powerbasic.com/forum/u...cp-http-server
                https://forum.powerbasic.com/forum/u...for-pbwin-9-02

                https://forum.powerbasic.com/forum/u...mpletion-ports
                https://duckduckgo.com instead of google

                Comment


                • #9
                  For systems with a lot of clients I always use UDP multicast. Easy to program, reliable, and no TCP connection needed...
                  Regards,
                  Peter

                  Comment


                  • #10
                    I hear you Peter - In this case I have little control over the clients calling in, they are TCP IP.

                    Mike - I read all the links you posted, helpful! I am stuck at the moment on WASSend - send data back but first CreateIoCompletionPort unsuccessfully.

                    I have 4 books on the topic and non of them have more than a page or two on the subject of CreateIoCompletionPort. I will have to keep testing.

                    Comment


                    • #11
                      Had an old test example of multicast for those interested. If you start multiple instances, then they all connect...

                      Code:
                      '==============================================================================
                      'Multicast test. Use PBCC5
                      '------------------------------------------------------------------------------
                      #COMPILE EXE
                      #COMPILER PBCC 5
                      #DIM ALL
                      #INCLUDE "win32api.inc"
                      %WSOCK32 = 1
                      #INCLUDE "Wsock_x.inc"
                      
                      $HELLO_GROUP = "233.1.1.1" 'IP address of multicast
                      %HELLO_PORT = 40001 'Port number
                      
                      GLOBAL rxThread, TxPort, RxPort, RxStop, hEvents(), lRx, lTx AS LONG
                      
                      THREAD FUNCTION Rx(BYVAL Rxt AS LONG)AS LONG
                      LOCAL LRet AS LONG, LSock AS LONG, LSockAddr_in AS SockAddr_in, lLen AS LONG
                      LOCAL lSockAddr AS Sockaddr
                      LOCAL LMsg AS STRING,LMReq AS Ip_MReq, RMsg AS STRING * 1024, lOne AS LONG
                      LOCAL lzPtr AS ASCIIZ PTR, lzRxIp AS ASCIIZ * 16, lport AS LONG
                      '----------------------------------------------------------------------------
                      LSock = Socket (%AF_INET,%SOCK_DGRAM,BYVAL 0)
                      IF LSock = %INVALID_SOCKET THEN PRINT "Create err.UDP RX socket" : GOTO EndCall
                      '----------------------------------------------------------------------------
                      LSockAddr_In.Sin_Family = %AF_INET
                      LSockAddr_In.s_Addr = htonl (%INADDR_ANY)
                      LSockAddr_In.Sin_Port = htons (%HELLO_PORT)
                      lOne = 1
                      LRet = SetSockOpt(LSock, %SOL_SOCKET, %SO_REUSEADDR, lOne, SIZEOF(lOne))
                      lRet = Bind (lSock, lSockAddr_In, SIZEOF (lSockAddr_In))
                      IF lRet = %SOCKET_ERROR THEN PRINT "Error on binding RX socket..." : GOTO EndCall
                      '----------------------------------------------------------------------------
                      LMReq.Imr_MultiAddress.s_Addr = Inet_Addr ($HELLO_GROUP)
                      LMreq.Imr_Interface.s_Addr = htonl (%INADDR_ANY)
                      LRet = SetSockOpt(LSock, %IPPROTO_IP, %IP_ADD_MEMBERSHIP, LMReq, SIZEOF(LMReq))
                      IF LRet < 0 THEN PRINT "Could not join multicast group" : GOTO EndCall
                      '----------------------------------------------------------------------------
                      LSockAddr_In.Sin_Family = %AF_INET
                      LSockAddr_In.s_Addr = htonl (%INADDR_ANY)
                      LSockAddr_In.Sin_Port = htons (%HELLO_PORT)
                      lPort = ntohs(lsockaddr_in.Sin_Port)
                      PRINT "Receiving port: " & FORMAT$(lPort) & ". Rx Waiting on packets...."
                      WHILE RxStop = 0
                      rMsg = ""
                      lLen = LEN (rMsg)
                      LRet = RecvFrom (LSock, rMsg, 1024, 0, lSockAddr_in, SIZEOF (lSockAddr_in))
                      RxPort = ntohs(lsockaddr_in.Sin_Port)
                      IF (LRet > 0) AND (TxPort <> RxPort) THEN
                      lzPtr = Inet_NtoA (BYVAL LSockAddr_In.s_addr)
                      IF lzPtr THEN lzRxIp = @lzPtr
                      RxPort = ntohs(lsockaddr_in.Sin_Port)
                      IF RxPort <> TxPort THEN
                      IF LEFT$(rmsg,1) = CHR$(27) THEN
                      PRINT "--> bytes: " + FORMAT$(lRet,"") + _
                      " from: " + lzRxIp + ":" + FORMAT$(RxPort) + ", msg: " & "Leaving session"
                      ELSE
                      PRINT "--> bytes: " + FORMAT$(lRet,"") + _
                      " from: " + lzRxIp + ":" + FORMAT$(RxPort) + ", msg: " & LEFT$(rMsg,lret)
                      END IF
                      END IF
                      END IF '
                      WEND
                      EndCall:
                      LRet = SetSockOpt(LSock, %IPPROTO_IP, %IP_DROP_MEMBERSHIP, LMReq, SIZEOF(LMReq))
                      PRINT "Rx:Leaving session"
                      END FUNCTION
                      
                      FUNCTION PBMAIN()AS LONG
                      LOCAL LRet AS DWORD, LWsaData AS WsaData, lOne AS LONG
                      LOCAL LSock AS LONG, LSockAddr_in AS SockAddr_in, lTTL AS LONG, lSockout AS SockAddr_In
                      LOCAL LDst_SockAddr AS SockAddr_In, lLen AS LONG, lzStr AS ASCIIZ * 256
                      LOCAL LIn_Addr AS In_Addr,LMsg AS ASCIIZ * 256,LM AS ASCIIZ * 64
                      LOCAL LMReq AS Ip_MReq,KeyString AS STRING,lString AS STRING
                      LOCAL lZCliIp AS ASCIIZ * 15, lZPtr AS ASCIIZ PTR, Keys AS ASCIIZ * 1024
                      '----------------------------------------------------------------------------
                      LRet = WSAStartUp (&h0202,LWsaData)
                      IF LRet <> 0 THEN PRINT "Error initializing Winsock DLL" : GOTO EndCall
                      '----------------------------------------------------------------------------
                      LDst_SockAddr.Sin_Family = %AF_INET
                      LDst_SockAddr.s_Addr = htonl(%INADDR_ANY)
                      LDst_SockAddr.Sin_port = 0
                      '----------------------------------------------------------------------------
                      LSock = Socket (%AF_INET,%SOCK_DGRAM,BYVAL 0)
                      IF LSock = %INVALID_SOCKET THEN PRINT "Create err.UDP socket" : GOTO EndCall
                      '----------------------------------------------------------------------------
                      PRINT "lSock: " & FORMAT$(lSock)
                      lOne = 1
                      LRet = SetSockOpt(LSock, %SOL_SOCKET, %SO_REUSEADDR, lOne, SIZEOF(lOne))
                      lRet = Bind (lSock, lDST_SockAddr, SIZEOF (LDST_SockAddr))
                      IF lRet = %SOCKET_ERROR THEN lRet = WSAGetLastError : PRINT "Error on TX binding..."+STR$(lRet) : GOTO EndCall
                      lTTL = 255
                      lRet = SetSockOpt(lSock, BYVAL %IPPROTO_IP, BYVAL %IPP_MULTICAST_TTL, BYREF lTTl , SIZEOF(lTTl))
                      IF lRet <> 0 THEN lRet = WSAGetLastError : PRINT "TTL error:" + STR$(lRet) :GOTO EndCall
                      IF lRet <> 0 THEN PRINT "Cannot set time-to-live option" : GOTO EndCall
                      lTTl = 1
                      '----------------------------------------------------------------------------
                      LDst_SockAddr.Sin_Family = %AF_INET
                      LDst_SockAddr.s_Addr = Inet_addr ($HELLO_GROUP)
                      LDst_SockAddr.Sin_port = htons (%HELLO_PORT)
                      lRet = GetSockName (lSock, lSockOut, SIZEOF(lSockOut))
                      IF lRet THEN PRINT "Error GetSockName"
                      
                      lZPtr = Inet_NtoA (BYVAL LSockOut.s_addr) 'and originating port
                      IF lZPtr THEN lZCliIp = @lZPtr '
                      TxPort = ntohs(lSockOut.Sin_port)
                      PRINT "IP Address: " + lZCLIIP + ":" + FORMAT$(TxPort)
                      PRINT "Waiting on input. <Esc> = end, <Enter> = send data
                      THREAD CREATE Rx(1) TO RxThread
                      WHILE %TRUE
                      Keys = ""
                      WHILE NOT INSTAT : SLEEP 10 : WEND
                      KeyString = INKEY$
                      IF INSTR(KeyString, ANY CHR$(13,27)) THEN
                      IF INSTR(KeyString,CHR$(27)) THEN lString = lString + CHR$(27)
                      INCR lTx
                      IF (INSTR(KeyString, ANY CHR$(13)) = 0) THEN lString = lString + KeyString
                      lLen = LEN (lString)
                      LRet = Sendto (LSock, BYVAL STRPTR(lString), BYVAL lLen,0, LDst_SockAddr, SIZEOF (LDst_SockAddr))
                      lZPtr = Inet_NtoA (LDst_SockAddr.s_addr) 'and originating port
                      IF lZPtr THEN lZCliIp = @lZPtr '
                      IF LRet < 0 THEN lRet = WSAGetLastError :PRINT "Error during sendto "+STR$(lRet) : GOTO EndCall
                      IF LEFT$(lString,1) <> CHR$(27) THEN PRINT "<-- bytes: " + FORMAT$(lRet,"000") TAB(45) "msg: " + lString
                      lString = ""
                      IF INSTR(KeyString,CHR$(27)) THEN EXIT LOOP
                      ELSE
                      lString = lString + KeyString
                      SLEEP 10
                      END IF
                      WEND
                      EndCall:
                      RxStop = %TRUE
                      PRINT "TX:Leaving session"
                      PRINT "Waiting on RX thread to end..."
                      TerminateThread RxThread, 0
                      
                      SELECT CASE WaitForSingleObject (RxThread, 2000)
                      CASE %WAIT_OBJECT_0 : PRINT "Thread released"
                      CASE %WAIT_TIMEOUT : PRINT "Cannot stop RX thread"
                      END SELECT
                      SLEEP 1000
                      WSACleanUp
                      END FUNCTION
                      Attached Files
                      Last edited by Peter Lameijn; 16 Jan 2020, 08:44 AM.
                      Regards,
                      Peter

                      Comment


                      • #12
                        If anyone figures out how to convert from PBCC5 to PBCC6/PBWIN10, please post.
                        I failed in the past using this include file.
                        This could replace a service running as a TCP server returning SQL recordsets to clients?
                        https://duckduckgo.com instead of google

                        Comment


                        • #13
                          Nice Peter! One issue I have read about is that WaitForSingleObject can only be waiting for 64 objects per thread. I am trying to get closer to 900 simultaneous connections. Same issue with Select, I think. I believe one needs to go to either Callback Routines or Completion Ports.

                          Comment


                          • #14
                            David, Did you get it to compile? I will have to see if I have PBCC 5 in the archives. I really don't want to use a modified older include file of winsock functions.
                            https://duckduckgo.com instead of google

                            Comment


                            • #15
                              The Wsock_x.inc is a combination of both winsock include files. The original files should also work...
                              Regards,
                              Peter

                              Comment


                              • #16
                                I wish they did.
                                https://duckduckgo.com instead of google

                                Comment


                                • #17
                                  Problem is that some declares are missing in the winsock1 include, and some missing in the winsock2 include.
                                  That's why I put them together...
                                  Regards,
                                  Peter

                                  Comment


                                  • #18
                                    Originally posted by David Clarke View Post
                                    Nice Peter! One issue I have read about is that WaitForSingleObject can only be waiting for 64 objects per thread. I am trying to get closer to 900 simultaneous connections. Same issue with Select, I think. I believe one needs to go to either Callback Routines or Completion Ports.
                                    You only have 1 thread per program, so more isn't needed...
                                    (you can omit select and waitforsingle object; terminatethread will kill it then.)
                                    For the remote clients: just send them a message to let them terminate themselves...
                                    Regards,
                                    Peter

                                    Comment


                                    • #19
                                      Peter,
                                      Do you know if these values changed in winsock2?
                                      Hopefully, I'll get past what doesn't compile converting to winsock2.

                                      'wsock_x.inc
                                      %SIOCSHIWAT = &h80047300 'set high watermark
                                      %SIOCGHIWAT = &h40047301 'get high watermark
                                      %SIOCSLOWAT = &h80047302 'set low watermark
                                      %SIOCGLOWAT = &h40047303 'get low waterm
                                      %SIOCATMARK = &h40047307 'at oob mark?

                                      'winsock2.inc (Jose Roca's)
                                      %SIOCSHIWAT = &H80047300??? ' _IOW('s', 0, u_long) /* set high watermark */
                                      %SIOCGHIWAT = &H80047301??? ' _IOR('s', 1, u_long) /* get high watermark */
                                      %SIOCSLOWAT = &H80047302??? ' _IOW('s', 2, u_long) /* set low watermark */
                                      %SIOCGLOWAT = &H80047303??? ' _IOR('s', 3, u_long) /* get low watermark */
                                      %SIOCATMARK = &H80047307??? ' _IOR('s', 7, u_long) /* at oob mark? */
                                      https://duckduckgo.com instead of google

                                      Comment


                                      • #20
                                        I don't use those, just copied them from the existing includes...
                                        Regards,
                                        Peter

                                        Comment

                                        Working...
                                        X