Using FN_writesocket from Socket Lib

Discussions related to network, internet and socket programming; also to serial, parallel, Bluetooth, USB etc.
vela025
Posts: 9
Joined: Thu 08 Feb 2024, 11:11

Re: Using FN_writesocket from Socket Lib

Post by vela025 »

Hated Moron wrote: Fri 09 Feb 2024, 17:02 so I must say I'm puzzled that you seem ready to give up.
I'm not sure how I would do this
Hated Moron wrote: Fri 09 Feb 2024, 17:02 You would also have to take care to support input editing, for example the user typing 'backspace' and the server keeping track of the edits, so that what the user sees on his terminal is always the same as what the server has in its buffer.
I was using the chat example and thought that the best way might be to create a temporary file and store each input into the file then send each bit back to the client...but then I thought surely that is just too convoluted a way to get a local echo so I'm trying to think of another simpler way. I'm not giving up, but the learning curve is turning out to be a little steeper than I had foreseen.
Hated Moron

Re: Using FN_writesocket from Socket Lib

Post by Hated Moron »

vela025 wrote: Fri 09 Feb 2024, 17:33 I thought surely that is just too convoluted a way to get a local echo so I'm trying to think of another simpler way.
Perhaps I'm missing something obvious, but if your server reads the incoming data one character at a time (and fortunately telnet assumes 7-bit ASCII so in that case character = byte) isn't it just a case of sending each character back to the client as soon as it is received:

Code: Select all

      rcvd% = FN_readsocket(socket%, ^char&, 1)
      IF rcvd% THEN
        sent% = FN_writesocket(socket%, ^char&, 1)
        buffer$ += CHR$(char&)
      ENDIF
I can see that if the client sends multiple characters the server should probably echo multiple characters as well, for reasons of efficiency, but that's not difficult:

Code: Select all

      BUFFER_SIZE = 256
      receive$ = STRING$(BUFFER_SIZE, CHR$0)

      rcvd% = FN_readsocket(socket%, PTR(receive$), BUFFER_SIZE)
      IF rcvd% THEN
        sent% = FN_writesocket(socket%, PTR(receive$), rcvd%)
        buffer$ += LEFT$(receive$, rcvd%)
      ENDIF
I know that some people prefer to create their buffers by allocating memory with DIM, rather than using strings, and that's absolutely fine, but it's easier to end up with an accidental memory-leak.
vela025
Posts: 9
Joined: Thu 08 Feb 2024, 11:11

Re: Using FN_writesocket from Socket Lib

Post by vela025 »

Hated Moron wrote: Fri 09 Feb 2024, 18:06
I can see that if the client sends multiple characters the server should probably echo multiple characters as well, for reasons of efficiency, but that's not difficult:

Code: Select all

      BUFFER_SIZE = 256
      receive$ = STRING$(BUFFER_SIZE, CHR$0)

      rcvd% = FN_readsocket(socket%, PTR(receive$), BUFFER_SIZE)
      IF rcvd% THEN
        sent% = FN_writesocket(socket%, PTR(receive$), rcvd%)
        buffer$ += LEFT$(receive$, rcvd%)
      ENDIF
I know that some people prefer to create their buffers by allocating memory with DIM, rather than using strings, and that's absolutely fine, but it's easier to end up with an accidental memory-leak.

I would have never thought of that, I understand that fn_readsocket doesn't wait for input so should I wrap this in a repeat until receive$=chr$13 to detect the return key?
Hated Moron

Re: Using FN_writesocket from Socket Lib

Post by Hated Moron »

vela025 wrote: Fri 09 Feb 2024, 20:14 I understand that fn_readsocket doesn't wait for input so should I wrap this in a repeat until receive$=chr$13 to detect the return key?
You want to make a server capable of handling multiple concurrent client connections, don't you, so you mustn't have a 'blocking' routine anywhere. Everything must be done using round-robin polling, with probably a single WAIT statement to prevent it using 100% CPU time when in an idle condition. This is the server_multi program to which I previously referred:

