Skip to content

Commit 3cac2b9

Browse files
committed
feat: add periodic lisp-chat/client ping to maintain TCP/IP socket
Fixes #17
1 parent c60dea5 commit 3cac2b9

File tree

2 files changed

+42
-15
lines changed

2 files changed

+42
-15
lines changed

src/client.lisp

Lines changed: 36 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,12 @@
1313
(defvar *io-lock* (make-lock "io mutex")
1414
"I/O Mutex for avoid terminal race conditions")
1515

16+
(defvar *periodic-ping-interval* 30
17+
"Interval in seconds to send a ping to the server")
18+
19+
(defvar *stop* nil
20+
"Stop sign to end the client")
21+
1622
(defun erase-last-line ()
1723
"Erase the last line by using ANSI Escape codes"
1824
(format t "~C[1A~C[2K" #\Esc #\Esc))
@@ -32,26 +38,31 @@
3238
(write-line message (socket-stream socket))
3339
(finish-output (socket-stream socket)))
3440

41+
(defun system-pongp (message)
42+
"SYSTEM-PONGP detect if a pong response was received as systematic send"
43+
(search "[@server]: pong (system)" message))
44+
3545
;; HACK: I don't know a better way to save state of cl-readline
3646
;; before printing messages from server, so I'm cleaning all the stuff
3747
;; before print a new message, and restore again. Maybe there is a
3848
;; better way for doing that.
3949
(defun receive-message (message)
4050
"Receive a message and print in the terminal carefully with IO race conditions"
4151
(with-lock-held (*io-lock*)
42-
(let ((line cl-readline:*line-buffer*)
43-
(prompt cl-readline:+prompt+))
44-
;; erase
45-
(cl-readline:replace-line "" nil)
46-
(cl-readline:set-prompt "")
47-
(cl-readline:redisplay)
48-
;; print message from server
49-
(write-line message)
50-
;; restore
51-
(cl-readline:replace-line (or line "") nil)
52-
(setq cl-readline:*point* cl-readline:+end+)
53-
(cl-readline:set-prompt prompt)
54-
(cl-readline:redisplay))))
52+
(unless (system-pongp message)
53+
(let ((line cl-readline:*line-buffer*)
54+
(prompt cl-readline:+prompt+))
55+
;; erase
56+
(cl-readline:replace-line "" nil)
57+
(cl-readline:set-prompt "")
58+
(cl-readline:redisplay)
59+
;; print message from server
60+
(write-line message)
61+
;; restore
62+
(cl-readline:replace-line (or line "") nil)
63+
(setq cl-readline:*point* cl-readline:+end+)
64+
(cl-readline:set-prompt prompt)
65+
(cl-readline:redisplay)))))
5566

5667
(defun client-sender (socket username)
5768
"Routine to check new messages being typed by the user"
@@ -87,6 +98,14 @@
8798
(send-message username socket)
8899
username))
89100

101+
(defun client-background-ping (socket)
102+
"Maintain TCP/IP connection by sending periodic ping to maintain connection alive.
103+
104+
The systematic pong is consumed and the @server response is not shown in the terminal
105+
"
106+
(loop (sleep *periodic-ping-interval*)
107+
(ignore-errors
108+
(send-message "/ping system" socket))))
90109

91110
(defun client-loop (host port)
92111
"Dispatch client threads for basic functioning system"
@@ -96,7 +115,10 @@
96115
(let ((sender (make-thread (lambda () (client-sender socket username))
97116
:name "client sender"))
98117
(broadcast (make-thread (lambda () (server-broadcast socket))
99-
:name "server broadcast")))
118+
:name "server broadcast"))
119+
(background-ping (make-thread (lambda () (client-background-ping socket))
120+
:name "background ping")))
121+
(join-thread background-ping)
100122
(join-thread sender)
101123
(join-thread broadcast))))
102124

src/server.lisp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
(defvar *uptime* (multiple-value-list (get-decoded-time))
1919
"Uptime of server variable")
2020
(defparameter *commands-names*
21-
'("/users" "/help" "/log" "/quit" "/uptime" "/nick")
21+
'("/users" "/help" "/log" "/quit" "/uptime" "/nick" "/ping")
2222
"Allowed command names to be called by client user")
2323
(defparameter *clients* nil "List of clients")
2424
(defparameter *messages-stack* nil "Messages pending to be send by broadcasting")
@@ -105,6 +105,11 @@
105105
(declare (ignorable client args))
106106
(command-message (format nil "~{~a~^, ~}" (mapcar #'client-name *clients*))))
107107

108+
(defun /ping (client &rest args)
109+
"Return a list separated by commas of the currently logged users"
110+
(declare (ignorable client args))
111+
(command-message (format nil "pong ~a" args)))
112+
108113

109114
(defun /help (client &rest args)
110115
"Show a list of the available commands of lisp-chat"

0 commit comments

Comments
 (0)