Coding With Fun
Home Docker Django Node.js Articles Python pip guide FAQ Policy

Julia networks and streams


May 14, 2021 Julia


Table of contents


Networks and streams

Julia provides a rich interface for handling terminals, pipelines, tcp sockets, and so on I/O flow objects.

The implementation of an interface at the system layer is asynchronous, and developers call it synchronously, generally without paying attention to the underlying asynchronous implementation. Interface implementations are primarily based on Julia-supported co-program (coroutine) functionality.

Basic flow I/O

All Julia streams provide at least one read and one write and the first argument is a stream object, for example:

    julia> write(STDOUT,"Hello World")
    Hello World

    julia> read(STDIN,Char)

    '\n'

Notice that I entered the carriage return again so that Julia would read the line break. N ow, as can be seen from the example, write of the write method is the data to be read argument of the read method is the data type to be read in. For example, to read into a simple array of bytes, we can:

    julia> x = zeros(Uint8,4)
    4-element Uint8 Array:
     0x00
     0x00
     0x00
     0x00

    julia> read(STDIN,x)
    abcd 
    4-element Uint8 Array:
     0x61
     0x62
     0x63
     0x64

But writing like this is a bit of a hassle, and it provides some simplifications. For example, we can rewrite the example above to:

    julia> readbytes(STDIN,4)
    abcd 
    4-element Uint8 Array:
     0x61
     0x62
     0x63
     0x64   

Or read the entire line of data directly:

    julia> readline(STDIN)
    abcd
    "abcd\n"

Note that depending on your terminal configuration, your TTY may be a row buffer that requires an input of one more carriage return to pass the data to julia.

If you want to read each line through STDIN, you can use the eachline method:

    for line in eachline(STDIN)
        print("Found $line")
    end

Or if you want to read in characters, here's it:

    while !eof(STDIN)
        x = read(STDIN, Char)
        println("Found: $x")
    end

Text I/O

Note that the write method mentioned above is to manipulate the binary stream. In particular, values are not converted to any canoned text, but are written as follows:

    julia> write(STDOUT,0x61)
    a

For text I/O, you can use the print or show method, depending on your needs (you can see the detailed description of the differences in the standard library):

    julia> print(STDOUT,0x61)
    97

Use the file

Like other environments, Julia has an open function that parameters with a file name and returns an IO streaming object through which you can read or write from a file. For example, if we have a file, hello .txt, which reads "Hello, World!":

    julia> f = open("hello.txt")
    IOStream(<file hello.txt>)

    julia> readlines(f)
    1-element Array{Union(ASCIIString,UTF8String),1}:
     "Hello, World!\n"

If you want to write something into a file, you can open it with a write flag ("w"):

    julia> f = open("hello.txt","w")
    IOStream(<file hello.txt>)

    julia> write(f,"Hello again.")
    12

If you check the .txt of the hello file in this way, you will notice that it is empty; This is because the IO stream must be turned off before the data is actually written to disk:

    julia> close(f)

Check the hello .txt will show that its contents have changed.

Open a file, make some changes to its contents, and then close it as a common pattern. T o make the process easier, there is another open call, using a method as its first argument, using the file name as his second argument, opening the file, calling the file's method as an argument, and then closing it again. To give an example, give a method:

    function read_and_capitalize(f::IOStream)
        return uppercase(readall(f))
    end

You can call:

    julia> open(read_and_capitalize, "hello.txt")
    "HELLO AGAIN."

To open the hello .txt, call its read_and_capitalize method, close the hello .txt and return the capital content.

To avoid defining a function that has already been named, you can use the do syntax to dynamically create an anonymous function:

    julia> open("hello.txt") do f
              uppercase(readall(f))
           end
    "HELLO AGAIN."

A simple example of TCP

Let's just illustrate it with a simple example of Tcp Sockets. We first need to create a simple server:

    julia> @async begin
             server = listen(2000)
             while true
               sock = accept(server)
               println("Hello World\n")
             end
           end
    Task

    julia>

Those familiar with the Unix socket API will find the method name similar to the Unix socket, even though their usage is simpler than the native Unix socket API. T he first call to listen creates a server to wait for an upcoming connection, in this case listening to a port of 2000. The same method might be used to create different kinds of servers:

    julia> listen(2000) # Listens on localhost:2000 (IPv4)
    TcpServer(active)

    julia> listen(ip"127.0.0.1",2000) # Equivalent to the first
    TcpServer(active)

    julia> listen(ip"::1",2000) # Listens on localhost:2000 (IPv6)
    TcpServer(active)

    julia> listen(IPv4(0),2001) # Listens on port 2001 on all IPv4 interfaces
    TcpServer(active)

    julia> listen(IPv6(0),2001) # Listens on port 2001 on all IPv6 interfaces
    TcpServer(active)

    julia> listen("testsocket") # Listens on a domain socket/named pipe
    PipeServer(active)

Note that the return value type of the last call is different. T his is because the server does not listen for TCP, but is in a named pipe (Windows term) - also known as a domain socket (UNIX term). T heir differences are tiny and have to do with how they receive and connect. T he accept method retrieves a connection to the client, connecting to the server side we just created, while the function connecting to the server uses a specific method. T he parameters of the connection method and the listening method are the same, so the environment used (such as host, cwd, etc.) can pass the same parameters as the listening method to establish a connection. So let's try it (provided we've created the server above):

    julia> connect(2000)
    TcpSocket(open, 0 bytes waiting)

    julia> Hello World

As we expected, we'll see "Hello World" printed out. S o let's analyze what's going on in the background. W hen we called the connection function, we connected to the server we just created. At the same time, the receive method returns a server-side connection to the newly created socket, and then prints "Hello World" to indicate that the connection was successful.

One of Julia's powerful features is that although I/O actually happens asynchronously, the APIs are still synchronized, and we don't even have to worry about callbacks or whether the server continues to function properly. W hen we call a connection, the current task waits for the connection to be established, and the current task does not continue until the connection is established. D uring the pause, the server task resumes execution (because a connection request is now available), accepts the connection, prints out the information, and waits for the next client. T he work of reading and writing is the same. For a better understanding, look at the following simple echo server:

    julia> @async begin
             server = listen(2001)
             while true
               sock = accept(server)
               @async while true
                 write(sock,readline(sock))
               end
             end
           end
    Task

    julia> clientside=connect(2001)
    TcpSocket(open, 0 bytes waiting)

    julia> @async while true
              write(STDOUT,readline(clientside))
           end

    julia> println(clientside,"Hello World from the Echo Server")

    julia> Hello World from the Echo Server

Resolve the IP address

A connect function that does not accompany the listening method is connect(host::ASCIIString, port), which attempts to connect to the host given by the host port parameters given by the port provided by the host parameters. It allows you to do the following:

    julia> connect("google.com",80)
    TcpSocket(open, 0 bytes waiting)

This feature is based on the getaddrinfo method, which will provide appropriate address resolution:

    julia> getaddrinfo("google.com")
    IPv4(74.125.226.225)