网络和流
Julia提供了一个丰富的接口来处理诸如终端、管道和TCP套接字等流I/O对象。这个接口虽然在系统级别上是异步的,但它是以同步的方式呈现给程序员的,通常不需要考虑底层的异步操作,这是通过大量使用Julia协作线程(Coroutine)功能来实现的。
基础流 I/O
所有Julia stream都暴露了read和write方法,将stream作为它们的第一个参数,如:
julia> write(stdout, "Hello World"); # suppress return value 11 with ;
Hello World
julia> read(stdin, Char)
'\n': ASCII/Unicode U+000a (category Cc: Other, control)
在这里,Enter被再次按下,这样Julia就可以读取换行符了。现在,正如您从这个例子中可以看到的,写以数据写作为它的第二个参数,而read则将要读取的数据类型作为第二个参数。
例如,要读取一个简单的字节数组,我们可以这样做:
julia> x = zeros(UInt8, 4)
4-element Array{UInt8,1}:
0x00
0x00
0x00
0x00
julia> read!(stdin, x)
abcd
4-element Array{UInt8,1}:
0x61
0x62
0x63
0x64
但是,由于这有点麻烦,所以提供了几种方便的方法。例如,我们可以将上面的内容写成:
julia> read(stdin, 4)
abcd
4-element Array{UInt8,1}:
0x61
0x62
0x63
0x64
或者如果我们想读整句话的话:
julia> readline(stdin)
abcd
"abcd"
请注意,根据您的终端设置,TTY可能是行缓冲的,因此在将数据发送给Julia之前可能需要一个额外的Enter。
要读取stdin中的每一行,可以使用每一行:
for line in eachline(stdin)
print("Found $line")
end
或者,如果您想按字符阅读,请阅读:
while !eof(stdin)
x = read(stdin, Char)
println("Found: $x")
end
文本I/O
请注意,上面提到的写入方法对二进制流进行操作。特别地,值不被转换成任何规范文本表示,而是按如下方式写成:
julia> write(stdout, 0x61); # suppress return value 1 with ;
a
注意,a被写函数写到stdout,返回的值是1(因为0x61是一个字节)。
对于文本I/o,根据需要使用print或Show方法(请参阅这两种方法的文档,详细讨论它们之间的差异):
julia> print(stdout, 0x61)
97
有关如何为自定义类型实现显示方法的更多信息,请参见自定义漂亮打印。
IO输出上下文属性
有时候io输出可以从将上下文信息传递到显示方法的能力中获益。ioContext对象提供了将任意元数据与io对象关联的框架。例如,:Compact=>true向io对象添加了一个提示参数,被调用的Show方法应该打印一个较短的输出(如果适用的话)。参见ioContext文档中的公共属性列表。
处理文件
与许多其他环境一样,Julia有一个OPEN函数,它接受一个文件名,并返回一个iostream对象,您可以使用该对象从文件中读取和写入内容。例如,如果我们有一个文件hello.txt,其内容是Hello,world!:
julia> f = open("hello.txt")
IOStream(<file hello.txt>)
julia> readlines(f)
1-element Array{String,1}:
"Hello, World!"
如果您想要写入一个文件,您可以使用写(“w”)标志打开它:
julia> f = open("hello.txt","w")
IOStream(<file hello.txt>)
julia> write(f,"Hello again.")
12
如果此时检查hello.txt的内容,就会注意到它是空的;实际上还没有写到磁盘。这是因为在写到磁盘之前,iostream必须关闭:
julia> close(f)
再次检查hello.txt将显示其内容已经更改。
打开一个文件,对它的内容做一些事情,然后再次关闭它是一种非常常见的模式。为了使这更容易,存在另一个OPEN调用,它将函数作为其第一个参数,文件名作为其第二个参数,打开该文件,以该文件作为参数调用该函数,然后再次关闭它。例如,给定一个函数:
function read_and_capitalize(f::IOStream)
return uppercase(read(f, String))
end
你可以打调用:
julia> open(read_and_capitalize, "hello.txt")
"HELLO AGAIN."
若要打开Helo.txt,请在其上调用Read和Syb,关闭Helo.txt并返回大写内容。
为了避免定义命名函数,您可以使用DO语法,它动态地创建一个匿名函数:
julia> open("hello.txt") do f
uppercase(read(f, String))
end
"HELLO AGAIN."
一个简单的TCP示例
让我们马上来看一个包含TCP套接字的简单例子。这个功能在一个标准的套接字包中,称为套接字。让我们先创建一个简单的服务器:
julia> using Sockets
julia> @async begin
server = listen(2000)
while true
sock = accept(server)
println("Hello World\n")
end
end
Task (runnable) @0x00007fd31dc11ae0
对于那些熟悉unix套接字api的人来说,方法名会比较熟悉,虽然它们的使用比原始unix套接字api稍微简单一些。在这种情况下,第一个侦听调用将创建一个服务器,等待指定端口(2000)上的传入连接。同样的功能也可以用来创建各种其他类型的服务器:
julia> listen(2000) # Listens on localhost:2000 (IPv4)
Base.TCPServer(active)
julia> listen(ip"127.0.0.1",2000) # Equivalent to the first
Base.TCPServer(active)
julia> listen(ip"::1",2000) # Listens on localhost:2000 (IPv6)
Base.TCPServer(active)
julia> listen(IPv4(0),2001) # Listens on port 2001 on all IPv4 interfaces
Base.TCPServer(active)
julia> listen(IPv6(0),2001) # Listens on port 2001 on all IPv6 interfaces
Base.TCPServer(active)
julia> listen("testsocket") # Listens on a UNIX domain socket
Base.PipeServer(active)
julia> listen("\\\\.\\pipe\\testsocket") # Listens on a Windows named pipe
Base.PipeServer(active)
请注意,上一次调用的返回类型是不同的。这是因为此服务器不监听TCP,而是侦听命名管道(Windows)或Unix域套接字。还请注意,Windows命名管道格式必须是特定的模式,因此名称前缀(.PIPE)唯一地标识文件类型。与接受和连接方法有关。接受方法检索到我们刚刚创建的服务器上连接的客户端的连接,而连接函数使用指定的方法连接到服务器。连接函数接受与侦听相同的参数,因此,假设环境(即主机、CWD等)是相同的,则应该能够将相同的参数传递给Conn。
julia> connect(2000)
TCPSocket(open, 0 bytes waiting)
julia> Hello World
正如我们所预期的那样,我们看到了“Hello world”打印。那么,让我们实际分析场景后面发生了什么。当我们调用CONNECT时,我们连接到刚才创建的服务器。同时,Accept函数返回到新创建的套接字的服务器端连接,并打印“hello world”以表示连接是成功的。
Julia的一个很大的优点是,由于API是同步公开的,即使I/O实际上是异步发生的,所以我们不必担心回调,甚至不需要确保服务器运行。当我们调用CONNECT当前任务时,等待连接被建立,然后才继续执行。在这个暂停中,服务器任务恢复执行(因为(连接请求现在可用),接受连接,打印消息并等待下一个客户端。读写工作以同样的方式进行。要查看这一点,请考虑以下简单的回显服务器:
julia> @async begin
server = listen(2001)
while true
sock = accept(server)
@async while isopen(sock)
write(sock, readline(sock, keep=true))
end
end
end
Task (runnable) @0x00007fd31dc12e60
julia> clientside = connect(2001)
TCPSocket(RawFD(28) open, 0 bytes waiting)
julia> @async while isopen(clientside)
write(stdout, readline(clientside, keep=true))
end
Task (runnable) @0x00007fd31dc11870
julia> println(clientside,"Hello World from the Echo Server")
Hello World from the Echo Server
与其他流一样,请使用“关闭”断开套接字:
julia> close(clientside)
解析IP地址
没有遵循侦听方法的连接方法之一是CONNECT(主机:String,port),它将尝试连接到端口参数给出的端口上的主机参数。它允许您执行以下操作:
julia> connect("google.com", 80)
TCPSocket(RawFD(30) open, 0 bytes waiting)
此功能的基础是getaddrinfo,它将执行适当的地址解析:
julia> getaddrinfo("google.com")
ip"74.125.226.225"
完结于 2018-08-30 22:24