TCP/IP Example Demo for LOCAL HOST/CLIENT Applications
#1
We had a discussion about using _CLIPBOARD to deliver info from one running QB64 app to another. Spriggsy brought up the point that method is frowned upon by M$, which recommends using a TCP/IP routine to pass the info.

So I decided to post a way to communicate back and forth between two QB64 programs.

Notes: You will probably need to tell Windows Defender to okay running these apps, as Defender checks for exe files of this nature.

How to use...

1) Copy/Paste the first program to your QB64 IDE.

2) Open a second IDE and copy/paste the second program.

3) Name and save the second program as; Pete2.bas.

4) Compile Pete2.bas or just run Pete2.bas and close it.

5) Go back to the first IDE, and select "Run" (You don't need to name this app. Untitled works just fine.)

What next? Well, the first program will SHELL open Pete.exe. Now you will have two windows opened. Click the first window, and INPUT 1, 2, or 3 at the prompt. After you hit Enter, you will see your choice appear in the second window as either A, B, C, or if you goofed up it will tell you. The second window sends a message back that it completed the task so the first window can loop back to the INPUT statement. Just close them out with the mouse when you are finished.

Code: (Select All)
_SCREENMOVE 0, 0 ' Set up this host window to the left of your desktop.
WIDTH 60, 25
DO
    CLS
    DO UNTIL x ' Stay in loop until window determines if it is the host or client window.
        x = _OPENCLIENT("TCP/IP:1234:localhost") ' Used to establish a TCP/IP routine.
        IF x = 0 THEN
            x = _OPENHOST("TCP/IP:1234") ' Note the host and clinet must have the same 1234 I.D. number.
            a$ = "Opening as host." ' x channel is now open and this window becomes the host.
        ELSE
            a$ = "Opening as client." ' Should not go here for this demo.
        END IF
        PRINT a$
    LOOP

    IF initiate = 0 THEN ' This only needs to be performed once, to open the client window.
        SHELL _HIDE "start pete2.exe" ' Open the client window.
        initiate = -1 ' Switches this block statement off for all subsequent loops.
    END IF

    IF z = 0 THEN ' Initiates an open channel number when zero.
        DO
            z = _OPENCONNECTION(x) ' Checks if host is available to transfer data.
        LOOP UNTIL z
        PRINT "Connection established."
    END IF

    LOCATE 3, 1 ' Okay, time to input something on the host that will be communicated to the client.
    INPUT "Input a number for letter of the alphabet 1, 2, or 3: "; choice
    _KEYCLEAR

    PUT #z, , choice ' Input is now entered into TCP/IP routine.

    DO
        GET #z, , a
    LOOP UNTIL a = -1 ' -1 is the return code from the client we set as: task_complete = -1
    PUT #z, , a ' Now put our -1 value back into the routine. Failure to do so would result in the client not waiting in the GET #x DO/LOOP.
    CLS
LOOP

Save this one as: Pete2.bas and compile it to Pete2.exe.
Code: (Select All)
_SCREENMOVE 600, 0 ' Set up this client window next to your host window.
WIDTH 50, 25
x = _OPENCLIENT("TCP/IP:1234:localhost") ' Used to establish a TCP/IP routine.
PRINT "Opened as client.": PRINT
DO UNTIL x = 0 ' Prevents running if this app is opened without using host.
    DO
        _LIMIT 30
        GET #x, , receive ' Waits until it receives data input from the host.
    LOOP UNTIL receive > 0

    PRINT "You chose the letter: ";

    SELECT CASE receive
        CASE 1
            PRINT "A"
        CASE 2
            PRINT "B"
        CASE 3
            PRINT "C"
        CASE ELSE
            PRINT "Wrong input!"
    END SELECT
    PRINT

    task_complete = -1 ' Send back a task completed message to the host.
    PUT #x, , task_complete
LOOP


@Spriggsy

Please have a look. I'd like to see if there is anything you would like to comment on, add or optimize. What I'm shooting for is making another example entry in our wiki to expand on the use of these QB64 available communication functions.

Pete
Reply
#2
Well your programs work. Can you show an example of the client sending information back to the host and the host responding?
I tried to do a quick modification to do this and it just hangs and I have no clue why. Steve posted a mini-chat example a few months ago that had the program reacting to multiple copies of itself that I was able to modify with a very simple format changes and to add extra command or two but somewhere along the line I'm just missing something and don't know enough to even ask a very clear question about it.
Reply
#3
Thumbs Up 
Telling from the first post, being a transmitter is more complicated than being the receiver. Transmitter requires three QB64PE-only functions and two file handles! There is no "CLOSE" statement, is that statement necessary for those "_OPEN" functions?

Definitely if one of those "_OPEN" functions returns zero then it's no-go as file handle. This is not really to my interest but I should become involved as it could become real useful some day offline.
Reply
#4
I'll have to give this a look-see at some point. We can probably tweak it to not require the child to be spawned from the parent and just have the parent find the child on its own by either using FindWindow or something similar that way you're not being forced into SHELL HIDE, which pauses the original process. You'd be able to use SHELL HIDE DONTWAIT (if you wish, or you could just have the other process already running) and then just check for it existing after a set amount of time. That allows both processes to run without pauses. But, of course, that depends on how you are wanting them to interact. Some may find more use in SHELL HIDE and use the child as a dialog.
Ask me about Windows API and maybe some Linux stuff
Reply
#5
That's the thing, variation. Folks are going to want to use it in different way. We have some interesting things I found to address...

1) SHELL _HIDE without START hangs and you have to close it with Task manager. That's why I went with SHELL _HIDE with START added.

