FORTH: Nanoservers and Nano Clients. Part 1
- Tutorial
It's time to talk about another aspect of using the wonderful Fort language.
In this series of articles, I will show how you can use it to create tiny client-server applications. Each of which can be used as a research and educational tool.
For entertainment, we need Windows up to seven and the SP-Forth package. Andrei Cherezov declared the possibility of launching his fort system under Linux, but I did not check it.
First, let's try to create a simple server program that will give the joined client well ... let's say the current date and time on the server.
In notepad or any other text editor (Akelpad), create the datetimes.f file in the same folder as the spf4.exe file.
What do we need to create a server? Sockets!
To start working with them you need to connect the appropriate library.
~ nn \ lib \ sock2.f
Conveniently, all libraries are stored in the source code. You can always look and see what it is and how it works.
The first two lines are obviously the definition of variables. VALUE variables are an improvement to the classic Fort. They allow you to avoid endless dereferences (get the value at the address) and make the text more readable.
The third line speaks for itself, the last one needs to be given a couple of comments.
The word CreateSoket is clear and true, but there are nuances. Sockets are different! Honestly, I don’t know much about sorts of sockets, but the simplest and, it seems, the most frequently used one is a blocking bidirectional TCP / IP stream socket created by this word. How to create sockets of a different type, you can look into the library. The magic word THROW only intercepts error codes and throws an exception if the error code is nonzero. Brackets print text between them on the terminal, CR - line feed.
That is almost all. Now we sit and listen on port 80 until someone knocks.
Of course, you can choose a completely arbitrary free port.
The word AcceptSoket will stop the program until it connects.
Who is connected to us?
displays the IP address and port from which the connection was made to the console. The only SWAP in the entire program arose only because of the not very well-thought-out implementation of the word GetPeerIP & Port .
We will prepare the date and time for dispatch.
We need one more library
lib \ include \ facil.f
From which we need the word TIME & DATE . (I had to fix it in the library, now it differs from the complete one. It began to give out milliseconds, and the date order also changed.) This word puts 7 values on the stack. Milliseconds, seconds, hours, year, month, day. Accordingly, the first number will be removed from the stack. This refers to the day of the month.
It looks awful. The beauty of Fort is that this disgrace can be turned into candy.
It can be seen that the same text is repeated 7 and two times in 3. Of course, you can put on a loop, but then the text will be difficult to read.
Take advantage of the Fort.
Define additional words
Logical, clear and easy to read. {Remark: The word S> D expands the top element of the stack with a mark to double precision. The word (D.) converts a double-precision number into a string and pushes its address and counter onto the stack.}
Still awful. What can be done?
One key feature of the Fort language comes to the rescue. We can create our own defining words.
The word make_value during its execution will select the next set of characters from the input stream, limited by spaces and place it at the top of the current dictionary. The semantics of this newfound word will be given by the word after DOES> . That is, we have a tool with which we can create concepts that are united by common semantics.
And for delimiters
Now we can write beautifully
Tear the connection
And clear the sockets
All.
As you can see the stages of execution and compilation can alternate. This is another basic feature of the Fort. Also, a well-written Fort program requires little comment.
Who was attentive enough noticed that we redefined the basic words colon and minus. What to do with this? The answer is below.
In this series of articles, I will show how you can use it to create tiny client-server applications. Each of which can be used as a research and educational tool.
For entertainment, we need Windows up to seven and the SP-Forth package. Andrei Cherezov declared the possibility of launching his fort system under Linux, but I did not check it.
First, let's try to create a simple server program that will give the joined client well ... let's say the current date and time on the server.
In notepad or any other text editor (Akelpad), create the datetimes.f file in the same folder as the spf4.exe file.
What do we need to create a server? Sockets!
To start working with them you need to connect the appropriate library.
~ nn \ lib \ sock2.f
Conveniently, all libraries are stored in the source code. You can always look and see what it is and how it works.
0 VALUE sockt 0 VALUE sockt_a SocketsStartup THROW. (Sockets started) CR CreateSocket THROW TO sockt. (Socket created) CR
The first two lines are obviously the definition of variables. VALUE variables are an improvement to the classic Fort. They allow you to avoid endless dereferences (get the value at the address) and make the text more readable.
The third line speaks for itself, the last one needs to be given a couple of comments.
The word CreateSoket is clear and true, but there are nuances. Sockets are different! Honestly, I don’t know much about sorts of sockets, but the simplest and, it seems, the most frequently used one is a blocking bidirectional TCP / IP stream socket created by this word. How to create sockets of a different type, you can look into the library. The magic word THROW only intercepts error codes and throws an exception if the error code is nonzero. Brackets print text between them on the terminal, CR - line feed.
80 sockt BindSocket THROW. (Socket binded) CR sockt ListenSocket THROW. (Listen) CR sockt AcceptSocket THROW TO sockt_a. (Accept connection from :)
That is almost all. Now we sit and listen on port 80 until someone knocks.
Of course, you can choose a completely arbitrary free port.
The word AcceptSoket will stop the program until it connects.
Who is connected to us?
sockt_a GetPeerIP & Port THROW SWAP NtoA TYPE. "port:".
displays the IP address and port from which the connection was made to the console. The only SWAP in the entire program arose only because of the not very well-thought-out implementation of the word GetPeerIP & Port .
We will prepare the date and time for dispatch.
We need one more library
lib \ include \ facil.f
From which we need the word TIME & DATE . (I had to fix it in the library, now it differs from the complete one. It began to give out milliseconds, and the date order also changed.) This word puts 7 values on the stack. Milliseconds, seconds, hours, year, month, day. Accordingly, the first number will be removed from the stack. This refers to the day of the month.
S "Current time: sockt_a WriteSocket THROW S> D (D.) sockt_a WriteSocket THROW (day) S "-" sockt_a WriteSocket THROW S> D (D.) sockt_a WriteSocket THROW (month) S "-" sockt_a WriteSocket THROW S> D (D.) sockt_a WriteSocket THROW (year) S "" sockt_a WriteSocket THROW S> D (D.) sockt_a WriteSocket THROW (hours) S "-" sockt_a WriteSocket THROW S> D (D.) sockt_a WriteSocket THROW (minutes) S "-" sockt_a WriteSocket THROW S> D (D.) sockt_a WriteSocket THROW (seconds) S "-" sockt_a WriteSocket THROW S> D (D.) sockt_a WriteSocket THROW (milliseconds)
It looks awful. The beauty of Fort is that this disgrace can be turned into candy.
It can be seen that the same text is repeated 7 and two times in 3. Of course, you can put on a loop, but then the text will be difficult to read.
Take advantage of the Fort.
Define additional words
: send_value S> D (D.) sockt_a WriteSocket THROW; : send_delimeter sockt_a WriteSocket THROW;
Logical, clear and easy to read. {Remark: The word S> D expands the top element of the stack with a mark to double precision. The word (D.) converts a double-precision number into a string and pushes its address and counter onto the stack.}
: Day send_value; : Month send_value; : Year send_value; : Hours send_value; : Minutes send_value; : Seconds send_value; : Milliseconds send_value;
Still awful. What can be done?
One key feature of the Fort language comes to the rescue. We can create our own defining words.
: make_value CREATE DOES> DROP send_value; : make_values 0 DO make_value LOOP;
The word make_value during its execution will select the next set of characters from the input stream, limited by spaces and place it at the top of the current dictionary. The semantics of this newfound word will be given by the word after DOES> . That is, we have a tool with which we can create concepts that are united by common semantics.
7 make_values Day Month Year Hours Minutes Seconds Milliseconds
And for delimiters
: make_delimeter CREATE, DOES> 1 send_delimeter; : make_delimeters 0 DO make_delimeter LOOP; BL CHAR - CHAR: 3 make_delimeters: - _
Now we can write beautifully
TIME & DATE Day - Month - Year _ Hours: Minutes: Seconds: Milliseconds
Tear the connection
sockt_a CloseSocket THROW
And clear the sockets
SocketsCleanup THROW
All.
As you can see the stages of execution and compilation can alternate. This is another basic feature of the Fort. Also, a well-written Fort program requires little comment.
Who was attentive enough noticed that we redefined the basic words colon and minus. What to do with this? The answer is below.
Combed code
~ nn \ lib \ sock2.f
lib \ include \ facil.f
80 CONSTANT port
0 VALUE sockt
0 VALUE sockt_a
: send_value S> D (D.) sockt_a WriteSocket THROW;
: send_delimeter sockt_a WriteSocket THROW;
: make_value CREATE DOES> DROP send_value;
: make_values 0 DO make_value LOOP;
7 make_values Day Month Year Hours Minutes Seconds Milliseconds
: make_delimeter CREATE, DOES> 1 send_delimeter;
: make_delimeters 0 DO make_delimeter LOOP;
: define::;
BL CHAR - CHAR: 3 make_delimeters: - _
define: time & date_server
SocketsStartup THROW. "Sockets started" CR
CreateSocket THROW TO sockt. "Socket created" CR
port sockt BindSocket THROW. "Socket binded" CR
sockt ListenSocket THROW. "Listen" CR
sockt AcceptSocket THROW TO sockt_a. "Accept connection from:"
sockt_a GetPeerIP & Port THROW SWAP NtoA TYPE. "port:".
S "Current time:" sockt_a WriteSocket THROW
TIME & DATE
Day - Month - Year _ Hours: Minutes: Seconds: Milliseconds
sockt_a CloseSocket THROW
SocketsCleanup THROW
;
time & date_server