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.
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:
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:
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.
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:
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?
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:
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
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.
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: