鍍金池/ 教程/ Java/ 網(wǎng)絡(luò)和流
類型轉(zhuǎn)換和類型提升
方法
嵌入式 Julia
交互
調(diào)用 C 和 Fortran 代碼
類型
代碼性能優(yōu)化
多維數(shù)組
元編程
函數(shù)
簡(jiǎn)介
線性代數(shù)
與其它語(yǔ)言的區(qū)別
數(shù)學(xué)運(yùn)算和基本函數(shù)
構(gòu)造函數(shù)
控制流
常見問(wèn)題
并行計(jì)算
擴(kuò)展包
開發(fā)擴(kuò)展包
開始
字符串
運(yùn)行外部程序
變量的作用域
模塊
網(wǎng)絡(luò)和流
代碼樣式
復(fù)數(shù)和分?jǐn)?shù)
可空類型
整數(shù)和浮點(diǎn)數(shù)
變量
日期和時(shí)間

網(wǎng)絡(luò)和流

Julia 提供了一個(gè)豐富的接口處理終端、管道、tcp套接字等等I/O流對(duì)象。

接口在系統(tǒng)層的實(shí)現(xiàn)是異步的,開發(fā)者以同步的方式調(diào)用該接口、一般無(wú)需關(guān)注底層異步實(shí)現(xiàn)。 接口實(shí)現(xiàn)主要基于Julia支持的協(xié)程(coroutine)功能。

基本流 I/O

所有 Julia 流都至少提供一個(gè) read 和一個(gè) write 方法,且第一個(gè)參數(shù)都是流對(duì)象,例如:

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

    julia> read(STDIN,Char)

    '\n'

注意我又輸入了一次回車,這樣 Julia 會(huì)讀入換行符?,F(xiàn)在,由例子可見,write 方法的第二個(gè)參數(shù)是將要寫入的數(shù)據(jù),read 方法的第二個(gè)參數(shù)是即將讀入的數(shù)據(jù)類型。例如,要讀入一個(gè)簡(jiǎn)單的字節(jié)數(shù)組,我們可以:

    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

不過(guò)像上面這么寫有點(diǎn)麻煩,還提供了一些簡(jiǎn)化的方法。例如,我們可以將上例重寫成:

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

或者直接讀入整行數(shù)據(jù):

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

注意這取決于你的終端配置,你的 TTY 可能是行緩沖、需要多輸入一個(gè)回車才會(huì)把數(shù)據(jù)傳給 julia。

如果你想要通過(guò) STDIN 去讀每一行,你可以使用 eachline 方法:

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

或者如果你想要以字符為單位去讀,則如下:

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

文本 I/O

注意上面提到的寫方法是對(duì)二進(jìn)制流進(jìn)行操作的。特別是,值不會(huì)被轉(zhuǎn)換成任何規(guī)范的文本表示,而是會(huì)被寫成如下:

    julia> write(STDOUT,0x61)
    a

對(duì)于文本 I/O,可以使用 print 或 show 方法,這取決你的需求(可以查看標(biāo)準(zhǔn)庫(kù)中對(duì)于兩者不同之處的細(xì)節(jié)描述):

    julia> print(STDOUT,0x61)
    97

使用文件

像其他的環(huán)境一樣,Julia 有一個(gè)開放的函數(shù),它以一個(gè)文件名作為參數(shù)并且返回一個(gè) IO 流對(duì)象,通過(guò)這個(gè) IO 流,你可以從文件中讀或者寫其中的內(nèi)容。舉個(gè)例子來(lái)說(shuō),如果我們有一個(gè)文件,hello.txt,它的內(nèi)容為 “Hello, World!”:

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

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

如果你想要寫入某些內(nèi)容到文件當(dāng)中,你可以用寫標(biāo)志 (“w”) 打開它:

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

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

如果你用這種方式檢查 hello.txt 的內(nèi)容,你將會(huì)注意到它是空的;其實(shí)沒(méi)有任何東西被寫入磁盤。這是因?yàn)?IO 流在數(shù)據(jù)真正寫到磁盤之前必須被關(guān)掉:

    julia> close(f)

再次檢查 hello.txt 將會(huì)顯示它的內(nèi)容已經(jīng)被改變了。