2) SHELL _DONTWAIT is a viable alternative. Seems to work the same and establishes the connection when the client window appears.

3) Do we need SHELL? No. Folks can just open both programs. The host will wait until the client is opened, without any code modification.

For James I'm not quite sure what you want to build with it, but the communication back is being made by that -1 value of the task_complete variable. I mean it sounds to me like you are looking for a demo where the host sends a memo to the client. and then the client sends a memo back.

Pete
Reply
#6
I'm just unclear of how and when data is passed. 'just". Something like your main program: has the user pick a number in the main program, the client receives the number displays whatever you want there and passes a data or a term back to the main program which now uses that transmitted data or term to display something of it's own.

I suppose a selection dialog example would cover it really.
Main program ... hey client display a diaolog "blah blah" with user choices 1,2,3.
Client program ... whoop there it is and here's what they selected.
Main program ... this is the results form that selection.


I suspected that -1 was indeed communicating back but I'm just fuzzy on what is going on.
Reply
#7
Messaging Demo. Follow instructions in Post #1 if it's new to you, except name the second program: Pete3.bas

Program #1
Code: (Select All)
_SCREENMOVE 0, 0 ' Set up this host window to the left of your desktop.
WIDTH 60, 25
DIM AS STRING host_msg, client_msg
DO
    IF initiate = 0 THEN ' This only needs to be performed once, to open the client window.
        DO UNTIL x ' Stay in loop until window determines if it is the host or client window.
            x = _OPENCLIENT("TCP/IP:1234:localhost") ' Used to establish a TCP/IP routine.
            IF x = 0 THEN
                x = _OPENHOST("TCP/IP:1234") ' Note the host and clinet must have the same 1234 I.D. number.
                a$ = "Opening as host." ' x channel is now open and this window becomes the host.
            ELSE
                a$ = "Opening as client." ' Should not go here for this demo.
            END IF
            PRINT a$
        LOOP
        SHELL _DONTWAIT "pete3.exe" ' Open the client window.
        initiate = -1 ' Switches this block statement off for all subsequent loops.
    END IF

    IF z = 0 THEN ' Initiates an open channel number when zero.
        DO
            z = _OPENCONNECTION(x) ' Checks if host is available to transfer data.
        LOOP UNTIL z
        PRINT "Connection established."
        _DELAY 1
        LOCATE 2: PRINT SPACE$(_WIDTH * 2) ' Remove these lines.
        LOCATE 3, 1
    END IF

    ' Okay, time to input something on the host that will be communicated to the client.
    LINE INPUT "Message to client: "; host_msg: PRINT

    PUT #z, , host_msg ' Input is now entered into TCP/IP routine.

    DO
        GET #z, , client_msg
    LOOP UNTIL LEN(client_msg) ' Exits loop when a return msg is received.

    PRINT "Message from client: "; client_msg: PRINT

    host_msg = "": PUT #z, , host_msg$ ' Now put our client value back into the routine. Failure to do so would result in the client not waiting in the GET #x DO/LOOP.
    _KEYCLEAR ' Prevents typing before ready.
LOOP

Program 2: Name as Pete3.bas and compile as Pete3.exe
Code: (Select All)
DIM AS STRING host_msg, client_msg
_SCREENMOVE 600, 0 ' Set up this client window next to your host window.
WIDTH 50, 25
x = _OPENCLIENT("TCP/IP:1234:localhost") ' Used to establish a TCP/IP routine.
PRINT "Opened as client.": PRINT
DO UNTIL x = 0 ' Prevents running if this app is opened without using host.
    DO
        _LIMIT 30
        GET #x, , host_msg ' Waits until it receives message sent from the host.
    LOOP UNTIL LEN(host_msg)

    PRINT "message from host: "; host_msg
    PRINT
    _KEYCLEAR ' Prevents typing before ready.
    LINE INPUT "Message to host: "; client_msg: PRINT

    PUT #x, , client_msg

LOOP

Pete
Reply
#8
Thanks. Cleared up some of what wasn't making sense to me.
Reply
#9
Hmm, it doesn't work for me. . . or i make a mistake. Input not responding.

[Image: Pete-Host-Client2022-10-26-233241.jpg]
Reply
#10
Thanks for posting the pic, because it allowed me to see why.

You have two windows open/ In the pick, look at the title bar. It shows the second window is active. You need to click that first widow to make it active, and then type. Same thing when you reply back. Click the second window before typing your response back.

For Windows users only, I could use the API to activate the relevant window, but I decided not to for this demo, so people wouldn't think it is needed in TCP/IP functions, and also because it would only work properly for Windows users.

Pete
Reply




Users browsing this thread: 7 Guest(s)