Mini Messenger
#1
A lot of folks are curious about how we can get our programs to talk to each other, and curious about how we'd use QB64 to communicate via TCP/IP over a network.  The wiki has a few examples, but they tend to be outdated and simply don't work for me.  (Such as the mini-messenger example in the wiki.)  I figured people might like a working example of how to get all the proper parts working together, so I tried various wiki samples and eventually decided to rework one until I got it to working for me...

The finished code here is working as intended on Windows. (I dunno if it'll work for Linux or Mac users, but I'd love to hear if it does or doesn't.)  Instead of a single set of code which tries to toggle between client and host, I worked this up as two separate sets of code -- one for each.   Copy one set of code into QB64, and then run it.  Then, while that program is still running in the background, copy the second set of code and run it..  Type in either program and watch as they happily communicate with each other without any issues.


THE HOST:

Code: (Select All)
DIM SHARED Users(1 TO 1000) ' array to hold other client info
DIM SHARED NumClients
DIM SHARED out$


PRINT "[Steve's Mini Messenger]"
host = _OPENHOST("TCP/IP:7319") ' no host found, so begin new host
IF host THEN
    PRINT "[Beginning new host chat session!]"
    NumClients = 0
    client = _OPENCLIENT("TCP/IP:7319:localhost")
    IF client = 0 THEN PRINT "ERROR: could not attach host's personal client to host!"
    INPUT "Enter your name:", myname$
    'PRINT #client, myname$ + " connected!"
    PRINT "[Chat session active!]"
ELSE
    PRINT "ERROR: Could not begin new host!"
END IF ' host


DO ' host main loop
    newclient = _OPENCONNECTION(host) ' receive any new connection
    IF newclient THEN
        NumClients = NumClients + 1
        Users(NumClients) = newclient
        PRINT "Welcome to Steve's Mini Messenger!"
    END IF
    FOR i = 1 TO NumClients
        GetMessage Users(i) 'check all clients for a message
        IF out$ <> "" THEN
            l = LEN(out$)
            FOR j = 1 TO NumClients ' distribute incoming messages to all clients
                PUT #Users(j), , l
                PUT #Users(j), , out$
            NEXT
        END IF
    NEXT i

    SendMessage myname$, mymessage$, client
    _LIMIT 30
LOOP


SUB GetMessage (client) ' get & display any new message
    GET #client, , l
    IF l > 0 THEN
        out$ = SPACE$(l)
        GET #client, , out$
        VIEW PRINT 1 TO 20
        LOCATE 20, 1
        PRINT out$
        VIEW PRINT 1 TO 24
    ELSE
        out$ = ""
    END IF
END SUB

SUB SendMessage (myname$, mymessage$, client) ' simple input handler
    k$ = INKEY$
    IF LEN(k$) THEN
        IF k$ = CHR$(8) AND LEN(mymessage$) <> 0 THEN
            mymessage$ = LEFT$(mymessage$, LEN(mymessage$) - 1)
        ELSE
            IF LEN(k$) = 1 AND ASC(k$) >= 32 THEN mymessage$ = mymessage$ + k$
        END IF
    END IF
    VIEW PRINT 1 TO 24
    LOCATE 22, 1: PRINT SPACE$(80); ' erase previous message displayed
    LOCATE 22, 1: PRINT myname$ + ": "; mymessage$;
    IF k$ = CHR$(13) THEN ' [Enter] sends the message
        IF mymessage$ = "" THEN SYSTEM ' [Enter] with no message ends program
        mymessage$ = myname$ + ":" + mymessage$
        l = LEN(mymessage$)
        PUT #client, , l
        PUT #client, , mymessage$
        mymessage$ = ""
    END IF
    IF k$ = CHR$(27) THEN SYSTEM ' [Esc] key ends program
END SUB


THE CLIENT:

Code: (Select All)
DIM SHARED out$


PRINT "[Steve's Mini Messenger]"
client = _OPENCLIENT("TCP/IP:7319:localhost") ' Attempt to connect to local host as a client
PRINT "[connected to " + _CONNECTIONADDRESS(client) + "]"

INPUT "Enter your name: ", myname$
out$ = myname$ + " connected!"
l = LEN(out$)
PUT #client, , l
PUT #client, , out$
DO
    GetMessage client
    SendMessage myname$, mymessage$, client ' display current input on screen
    _LIMIT 30
LOOP

'.................... END OF MAIN PROGRAM ................


SUB GetMessage (client) ' get & display any new message
    GET #client, , l
    IF l > 0 THEN
        out$ = SPACE$(l)
        GET #client, , out$
        VIEW PRINT 1 TO 20
        LOCATE 20, 1
        PRINT out$
        VIEW PRINT 1 TO 24
    ELSE
        out$ = ""
    END IF
END SUB

SUB SendMessage (myname$, mymessage$, client) ' simple input handler
    k$ = INKEY$
    IF LEN(k$) THEN
        IF k$ = CHR$(8) AND LEN(mymessage$) <> 0 THEN
            mymessage$ = LEFT$(mymessage$, LEN(mymessage$) - 1)
        ELSE
            IF LEN(k$) = 1 AND ASC(k$) >= 32 THEN mymessage$ = mymessage$ + k$
        END IF
    END IF
    VIEW PRINT 1 TO 24
    LOCATE 22, 1: PRINT SPACE$(80); ' erase previous message displayed
    LOCATE 22, 1: PRINT myname$ + ": "; mymessage$;
    IF k$ = CHR$(13) THEN ' [Enter] sends the message
        IF mymessage$ = "" THEN SYSTEM ' [Enter] with no message ends program
        mymessage$ = myname$ + ":" + mymessage$
        l = LEN(mymessage$)
        PUT #client, , l
        PUT #client, , mymessage$
        mymessage$ = ""
    END IF
    IF k$ = CHR$(27) THEN SYSTEM ' [Esc] key ends program
END SUB


Have fun playing around with this as a local system messenger program.  Try it out, kick it around, and let me know if there's anything you don't understand about what it's doing.  This isn’t exactly how I'd normally write one of these; but that's because I started with what the wiki had and then gutted it and rebuilt it up until it was actually working  for me as it should.  Honestly, I think I would've been better off to have just wrote the whole program from scratch!  Tongue
Reply
#2
And, here's a version posted by Zeppelin which combines both utilities into one:

Code: (Select All)
DIM SHARED Users(1 TO 1000) ' array to hold other client info
DIM SHARED NumClients
DIM SHARED OUT$


PRINT "[Steve's Mini Messenger]"
host = _OPENHOST("TCP/IP:7319") ' no host found, so begin new host
IF host THEN
    PRINT "[Beginning new host chat session!]"
    NumClients = 0
    client = _OPENCLIENT("TCP/IP:7319:localhost")
    IF client = 0 THEN PRINT "ERROR: could not attach host's personal client to host!"
    INPUT "Enter your name:", myname$
    'PRINT #client, myname$ + " connected!"
    PRINT "[Chat session active!]"
ELSE
    PRINT "ERROR: Could not begin new host!"
    PRINT "JOINING as CLIENT"

    'Join as client
    client = _OPENCLIENT("TCP/IP:7319:localhost") ' Attempt to connect to local host as a client
END IF ' host

IF host THEN
    _TITLE "HOST"
    DO ' host main loop
        newclient = _OPENCONNECTION(host) ' receive any new connection
        IF newclient THEN
            NumClients = NumClients + 1
            Users(NumClients) = newclient
            PRINT "Welcome to Steve's Mini Messenger!"
        END IF
        FOR i = 1 TO NumClients
            GetMessage Users(i) 'check all clients for a message
            IF OUT$ <> "" THEN
                l = LEN(OUT$)
                FOR j = 1 TO NumClients ' distribute incoming messages to all clients
                    PUT #Users(j), , l
                    PUT #Users(j), , OUT$
                NEXT
            END IF
        NEXT i

        SendMessage myname$, mymessage$, client
        _LIMIT 30
    LOOP
END IF

IF client THEN
    INPUT "Enter your name: ", myname$
    OUT$ = myname$ + " connected!"
    l = LEN(OUT$)
    PUT #client, , l
    PUT #client, , OUT$

    _TITLE "CLIENT"
    DO
        GetMessage client
        SendMessage myname$, mymessage$, client ' display current input on screen
        _LIMIT 30
    LOOP
END IF


SUB GetMessage (client) ' get & display any new message
    GET #client, , l
    IF l > 0 THEN
        OUT$ = SPACE$(l)
        GET #client, , OUT$
        VIEW PRINT 1 TO 20
        LOCATE 20, 1
        PRINT OUT$
        VIEW PRINT 1 TO 24
    ELSE
        OUT$ = ""
    END IF
END SUB

SUB SendMessage (myname$, mymessage$, client) ' simple input handler
    k$ = INKEY$
    IF LEN(k$) THEN
        IF k$ = CHR$(8) AND LEN(mymessage$) <> 0 THEN
            mymessage$ = LEFT$(mymessage$, LEN(mymessage$) - 1)
        ELSE
            IF LEN(k$) = 1 AND ASC(k$) >= 32 THEN mymessage$ = mymessage$ + k$
        END IF
    END IF
    VIEW PRINT 1 TO 24
    LOCATE 22, 1: PRINT SPACE$(80); ' erase previous message displayed
    LOCATE 22, 1: PRINT myname$ + ": "; mymessage$;
    IF k$ = CHR$(13) THEN ' [Enter] sends the message
        IF mymessage$ = "" THEN SYSTEM ' [Enter] with no message ends program
        mymessage$ = myname$ + ":" + mymessage$
        l = LEN(mymessage$)
        PUT #client, , l
        PUT #client, , mymessage$
        mymessage$ = ""
    END IF
    IF k$ = CHR$(27) THEN SYSTEM ' [Esc] key ends program
END SUB
Reply
#3
thank you. it's excellent. it would be useful to have a zip package when there are many source codes...
Reply
#4
Neato. I was able to add "advanced" features like incoming chat being tabbed over and sending a beep as part of a message in seconds. Fighting the urge to write an eliza chat-bot.
Reply
#5
I've searched for a while now and I can't find something like a messenger to share over the internet and not just local. has that been done on QB64? Was hoping to make something like shared inventory. Can anyone offer any direction?

Thanks,
-R
Reply
#6
(05-01-2022, 10:33 PM)SMcNeill Wrote: And, here's a version posted by Zeppelin which combines both utilities into one:

Code: (Select All)
DIM SHARED Users(1 TO 1000) ' array to hold other client info
DIM SHARED NumClients
DIM SHARED OUT$


PRINT "[Steve's Mini Messenger]"
host = _OPENHOST("TCP/IP:7319") ' no host found, so begin new host
IF host THEN
    PRINT "[Beginning new host chat session!]"
    NumClients = 0
    client = _OPENCLIENT("TCP/IP:7319:localhost")
    IF client = 0 THEN PRINT "ERROR: could not attach host's personal client to host!"
    INPUT "Enter your name:", myname$
    'PRINT #client, myname$ + " connected!"
    PRINT "[Chat session active!]"
ELSE
    PRINT "ERROR: Could not begin new host!"
    PRINT "JOINING as CLIENT"

    'Join as client
    client = _OPENCLIENT("TCP/IP:7319:localhost") ' Attempt to connect to local host as a client
END IF ' host

IF host THEN
    _TITLE "HOST"
    DO ' host main loop
        newclient = _OPENCONNECTION(host) ' receive any new connection
        IF newclient THEN
            NumClients = NumClients + 1
            Users(NumClients) = newclient
            PRINT "Welcome to Steve's Mini Messenger!"
        END IF
        FOR i = 1 TO NumClients
            GetMessage Users(i) 'check all clients for a message
            IF OUT$ <> "" THEN
                l = LEN(OUT$)
                FOR j = 1 TO NumClients ' distribute incoming messages to all clients
                    PUT #Users(j), , l
                    PUT #Users(j), , OUT$
                NEXT
            END IF
        NEXT i

        SendMessage myname$, mymessage$, client
        _LIMIT 30
    LOOP
END IF

IF client THEN
    INPUT "Enter your name: ", myname$
    OUT$ = myname$ + " connected!"
    l = LEN(OUT$)
    PUT #client, , l
    PUT #client, , OUT$

    _TITLE "CLIENT"
    DO
        GetMessage client
        SendMessage myname$, mymessage$, client ' display current input on screen
        _LIMIT 30
    LOOP
END IF


SUB GetMessage (client) ' get & display any new message
    GET #client, , l
    IF l > 0 THEN
        OUT$ = SPACE$(l)
        GET #client, , OUT$
        VIEW PRINT 1 TO 20
        LOCATE 20, 1
        PRINT OUT$
        VIEW PRINT 1 TO 24
    ELSE
        OUT$ = ""
    END IF
END SUB

SUB SendMessage (myname$, mymessage$, client) ' simple input handler
    k$ = INKEY$
    IF LEN(k$) THEN
        IF k$ = CHR$(8) AND LEN(mymessage$) <> 0 THEN
            mymessage$ = LEFT$(mymessage$, LEN(mymessage$) - 1)
        ELSE
            IF LEN(k$) = 1 AND ASC(k$) >= 32 THEN mymessage$ = mymessage$ + k$
        END IF
    END IF
    VIEW PRINT 1 TO 24
    LOCATE 22, 1: PRINT SPACE$(80); ' erase previous message displayed
    LOCATE 22, 1: PRINT myname$ + ": "; mymessage$;
    IF k$ = CHR$(13) THEN ' [Enter] sends the message
        IF mymessage$ = "" THEN SYSTEM ' [Enter] with no message ends program
        mymessage$ = myname$ + ":" + mymessage$
        l = LEN(mymessage$)
        PUT #client, , l
        PUT #client, , mymessage$
        mymessage$ = ""
    END IF
    IF k$ = CHR$(27) THEN SYSTEM ' [Esc] key ends program
END SUB

Hello, I'm trying to run this program on two computers in my own house, but it does not work.
I tried to change the line TCP/IP:7319:localhost and I tried 8080:192.168.1.23 (the host's IP) but it is useless.

What am I doing wrong?

Thank you very much.
10 PRINT "Hola! Smile"
20 GOTO 10
Reply
#7
(12-08-2022, 05:42 PM)Ikerkaz Wrote: Hello, I'm trying to run this program on two computers in my own house, but it does not work.
I tried to change the line TCP/IP:7319:localhost and I tried 8080:192.168.1.23 (the host's IP) but it is useless.

What am I doing wrong?

Thank you very much.

I haven't tried to reproduce what you're doing with 2 computers on the same home network yet, but my first though it that they both need to still use the same ICP/IP port to communicate. Try TCP/IP:7319:localhost and TCP/IP:7319:192.168.1.23, and let me know how that goes for you. (If there's any issues, disable firewall for few minutes on both PCs, if you can, just to rule out that it's not being a cause of problems with you, and then don't forget to re-enable if after testing.) Wink
Reply
#8
Funny, I typed in Pete for Window 1, and Repete for window 2, but then Re appeared in the first window. It shouldn't do that until the name is entered and the initial message is sent. So, just a small glitch.

You might want to add either a _screenclick or min/restore routine so the windows trade focus. See the Keyword of the day link here:

https://staging.qb64phoenix.com/showthread.php?tid=1238

See my messenger code TCP/IP alternative to the _CLIPBOARD$ keyword in that thread.

I would recommending expanding _SCREENCLICK in that demo to recognize when a window is moved though. Obviously I can't get into that much detail in an explanation demo, but Windows API offers some pretty easy to make ways to tell QB64 where the top-left of your program window is located as well as using _SCREENY and _SCREENX in QB64. With that addition, you won't be blind clicking on your desktop if a window gets moved.

Also, you can use Win32 API to trade focus if you min/restore. That can be partially done with _SCREENICON, too. The WinAPI only example I have at my Messenger app at the QBasic Forum: https://www.tapatalk.com/groups/qbasic/t...39735.html

Fun stuff, glad you got yours working. If it plays nice with mine we start our own DOScord channel!

Pete
Reply
#9
(12-08-2022, 06:10 PM)SMcNeill Wrote:
(12-08-2022, 05:42 PM)Ikerkaz Wrote: Hello, I'm trying to run this program on two computers in my own house, but it does not work.
I tried to change the line TCP/IP:7319:localhost and I tried 8080:192.168.1.23 (the host's IP) but it is useless.

What am I doing wrong?

Thank you very much.

I haven't tried to reproduce what you're doing with 2 computers on the same home network yet, but my first though it that they both need to still use the same ICP/IP port to communicate.  Try TCP/IP:7319:localhost  and TCP/IP:7319:192.168.1.23, and let me know how that goes for you.  (If there's any issues, disable firewall for few minutes on both PCs, if you can, just to rule out that it's not being a cause of problems with you, and then don't forget to re-enable if after testing.)  Wink

When I open the program both times in the same computer, one is titled "HOST" and the other is titled "CLIENT", but when I open in two different computers both are titled "HOST". I tried what you said changing IPs, but it does not work Sad

EDIT: I changed the code and reprogrammed some parts using TCP/IP:300:192.168.1.23 and now it works !!!!! Big Grin
10 PRINT "Hola! Smile"
20 GOTO 10
Reply




Users browsing this thread: 7 Guest(s)