Skip to content

Commit fc140dd

Browse files
committed
General refactor of server.lisp and add docstrings
1 parent 6db1bed commit fc140dd

File tree

1 file changed

+84
-35
lines changed

1 file changed

+84
-35
lines changed

src/server.lisp

Lines changed: 84 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -2,112 +2,136 @@
22
;; Manoel Vilela
33

44
(defpackage :lisp-chat-server
5-
(:use :usocket :cl :lisp-chat-config)
5+
(:use :usocket :cl :lisp-chat-config :sb-thread)
66
(:export :main))
77

88
(in-package :lisp-chat-server)
99

10-
;; constants
11-
;; (don't use defconstant because create a bug as ASDF system compile time)
12-
(defparameter +commands-names+ '("/users" "/help" "/log" "/quit" "/uptime"))
13-
(defparameter +day-names+ '("Monday" "Tuesday" "Wednesday"
14-
"Thursday" "Friday" "Saturday" "Sunday"))
15-
(defparameter +uptime+ (multiple-value-list (get-decoded-time)))
1610

1711
;; global vars
12+
(defvar *day-names* '("Monday" "Tuesday" "Wednesday"
13+
"Thursday" "Friday" "Saturday" "Sunday"))
14+
(defvar *uptime* (multiple-value-list (get-decoded-time)))
15+
(defparameter *commands-names* '("/users" "/help" "/log" "/quit" "/uptime"))
1816
(defparameter *clients* nil)
1917
(defparameter *messages-stack* nil)
2018
(defparameter *messages-log* nil)
21-
19+
(defparameter *server-nickname* "@server")
2220

2321
;; thread control
24-
(defvar *message-semaphore* (sb-thread:make-semaphore :name "message semaphore"
22+
(defvar *message-semaphore* (make-semaphore :name "message semaphore"
2523
:count 0))
26-
(defvar *client-mutex* (sb-thread:make-mutex :name "client list mutex"))
24+
(defvar *client-mutex* (make-mutex :name "client list mutex"))
25+
2726

2827

2928
(defstruct message
30-
from content time)
29+
"This structure abstract the type message with is saved
30+
into *messages-log* and until consumed, temporally pushed
31+
to *messages-stack*. FROM, CONTENT and TIME has type string"
32+
from
33+
content
34+
time )
3135

3236
(defstruct client
33-
name socket address)
37+
"This structure handle the creation/control of the clients of the server.
38+
NAME is a string. Socket is a USOCKET:SOCKET and address is a ipv4 encoded
39+
string. "
40+
name
41+
socket
42+
address)
3443

3544

3645
(defun socket-peer-address (socket)
46+
"Given a USOCKET:SOCKET instance return a ipv4 encoded IP string"
3747
(format nil "~{~a~^.~}\:~a"
3848
(map 'list #'identity (get-peer-address socket))
3949
(get-peer-port socket)))
4050

4151
(defun client-stream (c)
52+
"Select the stream IO from the client"
4253
(socket-stream (client-socket c)))
4354

4455

4556
(defun debug-format (&rest args)
57+
"If *debug* from lisp-chat-config is true, print debug info on
58+
running based on ARGS"
4659
(if *debug*
4760
(apply #'format args)))
4861

4962

5063
(defun get-time ()
64+
"Return a encoded string as HH:MM:SS based on the current timestamp."
5165
(multiple-value-bind (second minute hour)
5266
(get-decoded-time)
5367
(format nil "~2,'0d:~2,'0d:~2,'0d" hour minute second)))
5468

5569

5670
(defun formated-message (message)
71+
"The default message format of this server. MESSAGE is a string
72+
Changing this reflects all the layout from client/server.
73+
Probably this would be the MFRP: Manoel Fucking Raw Protocol.
74+
Because this we can still use netcat as client for lisp-chat."
5775
(format nil "|~a| [~a]: ~a"
5876
(message-time message)
5977
(message-from message)
6078
(message-content message)))
6179

6280
(defun command-message (content)
63-
(let* ((from "@server")
81+
"This function prepare the CONTENT as a message by the @server"
82+
(let* ((from *server-nickname*)
6483
(time (get-time))
6584
(message (make-message :from from :content content :time time)))
6685
(formated-message message)))
6786

6887
(defun call-command-by-name (string)
88+
"Wow, this is a horrible hack to get a string as symbol for functions/command
89+
like /help /users /log and so on."
6990
(funcall (find-symbol (string-upcase string) :lisp-chat-server)))
7091

7192
;; user commands prefixed with /
7293
(defun /users ()
94+
"Return a list separated by commas of the currently logged users"
7395
(command-message (format nil "~{~a~^, ~}" (mapcar #'client-name *clients*))))
7496

7597

7698
(defun /help ()
77-
(command-message (format nil "~{~a~^, ~}" +commands-names+)))
99+
"Show a list of the available commands of lisp-chat"
100+
(command-message (format nil "~{~a~^, ~}" *commands-names*)))
78101

79102

80103
(defun /log (&optional (depth 20))
104+
"Show the last messages typed on the server.
105+
DEPTH is optional number of messages frames from log"
81106
(format nil "~{~a~^~%~}" (reverse (subseq *messages-log* 0
82107
(min depth (length *messages-log*))))))
83108

84109
(defun /uptime ()
110+
"Return a string nice encoded to preset the uptime since the server started."
85111
(multiple-value-bind
86112
(second minute hour date month year day-of-week dst-p tz)
87-
(values-list +uptime+)
113+
(values-list *uptime*)
88114
(declare (ignore dst-p))
89115
(command-message
90116
(format nil
91117
"Server online since ~2,'0d:~2,'0d:~2,'0d of ~a, ~2,'0d/~2,'0d/~d (GMT~@d)"
92-
hour
93-
minute
94-
second
95-
(nth day-of-week +day-names+)
96-
month
97-
date
98-
year
118+
hour minute second
119+
(nth day-of-week *day-names*)
120+
month date year
99121
(- tz)))))
100122

101123

102124
(defun push-message (from content)
125+
"Push a messaged FROM as CONTENT into the *messages-stack*"
103126
(push (make-message :from from
104127
:content content
105128
:time (get-time))
106129
*messages-stack*)
107-
(sb-thread:signal-semaphore *message-semaphore*))
130+
(signal-semaphore *message-semaphore*))
108131

109132
(defun client-delete (client)
110-
(sb-thread:with-mutex (*client-mutex*)
133+
"Delete a CLIENT from the list *clients*"
134+
(with-mutex (*client-mutex*)
111135
(setf *clients* (remove-if (lambda (c)
112136
(equal (client-address c)
113137
(client-address client)))
@@ -120,14 +144,18 @@
120144
(socket-close (client-socket client)))
121145

122146
(defun send-message (client message)
147+
"Send to CLIENT a MESSAGE :type string"
123148
(let ((stream (client-stream client)))
124149
(write-line message stream)
125150
(finish-output stream)))
126151

127152
(defun client-reader-routine (client)
153+
"This function create a IO-bound procedure to act
154+
by reading the events of a specific CLIENT.
155+
On this software each client talks on your own thread."
128156
(loop for message = (read-line (client-stream client))
129157
while (not (equal message "/quit"))
130-
if (member message +commands-names+ :test #'equal)
158+
if (member message *commands-names* :test #'equal)
131159
do (send-message client (call-command-by-name message))
132160
else
133161
when (> (length message) 0)
@@ -136,6 +164,8 @@
136164
finally (client-delete client)))
137165

138166
(defun client-reader (client)
167+
"This procedure is a wrapper for CLIENT-READER-ROUTINE
168+
treating all the possible errors based on HANDLER-CASE macro."
139169
(handler-case (client-reader-routine client)
140170
(end-of-file () (client-delete client))
141171
(sb-int:simple-stream-error ()
@@ -150,31 +180,38 @@
150180
(client-delete client)))))
151181

152182
(defun create-client (connection)
183+
"This procedure create a new client based on CONNECTION made by
184+
USOCKET:SOCKET-ACCEPT. This shit create a lot of side effects as messages
185+
if the debug is on because this makes all the log stuff to make analysis"
153186
(debug-format t "Incoming connection from ~a ~%" (socket-peer-address connection))
154187
(let ((client-stream (socket-stream connection)))
155188
(write-line "> Type your username: " client-stream)
156189
(finish-output client-stream)
157190
(let ((client (make-client :name (read-line client-stream)
158191
:socket connection
159192
:address (socket-peer-address connection))))
160-
(sb-thread:with-mutex (*client-mutex*)
193+
(with-mutex (*client-mutex*)
161194
(debug-format t "Added new user ~a@~a ~%"
162195
(client-name client)
163196
(client-address client))
164197
(push client *clients*))
165198
(push-message "@server" (format nil "The user ~s joined to the party!" (client-name client)))
166-
(sb-thread:make-thread #'client-reader
199+
(make-thread #'client-reader
167200
:name (format nil "~a reader thread" (client-name client))
168201
:arguments (list client)))))
169202

170203
;; a function defined to handle the errors of client thread
171204
(defun safe-client-thread (connection)
205+
"This function is a wrapper for CREATE-CLIENT treating the
206+
exceptions."
172207
(handler-case (create-client connection)
173208
(end-of-file () nil)
174209
(usocket:address-in-use-error () nil)))
175210

176211
(defun message-broadcast ()
177-
(loop when (sb-thread:wait-on-semaphore *message-semaphore*)
212+
"This procedure is a general independent thread to run brodcasting
213+
all the clients when a message is ping on this server"
214+
(loop when (wait-on-semaphore *message-semaphore*)
178215
do (let ((message (formated-message (pop *messages-stack*))))
179216
(push message *messages-log*)
180217
(loop for client in *clients*
@@ -183,22 +220,34 @@
183220
(sb-bsd-sockets:not-connected-error () (client-delete client)))))))
184221

185222
(defun connection-handler (socket-server)
223+
"This is a special thread just for accepting connections from SOCKET-SERVER
224+
and creating new clients from it."
186225
(loop for connection = (socket-accept socket-server)
187-
do (sb-thread:make-thread #'safe-client-thread
188-
:arguments (list connection)
189-
:name "create client")))
226+
do (make-thread #'safe-client-thread
227+
:arguments (list connection)
228+
:name "create client")))
190229

191230
(defun server-loop (socket-server)
231+
"This is the general server-loop procedure. Create the threads
232+
necessary for the basic working state of this chat. The main idea
233+
is creating a MESSAGE-BROADCAST procedure and CONNECTION-HANDLER
234+
procedure running as separated threads.
235+
236+
The first procedure send always a new message too all clients
237+
defined on *clients* when *messages-semaphore* is signalized.
238+
The second procedure is a general connection-handler for new
239+
clients trying connecting to the server."
192240
(format t "Running server... ~%")
193-
(let* ((connection-thread (sb-thread:make-thread #'connection-handler
241+
(let* ((connection-thread (make-thread #'connection-handler
194242
:arguments (list socket-server)
195243
:name "Connection handler"))
196-
(broadcast-thread (sb-thread:make-thread #'message-broadcast
244+
(broadcast-thread (make-thread #'message-broadcast
197245
:name "Message broadcast")))
198-
(sb-thread:join-thread connection-thread)
199-
(sb-thread:join-thread broadcast-thread)))
246+
(join-thread connection-thread)
247+
(join-thread broadcast-thread)))
200248

201249
(defun main ()
250+
"Well, this function run all the necessary shits."
202251
(let ((socket-server (socket-listen *host* *port*)))
203252
(unwind-protect (handler-case (server-loop socket-server)
204253
(usocket:address-in-use-error ()

0 commit comments

Comments
 (0)