Code: Select all

      REM A very simple text server to demonstrate the use of 'socklib'
      REM This version supports up to eight concurrent connections.
      REM v1.0, 14-Nov-2032, Richard Russell http://www.rtrussell.co.uk/

      MAX_CONNECT = 8
      BUFLEN = 256

      VDU 23,22,1024;580;8,20,16,128

      INSTALL @lib$ + "socklib"
      PROC_initsockets

      ON CLOSE PROCcleanup : QUIT
      ON ERROR ON ERROR OFF : PROCcleanup : PRINT REPORT$ : END

      REM Create an array of structures to hold the 'state' associated with each connection:
      DIM state{(MAX_CONNECT-1) socket%, xcaret%, ycaret%, viewport$, buffer&(BUFLEN)}

      REM. Get local IP address:
      myip% = FN_sethost(FN_gethostname)

      REM. Create a 'listening socket':
      listen% = FN_tcplisten(FN_gethostname, "50343")
      IF listen% <= 0 PRINT "Couldn't open listening socket" : END

      PRINT "Waiting for client connections (maximum "; MAX_CONNECT ") to ";
      PRINT ;myip% AND &FF;".";(myip% >> 8) AND &FF;".";(myip% >> 16) AND &FF;".";myip% >>> 24

      REM Set some pastel background colours:
      FOR I% = 8 TO 15
        COLOUR I%, 216+24*(I%AND1), 216+12*(I%AND2), 216+6*(I%AND4)
      NEXT

      REM Initialise the viewports and background colours for each connection:
      state{(0)}.viewport$ = CHR$28 + CHR$00 + CHR$14 + CHR$31  + CHR$01 + CHR$17 + CHR$136
      state{(1)}.viewport$ = CHR$28 + CHR$32 + CHR$14 + CHR$63  + CHR$01 + CHR$17 + CHR$137
      state{(2)}.viewport$ = CHR$28 + CHR$64 + CHR$14 + CHR$95  + CHR$01 + CHR$17 + CHR$138
      state{(3)}.viewport$ = CHR$28 + CHR$96 + CHR$14 + CHR$127 + CHR$01 + CHR$17 + CHR$139
      state{(4)}.viewport$ = CHR$28 + CHR$00 + CHR$28 + CHR$31  + CHR$15 + CHR$17 + CHR$140
      state{(5)}.viewport$ = CHR$28 + CHR$32 + CHR$28 + CHR$63  + CHR$15 + CHR$17 + CHR$141
      state{(6)}.viewport$ = CHR$28 + CHR$64 + CHR$28 + CHR$95  + CHR$15 + CHR$17 + CHR$142
      state{(7)}.viewport$ = CHR$28 + CHR$96 + CHR$28 + CHR$127 + CHR$15 + CHR$17 + CHR$143

      REM Main polling loop, checking for new connections and incoming data from open connections:
      REPEAT
        REM Check for new connection:
        connect% = FN_check_connectionM(listen%)
        IF connect% THEN
          FOR I% = 0 TO MAX_CONNECT-1
            IF FNconnected(connect%, state{(I%)}) EXIT FOR
          NEXT
          IF I% >= MAX_CONNECT PROC_closesocket(connect%)
        ENDIF

        REM Check for data received on open connections:
        active% = FALSE
        FOR I% = 0 TO MAX_CONNECT-1
          active% OR= FNreceived(state{(I%)})
        NEXT

        IF NOT active% WAIT 1
      UNTIL FALSE
      END

      DEF FNconnected(S%, s{})
      IF s.socket% THEN = FALSE
      s.socket% = S%
      PRINT s.viewport$ CHR$12;
      PRINT "Connected to " + FN_getpeername(S%)
      s.xcaret% = POS : s.ycaret% = VPOS
      = TRUE

      DEF FNreceived(s{})
      LOCAL N%, reply$
      IF s.socket% = 0 THEN = FALSE
      N% = FN_readsocket(s.socket%, ^s.buffer&(0), BUFLEN)
      IF N% = 0 THEN = FALSE
      IF N% > 0 THEN
        s.buffer&(N%) = 0
        PRINT s.viewport$ TAB(s.xcaret%, s.ycaret%);
        PRINT s.buffer&();
        s.xcaret% = POS : s.ycaret% = VPOS
        reply$ = "Received." + CHR$&D + CHR$&A
        N% = FN_writesocket(s.socket%, PTR(reply$), LEN(reply$))
      ELSE
        PRINT s.viewport$ TAB(s.xcaret%, s.ycaret%);
        PRINT "Client closed connection."
        PROC_closesocket(s.socket%)
        s.socket% = 0
      ENDIF
      = TRUE

      DEF PROCcleanup
      PROC_exitsockets
      ENDPROC
vela025
Posts: 9
Joined: Thu 08 Feb 2024, 11:11

Re: Using FN_writesocket from Socket Lib

Post by vela025 »

Ok thanks, I'll have a look through that. Is this what it is supposed to do once the enter key is pressed? (put each new character on a seperate indented line)
sockets.PNG
You do not have the required permissions to view the files attached to this post.
Hated Moron

Re: Using FN_writesocket from Socket Lib

Post by Hated Moron »

vela025 wrote: Fri 09 Feb 2024, 21:44 Is this what it is supposed to do once the enter key is pressed? (put each new character on a seperate indented line)
From that I'm guessing you weren't using client.bbc for the client end. Client.bbc always sends CRLF as the line termination (as is defined by the telnet protocol), not LF. LF is the normal line termination in Linux, but that's not compliant.

If you are using server_multi with a different client I would recommend adding this line to explicitly terminate the receive buffer:

Code: Select all

        s.buffer&(N%) = 0
(I've amended the listing).