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

2018-08-12 21:26 更新

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

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

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

基本流 I/O

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

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

    julia> read(STDIN,Char)

    '\n'

注意我又輸入了一次回車,這樣 Julia 會讀入換行符?,F(xiàn)在,由例子可見,write 方法的第二個參數(shù)是將要寫入的數(shù)據(jù),read 方法的第二個參數(shù)是即將讀入的數(shù)據(jù)類型。例如,要讀入一個簡單的字節(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

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

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

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

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

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

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

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

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

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

文本 I/O

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

    julia> write(STDOUT,0x61)
    a

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

    julia> print(STDOUT,0x61)
    97

使用文件

像其他的環(huán)境一樣,Julia 有一個開放的函數(shù),它以一個文件名作為參數(shù)并且返回一個 IO 流對象,通過這個 IO 流,你可以從文件中讀或者寫其中的內(nèi)容。舉個例子來說,如果我們有一個文件,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)容,你將會注意到它是空的;其實沒有任何東西被寫入磁盤。這是因為 IO 流在數(shù)據(jù)真正寫到磁盤之前必須被關(guān)掉:

    julia> close(f)

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

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

    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)容。

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

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

簡單的 TCP 例子

讓我們直接用一個簡單的 Tcp Sockets 的示例來說明。我們首先需要創(chuàng)建一個簡單地服務(wù)器:

    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 要簡單。第一次調(diào)用 listen 將會創(chuàng)建一個服務(wù)器來等待即將到來的連接,在這個案例中監(jiān)聽的端口為 2000。相同的方法可能會用來去創(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)用的返回值類型是不同的。這是因為這個服務(wù)器沒有監(jiān)聽 TCP,而是在一個命名管道(Windows 術(shù)語)- 同樣也稱為域套接字(UNIX 的術(shù)語)。他們的不同之處非常微小,并且與他們的接收和連接方法有關(guān)系。接受方法會檢索一個到客戶端的連接,連接到我們剛剛創(chuàng)建的服務(wù)器端,而連接到服務(wù)器的函數(shù)使用的是特定的方法。連接方法和監(jiān)聽方法的參數(shù)是一樣的,所以使用的環(huán)境(比如主機,cwd 等等)能夠傳遞和監(jiān)聽方法相同的參數(shù)來建立一個連接。所以讓我們來嘗試一下(前提是已經(jīng)創(chuàng)建好上面的服務(wù)器):

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

    julia> Hello World

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

Julia 的一個強大功能是盡管 I/O 實際上是異步發(fā)生的,但 API 仍然是同步的,我們甚至不必擔(dān)心回調(diào)或服務(wù)器是否繼續(xù)正常運行。當(dāng)我們調(diào)用連接時,當(dāng)前任務(wù)會等待連接建立,并且在連接建立之后,當(dāng)前任務(wù)才會繼續(xù)執(zhí)行。在暫停期間,服務(wù)器任務(wù)會恢復(fù)執(zhí)行(因為現(xià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),它會嘗試去連接到主機端口參數(shù)給出的端口提供的主機參數(shù)給出的主機。它允許你如下操作:

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

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

    julia> getaddrinfo("google.com")
    IPv4(74.125.226.225)
以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號