mirror of
https://github.com/gopl-zh/gopl-zh.github.com.git
synced 2025-12-18 11:44:20 +08:00
rebuild
This commit is contained in:
122
ch8/ch8-02.html
122
ch8/ch8-02.html
@@ -5,7 +5,7 @@
|
||||
|
||||
<meta charset="UTF-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||
<title>示例: 併髮的Clock服務 | Go编程语言</title>
|
||||
<title>示例: 併發的Clock服務 | Go编程语言</title>
|
||||
<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
|
||||
<meta name="description" content="">
|
||||
<meta name="generator" content="GitBook 2.5.2">
|
||||
@@ -48,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="8.2" data-chapter-title="示例: 併髮的Clock服務" data-filepath="ch8/ch8-02.md" data-basepath=".." data-revision="Wed Dec 16 2015 10:54:29 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="8.2" data-chapter-title="示例: 併發的Clock服務" data-filepath="ch8/ch8-02.md" data-basepath=".." data-revision="Mon Dec 21 2015 12:51:02 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -146,7 +146,7 @@
|
||||
|
||||
<b>0.5.</b>
|
||||
|
||||
緻謝
|
||||
致謝
|
||||
</a>
|
||||
|
||||
|
||||
@@ -212,7 +212,7 @@
|
||||
|
||||
<b>1.3.</b>
|
||||
|
||||
査找重復的行
|
||||
査找重複的行
|
||||
</a>
|
||||
|
||||
|
||||
@@ -227,7 +227,7 @@
|
||||
|
||||
<b>1.4.</b>
|
||||
|
||||
GIF動畫
|
||||
GIF動畵
|
||||
</a>
|
||||
|
||||
|
||||
@@ -257,7 +257,7 @@
|
||||
|
||||
<b>1.6.</b>
|
||||
|
||||
併髮穫取多個URL
|
||||
併發穫取多個URL
|
||||
</a>
|
||||
|
||||
|
||||
@@ -479,7 +479,7 @@
|
||||
|
||||
<b>3.3.</b>
|
||||
|
||||
復數
|
||||
複數
|
||||
</a>
|
||||
|
||||
|
||||
@@ -494,7 +494,7 @@
|
||||
|
||||
<b>3.4.</b>
|
||||
|
||||
佈爾型
|
||||
布爾型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -544,7 +544,7 @@
|
||||
|
||||
<b>4.</b>
|
||||
|
||||
復閤數據類型
|
||||
複合數據類型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -857,7 +857,7 @@
|
||||
|
||||
<b>6.2.</b>
|
||||
|
||||
基於指鍼對象的方法
|
||||
基於指針對象的方法
|
||||
</a>
|
||||
|
||||
|
||||
@@ -887,7 +887,7 @@
|
||||
|
||||
<b>6.4.</b>
|
||||
|
||||
方法值和方法錶達式
|
||||
方法值和方法表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -953,7 +953,7 @@
|
||||
|
||||
<b>7.1.</b>
|
||||
|
||||
接口是閤約
|
||||
接口是合約
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1073,7 +1073,7 @@
|
||||
|
||||
<b>7.9.</b>
|
||||
|
||||
示例: 錶達式求值
|
||||
示例: 表達式求值
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1103,7 +1103,7 @@
|
||||
|
||||
<b>7.11.</b>
|
||||
|
||||
基於類型斷言識彆錯誤類型
|
||||
基於類型斷言識别錯誤類型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1214,7 +1214,7 @@
|
||||
|
||||
<b>8.2.</b>
|
||||
|
||||
示例: 併髮的Clock服務
|
||||
示例: 併發的Clock服務
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1229,7 +1229,7 @@
|
||||
|
||||
<b>8.3.</b>
|
||||
|
||||
示例: 併髮的Echo服務
|
||||
示例: 併發的Echo服務
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1274,7 +1274,7 @@
|
||||
|
||||
<b>8.6.</b>
|
||||
|
||||
示例: 併髮的Web爬蟲
|
||||
示例: 併發的Web爬蟲
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1289,7 +1289,7 @@
|
||||
|
||||
<b>8.7.</b>
|
||||
|
||||
基於select的多路復用
|
||||
基於select的多路複用
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1304,7 +1304,7 @@
|
||||
|
||||
<b>8.8.</b>
|
||||
|
||||
示例: 併髮的字典遍歷
|
||||
示例: 併發的字典遍歷
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1319,7 +1319,7 @@
|
||||
|
||||
<b>8.9.</b>
|
||||
|
||||
併髮的退齣
|
||||
併發的退齣
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1354,7 +1354,7 @@
|
||||
|
||||
<b>9.</b>
|
||||
|
||||
基於共享變量的併髮
|
||||
基於共享變量的併發
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1415,7 +1415,7 @@
|
||||
|
||||
<b>9.4.</b>
|
||||
|
||||
內存衕步
|
||||
內存同步
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1460,7 +1460,7 @@
|
||||
|
||||
<b>9.7.</b>
|
||||
|
||||
示例: 併髮的非阻塞緩存
|
||||
示例: 併發的非阻塞緩存
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1475,7 +1475,7 @@
|
||||
|
||||
<b>9.8.</b>
|
||||
|
||||
Goroutines和綫程
|
||||
Goroutines和線程
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1748,7 +1748,7 @@
|
||||
|
||||
<b>12.1.</b>
|
||||
|
||||
為何需要反射?
|
||||
爲何需要反射?
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1793,7 +1793,7 @@
|
||||
|
||||
<b>12.4.</b>
|
||||
|
||||
示例: 編碼S錶達式
|
||||
示例: 編碼S表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1823,7 +1823,7 @@
|
||||
|
||||
<b>12.6.</b>
|
||||
|
||||
示例: 解碼S錶達式
|
||||
示例: 解碼S表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1975,50 +1975,14 @@
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="14" data-path="exercise/ex.html">
|
||||
|
||||
|
||||
<a href="../exercise/ex.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.</b>
|
||||
|
||||
習題解答
|
||||
</a>
|
||||
|
||||
|
||||
<ul class="articles">
|
||||
|
||||
|
||||
<li class="chapter " data-level="14.1" data-path="exercise/ex-ch1.html">
|
||||
|
||||
|
||||
<a href="../exercise/ex-ch1.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.1.</b>
|
||||
|
||||
第一章 入門
|
||||
</a>
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
|
||||
</ul>
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="15" data-path="errata.html">
|
||||
<li class="chapter " data-level="14" data-path="errata.html">
|
||||
|
||||
|
||||
<a href="../errata.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>15.</b>
|
||||
<b>14.</b>
|
||||
|
||||
勘誤
|
||||
</a>
|
||||
@@ -2059,9 +2023,9 @@
|
||||
|
||||
<section class="normal" id="section-">
|
||||
|
||||
<h2 id="82-示例-併髮的clock服務">8.2. 示例: 併髮的Clock服務</h2>
|
||||
<p>網絡編程是併髮大顯身手的一個領域,由於服務器是最典型的需要衕時處理很多連接的程序,這些連接一般來自遠彼此獨立的客戶端。在本小節中,我們會講解go語言的net包,這個包提供編寫一個網絡客戶端或者服務器程序的基本組件,無論兩者間通信是使用TCP,UDP或者Unix domain sockets。在第一章中我們已經使用過的net/http包裏的方法,也算是net包的一部分。</p>
|
||||
<p>我們的第一個例子是一個順序執行的時鍾服務器,它會每隔一秒鍾將當前時間寫到客戶端:</p>
|
||||
<h2 id="82-示例-併發的clock服務">8.2. 示例: 併發的Clock服務</h2>
|
||||
<p>網絡編程是併發大顯身手的一個領域,由於服務器是最典型的需要同時處理很多連接的程序,這些連接一般來自遠彼此獨立的客戶端。在本小節中,我們會講解go語言的net包,這個包提供編寫一個網絡客戶端或者服務器程序的基本組件,無論兩者間通信是使用TCP,UDP或者Unix domain sockets。在第一章中我們已經使用過的net/http包里的方法,也算是net包的一部分。</p>
|
||||
<p>我們的第一個例子是一個順序執行的時鐘服務器,它會每隔一秒鐘將當前時間寫到客戶端:</p>
|
||||
<pre><code class="lang-go">gopl.io/ch8/clock1
|
||||
<span class="hljs-comment">// Clock1 is a TCP server that periodically writes the time.</span>
|
||||
<span class="hljs-keyword">package</span> main
|
||||
@@ -2100,10 +2064,10 @@
|
||||
}
|
||||
}
|
||||
</code></pre>
|
||||
<p>Listen函數創建了一個net.Listener的對象,這個對象會監聽一個網絡端口上到來的連接,在這個例子裏我們用的是TCP的localhost:8000端口。listener對象的Accept方法會直接阻塞,直到一個新的連接被創建,然後會返迴一個net.Conn對象來錶示這個連接。</p>
|
||||
<p>handleConn函數會處理一個完整的客戶端連接。在一個for死循環中,將當前的時候用time.Now()函數得到,然後寫到客戶端。由於net.Conn實現了io.Writer接口,我們可以直接曏其寫入內容。這個死循環會一直執行,直到寫入失敗。最可能的原因是客戶端主動斷開連接。這種情況下handleConn函數會用defer調用關閉服務器側的連接,然後返迴到主函數,繼續等待下一個連接請求。</p>
|
||||
<p>time.Time.Format方法提供了一種格式化日期和時間信息的方式。它的參數是一個格式化模闆標識如何來格式化時間,而這個格式化模闆限定為Mon Jan 2 03:04:05PM 2006 UTC-0700。有8個部分(週幾,月份,一個月的第幾天,等等)。可以以任意的形式來組閤前麫這個模闆;齣現在模闆中的部分會作為參考來對時間格式進行輸齣。在上麫的例子中我們隻用到了小時、分鍾和秒。time包裏定義了很多標準時間格式,比如time.RFC1123。在進行格式化的逆曏操作time.Parse時,也會用到衕樣的策略。(譯註:這是go語言和其它語言相比比較奇葩的一個地方。。你需要記住格式化字符串是1月2日下午3點4分5秒零六年UTC-0700,而不像其它語言那樣Y-m-d H:i:s一樣,當然了這裏可以用1234567的方式來記憶,倒是也不麻煩)</p>
|
||||
<p>為了連接例子裏的服務器,我們需要一個客戶端程序,比如netcat這個工具(nc命令),這個工具可以用來執行網絡連接操作。</p>
|
||||
<p>Listen函數創建了一個net.Listener的對象,這個對象會監聽一個網絡端口上到來的連接,在這個例子里我們用的是TCP的localhost:8000端口。listener對象的Accept方法會直接阻塞,直到一個新的連接被創建,然後會返迴一個net.Conn對象來表示這個連接。</p>
|
||||
<p>handleConn函數會處理一個完整的客戶端連接。在一個for死循環中,將當前的時候用time.Now()函數得到,然後寫到客戶端。由於net.Conn實現了io.Writer接口,我們可以直接向其寫入內容。這個死循環會一直執行,直到寫入失敗。最可能的原因是客戶端主動斷開連接。這種情況下handleConn函數會用defer調用關閉服務器側的連接,然後返迴到主函數,繼續等待下一個連接請求。</p>
|
||||
<p>time.Time.Format方法提供了一種格式化日期和時間信息的方式。它的參數是一個格式化模闆標識如何來格式化時間,而這個格式化模闆限定爲Mon Jan 2 03:04:05PM 2006 UTC-0700。有8個部分(週幾,月份,一個月的第幾天,等等)。可以以任意的形式來組合前面這個模闆;齣現在模闆中的部分會作爲參考來對時間格式進行輸齣。在上面的例子中我們隻用到了小時、分鐘和秒。time包里定義了很多標準時間格式,比如time.RFC1123。在進行格式化的逆向操作time.Parse時,也會用到同樣的策略。(譯註:這是go語言和其它語言相比比較奇葩的一個地方。。你需要記住格式化字符串是1月2日下午3點4分5秒零六年UTC-0700,而不像其它語言那樣Y-m-d H:i:s一樣,當然了這里可以用1234567的方式來記憶,倒是也不麻煩)</p>
|
||||
<p>爲了連接例子里的服務器,我們需要一個客戶端程序,比如netcat這個工具(nc命令),這個工具可以用來執行網絡連接操作。</p>
|
||||
<pre><code>$ go build gopl.io/ch8/clock1
|
||||
$ ./clock1 &
|
||||
$ nc localhost 8000
|
||||
@@ -2112,7 +2076,7 @@ $ nc localhost 8000
|
||||
13:58:56
|
||||
13:58:57
|
||||
^C
|
||||
</code></pre><p>客戶端將服務器髮來的時間顯示了齣來,我們用Control+C來中斷客戶端的執行,在Unix繫統上,你會看到^C這樣的響應。如果你的繫統沒有裝nc這個工具,你可以用telnet來實現衕樣的效果,或者也可以用我們下麫的這個用go寫的簡單的telnet程序,用net.Dial就可以簡單地創建一個TCP連接:</p>
|
||||
</code></pre><p>客戶端將服務器發來的時間顯示了齣來,我們用Control+C來中斷客戶端的執行,在Unix繫統上,你會看到^C這樣的響應。如果你的繫統沒有裝nc這個工具,你可以用telnet來實現同樣的效果,或者也可以用我們下面的這個用go寫的簡單的telnet程序,用net.Dial就可以簡單地創建一個TCP連接:</p>
|
||||
<pre><code class="lang-go">gopl.io/ch8/netcat1
|
||||
<span class="hljs-comment">// Netcat1 is a read-only TCP client.</span>
|
||||
<span class="hljs-keyword">package</span> main
|
||||
@@ -2139,7 +2103,7 @@ $ nc localhost 8000
|
||||
}
|
||||
}
|
||||
</code></pre>
|
||||
<p>這個程序會從連接中讀取數據,併將讀到的內容寫到標準輸齣中,直到遇到end of file的條件或者髮生錯誤。mustCopy這個函數我們在本節的幾個例子中都會用到。讓我們衕時運行兩個客戶端來進行一個測試,這裏可以開兩個終端窗口,下麫左邊的是其中的一個的輸齣,右邊的是另一個的輸齣:</p>
|
||||
<p>這個程序會從連接中讀取數據,併將讀到的內容寫到標準輸齣中,直到遇到end of file的條件或者發生錯誤。mustCopy這個函數我們在本節的幾個例子中都會用到。讓我們同時運行兩個客戶端來進行一個測試,這里可以開兩個終端窗口,下面左邊的是其中的一個的輸齣,右邊的是另一個的輸齣:</p>
|
||||
<pre><code>$ go build gopl.io/ch8/netcat1
|
||||
$ ./netcat1
|
||||
13:58:54 $ ./netcat1
|
||||
@@ -2152,7 +2116,7 @@ $ ./netcat1
|
||||
^C
|
||||
$ killall clock1
|
||||
</code></pre><p>killall命令是一個Unix命令行工具,可以用給定的進程名來殺掉所有名字匹配的進程。</p>
|
||||
<p>第二個客戶端必鬚等待第一個客戶端完成工作,這樣服務端纔能繼續曏後執行;因為我們這裏的服務器程序衕一時間隻能處理一個客戶端連接。我們這裏對服務端程序做一點小改動,使其支持併髮:在handleConn函數調用的地方增加go關鍵字,讓每一次handleConn的調用都進入一個獨立的goroutine。</p>
|
||||
<p>第二個客戶端必鬚等待第一個客戶端完成工作,這樣服務端纔能繼續向後執行;因爲我們這里的服務器程序同一時間隻能處理一個客戶端連接。我們這里對服務端程序做一點小改動,使其支持併發:在handleConn函數調用的地方增加go關鍵字,讓每一次handleConn的調用都進入一個獨立的goroutine。</p>
|
||||
<pre><code class="lang-go">gopl.io/ch8/clock2
|
||||
<span class="hljs-keyword">for</span> {
|
||||
conn, err := listener.Accept()
|
||||
@@ -2163,7 +2127,7 @@ $ killall clock1
|
||||
<span class="hljs-keyword">go</span> handleConn(conn) <span class="hljs-comment">// handle connections concurrently</span>
|
||||
}
|
||||
</code></pre>
|
||||
<p>現在多個客戶端可以衕時接收到時間了:</p>
|
||||
<p>現在多個客戶端可以同時接收到時間了:</p>
|
||||
<pre><code>$ go build gopl.io/ch8/clock2
|
||||
$ ./clock2 &
|
||||
$ go build gopl.io/ch8/netcat1
|
||||
@@ -2179,12 +2143,12 @@ $ ./netcat1
|
||||
^C 14:03:02
|
||||
^C
|
||||
$ killall clock2
|
||||
</code></pre><p>練習8.1: 脩改clock2來支持傳入參數作為端口號,然後寫一個clockwall的程序,這個程序可以衕時與多個clock服務器通信,從多服務器中讀取時間,併且在一個錶格中一次顯示所有服務傳迴的結果,類似於你在某些辦公室裏看到的時鍾牆。如果你有地理學上分佈式的服務器可以用的話,讓這些服務器跑在不衕的機器上麫;或者在衕一颱機器上跑多個不衕的實例,這些實例監聽不衕的端口,假裝自己在不衕的時區。像下麫這樣:</p>
|
||||
</code></pre><p>練習8.1: 脩改clock2來支持傳入參數作爲端口號,然後寫一個clockwall的程序,這個程序可以同時與多個clock服務器通信,從多服務器中讀取時間,併且在一個表格中一次顯示所有服務傳迴的結果,類似於你在某些辦公室里看到的時鐘牆。如果你有地理學上分布式的服務器可以用的話,讓這些服務器跑在不同的機器上面;或者在同一颱機器上跑多個不同的實例,這些實例監聽不同的端口,假裝自己在不同的時區。像下面這樣:</p>
|
||||
<pre><code>$ TZ=US/Eastern ./clock2 -port 8010 &
|
||||
$ TZ=Asia/Tokyo ./clock2 -port 8020 &
|
||||
$ TZ=Europe/London ./clock2 -port 8030 &
|
||||
$ clockwall NewYork=localhost:8010 London=localhost:8020 Tokyo=localhost:8030
|
||||
</code></pre><p>練習8.2: 實現一個併髮FTP服務器。服務器應該解析客戶端來的一些命令,比如cd命令來切換目彔,ls來列齣目彔內文件,get和send來傳輸文件,close來關閉連接。你可以用標準的ftp命令來作為客戶端,或者也可以自己實現一個。</p>
|
||||
</code></pre><p>練習8.2: 實現一個併發FTP服務器。服務器應該解析客戶端來的一些命令,比如cd命令來切換目録,ls來列齣目録內文件,get和send來傳輸文件,close來關閉連接。你可以用標準的ftp命令來作爲客戶端,或者也可以自己實現一個。</p>
|
||||
|
||||
|
||||
</section>
|
||||
@@ -2198,7 +2162,7 @@ $ clockwall NewYork=localhost:8010 London=localhost:8020 Tokyo=localhost:8030
|
||||
<a href="../ch8/ch8-01.html" class="navigation navigation-prev " aria-label="Previous page: Goroutines"><i class="fa fa-angle-left"></i></a>
|
||||
|
||||
|
||||
<a href="../ch8/ch8-03.html" class="navigation navigation-next " aria-label="Next page: 示例: 併髮的Echo服務"><i class="fa fa-angle-right"></i></a>
|
||||
<a href="../ch8/ch8-03.html" class="navigation navigation-next " aria-label="Next page: 示例: 併發的Echo服務"><i class="fa fa-angle-right"></i></a>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user