Julia 提供了一个丰富的接口处理终端、管道、tcp套接字等等I/O流对象。
接口在系统层的实现是异步的,开发者以同步的方式调用该接口、一般无需关注底层异步实现。 接口实现主要基于Julia支持的协程(coroutine)功能。
所有 Julia 流都至少提供一个 read
和一个 write
方法,且第一个参数都是流对象,例如:
julia> write(STDOUT,"Hello World") Hello World julia> read(STDIN,Char) '\n'注意我又输入了一次回车,这样 Julia 会读入换行符。现在,由例子可见,
write
方法的第二个参数是将要写入的数据,read
方法的第二个参数是即将读入的数据类型。例如,要读入一个简单的字节数组,我们可以:
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不过像上面这么写有点麻烦,还提供了一些简化的方法。例如,我们可以将上例重写成:
julia> readbytes(STDIN,4) abcd 4-element Uint8 Array: 0x61 0x62 0x63 0x64或者直接读入整行数据:
julia> readline(STDIN) abcd "abcd\n"注意这取决于你的终端配置,你的 TTY 可能是行缓冲、需要多输入一个回车才会把数据传给 julia。
注意上面提到的写方法是对二进制流进行操作的。特别是,值不会被转换成任何规范的文本表示,而是会被写成如下:
julia> write(STDOUT,0x61) a对于文本 I/O,可以使用 print 或 show 方法,这取决你的需求(可以查看标准库中对于两者不同之处的细节描述):
julia> print(STDOUT,0x61) 97简单的 TCP 例子
让我们直接用一个简单的 Tcp Sockets 的示例来说明。我们首先需要创建一个简单地服务器:
julia> @async begin server = listen(2000) while true sock = accept(server) println("Hello World\n") end end Task julia>那些熟悉 Unix socket API 的人,会觉得方法名和 Unix socket 很相似,尽管他们的用法比原生 Unix socket API 要简单。第一次调用 listen 将会创建一个服务器来等待即将到来的连接,在这个案例中监听的端口为 2000。相同的方法可能会用来去创建不同的其他种类的服务器:
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)注意最后一次调用的返回值类型是不同的。这是因为这个服务器没有监听 TCP,而是在一个命名管道(Windows 术语)- 同样也称为域套接字(UNIX 的术语)。
他们的不同之处非常微小,并且与他们的接收和连接方法有关系。
接受方法会检索一个到客户端的连接,连接到我们刚刚创建的服务器端,而连接到服务器的函数使用的是特定的方法。连接方法和监听方法的参数是一样的,所以使用的环境(比如主机,cwd 等等)能够传递和监听方法相同的参数来建立一个连接。所以让我们来尝试一下(前提是已经创建好上面的服务器):
julia> connect(2000) TcpSocket(open, 0 bytes waiting) julia> Hello World
正如我们预期的那样,我们会看到 “Hello World” 被打印出来了。所以我让我们分析一下在后台发生了什么。当我们调用连接函数时,我们连接到了我们刚刚创建的服务器。
同时,接收方法返回一个服务器端的连接到最新创建的套接字上,然后打印 “Hello World” 来表明连接成功了。
Julia 的一个强大功能是尽管 I/O 实际上是异步发生的,但 API 仍然是同步的,我们甚至不必担心回调或服务器是否继续正常运行。
当我们调用连接时,当前任务会等待连接建立,并且在连接建立之后,当前任务才会继续执行。在暂停期间,服务器任务会恢复执行(因为现在一个连接请求可用),接受这个连接,打印出信息并且等待下一个客户端。读和写的工作是相同的。为了更好地理解,请看以下一个简单的 echo 服务器:
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
一种不伴随监听方法的 connect 函数为 connect(host::ASCIIString,port),它会尝试去连接到主机端口参数给出的端口提供的主机参数给出的主机。它允许你如下操作:
julia> connect("google.com",80) TcpSocket(open, 0 bytes waiting)这个功能的基础是 getaddrinfo 方法,将提供适当的地址解析:
julia> getaddrinfo("google.com") IPv4(74.125.226.225)