Network and File I/O Tutorial
Network and file system input and output are common operations in any programming. Luban offers a set of tools for IO purpose. Youll see how easily it is to persist and recover Luban objects through network or file system. Though they are not part of the Luban programming language, they are so useful that we need to use a chapter to describe them.
These are the basic data types used to persist information or pass them around network. And they share a more or less common interface. Below listed are their Luban type names and the description of their member functions.
Luban type names.
Console: std::console
File: std::file
Socket: net::socket
Member functions that apply to all above data types:
write(obj1,obj2 ):
Writes the object in human readable string format to the media.
Return null, or error in case of failure.
writeline(obj1,obj2
):
Same as write, only adds one extra line break at the end of each object string.
Return null, or error in case of failure.
read( int n=1 ):
Reads specified number of characters, or read one character if n not specified.
Returns a string containing all characters read.
readline():
Reads the media to break of line.
Returns a string.
writeobj(obj1,obj2
):
Writes the objects in platform independent binary format from which the object can be restored.
Returns null, or error for failure.
readobj():
Restore object back from media, likely the object written by writeobj.
Returns restored object.
What need to be mentioned is that writeobj and readobj are designed to be used in pair. Any object serialized and persisted with writeobj can be restored using readobj.
Below is a simple text file processing example:
linecount = 0;
wordcount = 0;
txtfile = std::file(mydatafile,
r); // open file mydatafile
alldata = txtfile.readall();
// read ALL content of the file into alldata
lines
= alldata.split(\n); // split alldata
into vector lines
foreach( line in lines
) // Iterate through each line in the
vector lines
{
++linecount; //increase line count
words = line.split(); // Split one line into a vector of words
wordcount += words.size();
// increase word count
}
//print result
std::println(obj=lines:
+string(linecount)+ words: +string(wordcount));
It needs to be mentioned that above code use a new file type member
function readall() that reads
while content of a file into a string data object. The code then split the
whole content into lines and does analysis.
The below example script shows how to use file to persist and restore data object using file:
fw = std::file(objfile, w); // open a file for writing
fw.writeobj([1,2,3]); //
write a vector object
fw.close(); // close the
file
fr = std::file(objfile, r); // open file for reading
vec = fr.readobj(); // read
one object back
std::println(obj=vec); // print out [1,2,3]
There are three modes can be used to open a file. They are read, write and append, and are represented by characters as r, w and a. Above code shows read and write. While in the write mode opening an existing file will cause it to be truncated, opening the same file in append mode will cause the later writing append to the end the file.
The below scripts demonstrate how to use socket, including both client and server side. The server side uses a new data type net::listener that is basically a TCP port listener. It can be created given a port number. Its major member function is accept() that accepts one incoming connection and return a socket object that can talk to the client.
Server script:
//
This server greets clients and assign each of them a
number
listener = net::listener(6500); // take
TCP port 6500
clientcount=0;
while( true
) // forever loop
{
socket
= listener.accept(); // wait for client
connection
name = socket.readobj(); //
read client name
socket.writeobj(Hello,
+name); // say hello
++clientcount; //
increase client number
Socket.writeobj(clientcount); // send the number to client
}
Client script:
socket = net::socket(localhost, 6500); // connect to server at port 6500
socket.writeobj(Chinese); // send server its name
msg = socket.readobj(); //
receive greeting
std::console().writeline(The server says: , msg); //
print greeting
mynumber = socket.readobj();
// read assigned number
std::console().writeline(My number is , mynumber); //
print number
In the above, the server script takes TCP port 6500 and listens to incoming connection. For each client connection server says hello then assign a number to the client. Client script simply connect to the server at port 6500, send its name, then receive greeting message and number. You can run both scripts on the same machine. If you want to run them on different machines, you need to change the sever name from localhost to the actual server host name.
Just for convenience, std::println and std::print structures are coded as part of Luban package. The structure print object to console in a simple way. Below is their source code:
namespace std;
struct println(
input obj;)
as process
{
std::console().writeline(input.obj);
}
struct print( input obj;)
as process
{
std::console().write(input.obj); }
Every Luban object can dump itself into a binary stream. In order to restore one object out of a binary string, the std::des structure is needed, as below sample code shows.
x = {one:1, two:2}; // construct a map
xguts = x.serialize();// serialize it into a string
xback = std::des(stream=xguts).obj; //
call std::des to restore it
truth = xback == x; // truth = true,
compare the restored object and original