打開一個(gè)文件,對(duì)它的內(nèi)容做一些改變,然后關(guān)閉它是一個(gè)常見的模式。為了讓這個(gè)過(guò)程更簡(jiǎn)單,這里存在另一個(gè) open 的調(diào)用,用一個(gè)方法作為其第一個(gè)參數(shù),用文件名作為他的第二個(gè)參數(shù),打開文件,調(diào)用該文件的方法作為一個(gè)參數(shù),然后再次關(guān)閉它。舉一個(gè)例子,給出一個(gè)方法:

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

你可以調(diào)用:

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

為了打開 hello.txt,調(diào)用它的 read_and_capitalize 方法,關(guān)閉 hello.txt 然后返回大寫的內(nèi)容。

為了避免去定義一個(gè)已經(jīng)命名的函數(shù),你可以使用 do 語(yǔ)法,動(dòng)態(tài)的去創(chuàng)建一個(gè)匿名函數(shù):

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

簡(jiǎn)單的 TCP 例子

讓我們直接用一個(gè)簡(jiǎn)單的 Tcp Sockets 的示例來(lái)說(shuō)明。我們首先需要?jiǎng)?chuàng)建一個(gè)簡(jiǎn)單地服務(wù)器:

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

    julia>

那些熟悉 Unix socket API 的人,會(huì)覺得方法名和 Unix socket 很相似,盡管他們的用法比原生 Unix socket API 要簡(jiǎn)單。第一次調(diào)用 listen 將會(huì)創(chuàng)建一個(gè)服務(wù)器來(lái)等待即將到來(lái)的連接,在這個(gè)案例中監(jiān)聽的端口為 2000。相同的方法可能會(huì)用來(lái)去創(chuàng)建不同的其他種類的服務(wù)器:

    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)

注意最后一次調(diào)用的返回值類型是不同的。這是因?yàn)檫@個(gè)服務(wù)器沒(méi)有監(jiān)聽 TCP,而是在一個(gè)命名管道(Windows 術(shù)語(yǔ))- 同樣也稱為域套接字(UNIX 的術(shù)語(yǔ))。他們的不同之處非常微小,并且與他們的接收和連接方法有關(guān)系。接受方法會(huì)檢索一個(gè)到客戶端的連接,連接到我們剛剛創(chuàng)建的服務(wù)器端,而連接到服務(wù)器的函數(shù)使用的是特定的方法。連接方法和監(jiān)聽方法的參數(shù)是一樣的,所以使用的環(huán)境(比如主機(jī),cwd 等等)能夠傳遞和監(jiān)聽方法相同的參數(shù)來(lái)建立一個(gè)連接。所以讓我們來(lái)嘗試一下(前提是已經(jīng)創(chuàng)建好上面的服務(wù)器):

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

    julia> Hello World

正如我們預(yù)期的那樣,我們會(huì)看到 “Hello World” 被打印出來(lái)了。所以我讓我們分析一下在后臺(tái)發(fā)生了什么。當(dāng)我們調(diào)用連接函數(shù)時(shí),我們連接到了我們剛剛創(chuàng)建的服務(wù)器。同時(shí),接收方法返回一個(gè)服務(wù)器端的連接到最新創(chuàng)建的套接字上,然后打印 “Hello World” 來(lái)表明連接成功了。

Julia 的一個(gè)強(qiáng)大功能是盡管 I/O 實(shí)際上是異步發(fā)生的,但 API 仍然是同步的,我們甚至不必?fù)?dān)心回調(diào)或服務(wù)器是否繼續(xù)正常運(yùn)行。當(dāng)我們調(diào)用連接時(shí),當(dāng)前任務(wù)會(huì)等待連接建立,并且在連接建立之后,當(dāng)前任務(wù)才會(huì)繼續(xù)執(zhí)行。在暫停期間,服務(wù)器任務(wù)會(huì)恢復(fù)執(zhí)行(因?yàn)楝F(xiàn)在一個(gè)連接請(qǐng)求可用),接受這個(gè)連接,打印出信息并且等待下一個(gè)客戶端。讀和寫的工作是相同的。為了更好地理解,請(qǐng)看以下一個(gè)簡(jiǎn)單的 echo 服務(wù)器:

    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

解析 IP 地址

一種不伴隨監(jiān)聽方法的 connect 函數(shù)為 connect(host::ASCIIString,port),它會(huì)嘗試去連接到主機(jī)端口參數(shù)給出的端口提供的主機(jī)參數(shù)給出的主機(jī)。它允許你如下操作:

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

這個(gè)功能的基礎(chǔ)是 getaddrinfo 方法,將提供適當(dāng)?shù)牡刂方馕?

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