├── Errata.md ├── README.md └── zh ├── Images ├── Cover.png ├── app.png ├── continuum.gif ├── cs-application.gif ├── cs-system.gif ├── dce.png ├── dist.png ├── expect.gif ├── gartner.gif ├── gartner1.gif ├── gartner2.gif ├── gartner3.gif ├── gartner4.gif ├── gartner5.gif ├── iso.gif ├── many-one.gif ├── middleware.gif ├── msg.gif ├── nfs.png ├── one-many.gif ├── one-one.gif ├── packets.gif ├── peer.gif ├── rpc.gif ├── rpc_stub.png ├── single.png ├── tcp_stack.gif ├── threetier.gif └── version.png ├── Styles └── stylesheet.css ├── Text ├── chapter-arch.html ├── chapter-channel.html ├── chapter-charsets.html ├── chapter-chinese.html ├── chapter-golang.html ├── chapter-html.html ├── chapter-http.html ├── chapter-protocol.html ├── chapter-rpc.html ├── chapter-security.html ├── chapter-serialisation.html ├── chapter-socket.html ├── chapter-template.html ├── chapter-websockets.html └── chapter-xml.html └── index.html /Errata.md: -------------------------------------------------------------------------------- 1 | Errata 2 | ======== 3 | 原文勘误表 4 | 5 | 格式如下: 6 | 7 | Chapter 8 | L, C: -> 9 | 10 | 其中: 11 | 12 | Ln = typo行号 13 | Cn = typo列号 14 | 15 | 例如: 16 | 17 | ## Chapter 1 Architecture 18 | 19 | L51, C76: sytems -> systems 20 | L58, C30: highl evel -> high level 21 | ? L84, C37: independance -> independence 22 | ... 23 | 24 | ## Chapter 2 Overview of the Go language 25 | None 26 | 27 | ## Chapter 3 Socket-level Programming 28 | Lxx, Cxx: xxx -> yyy 29 | ... 30 | 31 | 请各章译者自行添加到相应小节中;行号以源码原文为准。 32 | 注:由于作者所用英语为澳大利亚方言(en_AU),因此有些不能算typo,不确定的请前缀`?`。 33 | 34 | 勘误正文 35 | ======== 36 | 37 | ## Chapter 1 Architecture 38 | 39 | L51, C76 : sytems -> systems 40 | L58, C30 : highl evel -> high level 41 | ? L84, C37 : independance -> independence 42 | L91, C191: succesful -> successful 43 | L108, C6 : Thre -> There 44 | ? L114, C310: organisation -> organization 45 | ? L114, C343: organisation -> organization 46 | ? L118, C159: organisation -> organization 47 | L116, C194: Are -> Area 48 | L116, C228: Are -> Area 49 | ? L120, C341: compatable -> compatible 50 | L122, C245: smorgasborg -> smorgasbord 51 | L142, C23 : TFP -> TFTP 52 | ? L156, C52 : independant -> independent 53 | L186, C21 : lvel -> level 54 | L188, C276: similarlym -> similarly 55 | L230, C56 : intrepreting -> interpreting 56 | L230, C174: thir -> their 57 | L232, C60 : stroing -> storing 58 | L236, C47 : applicaitons -> applications 59 | L236, C139: sysem -> system 60 | L246, C141: usuall -> usually 61 | L248, C46 : Al -> All 62 | L252, C53 : acess -> access 63 | L254, C30 : scuh -> such 64 | L288, C68 : laelled -> labelled 65 | L288, C108: diplay -> display 66 | L292, C20 : teh -> the 67 | ? L373, C47 : incompatable -> incompatible 68 | ? L448, C204: recognise -> recognize 69 | 70 | ## Chapter 2 Overview of the Go language 71 | 72 | None 73 | 74 | ## Chapter 3 Socket-level Programming 75 | 76 | ## Chapter 4 Data serialisation 77 | 78 | ## Chapter 5 Application-Level Protocols 79 | 80 | ## Chapter 6 Managing character sets and encodings 81 | 82 | ## Chapter 7 Security 83 | 84 | ## Chapter 8 HTTP 85 | 86 | ## Chapter 9 Templates 87 | 88 | ## Chapter 10 A Complete Web Server 89 | 90 | ## Chapter 11 HTML 91 | 92 | ## Chapter 12 XML 93 | 94 | ## Chapter 13 Remote Procedure Call 95 | 96 | ## Chapter 14 Network channels 97 | 98 | ## Chapter 15 Web Sockets 99 | 100 | ## Index 101 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | NPWG_zh 2 | ======= 3 | 4 | Network programming with Go 中文翻译版本 5 | 6 | ## 翻译组成员 7 | 人名不分先后,是按照QQ群里面的成员名字顺序排列的: 8 | 9 | - 欧林猫 10 | - Asta谢 11 | - Lua 12 | - 四月份平民 13 | - 轩脉刃 14 | - JessonChan 15 | - KETQI 16 | - RaiDen 17 | - Wayne_Lau 18 | - 打柴人 19 | - 飛鱼 20 | - 士豆口 21 | - 吴文磊 22 | - Border 23 | - 微尘 24 | - chenzhekl 25 | 26 | ## 翻译计划 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 |
章节第一翻译者First Review最终 Review负责人
第一章:Architecture欧林猫Asta谢Lua四月份平民
第二章:Overview of the Go languageAsta谢
2012/12/27
Lua四月份平民轩脉刃
第三章:Socket-level ProgrammingLua四月份平民轩脉刃JessonChan
第四章:Data serialisation四月份平民轩脉刃JessonChanKETQI
第五章:Application-Level Protocols轩脉刃JessonChanKETQIRaiDen
第六章:Managing character sets and encodingsJessonChanKETQIRaiDenWayne_Lau
第七章:SecurityKETQIRaiDenWayne_Lau打柴人
第八章:HTTPRaiDenWayne_Lau打柴人飛鱼
第九章:TemplatesWayne_Lau打柴人飛鱼士豆口
第十章:A Complete Web Server打柴人飛鱼士豆口吴文磊
第十一章:HTML飛鱼士豆口吴文磊Border
第十二章:XML士豆口吴文磊Border微尘
第十三章:Remote Procedure Call吴文磊Border微尘欧林猫
第十四章:Network channelschenzhekl微尘欧林猫Asta谢
第十五章:Web Sockets微尘欧林猫Asta谢四月份平民
142 | 143 | ## 翻译格式 144 | 145 | 第一翻译、first review和最终Review的人需要保留英文 146 | 147 | - 英文里面`

`为一个段落,那么把这一段修改成`

`,那么相应的中文也是一个段落`

` 148 | 149 |

Please go to the main index for the content pages for network computing.

150 | 151 | 那么加上中文之后应该如下: 152 | 153 |

Please go to the main index for the content pages for network computing.

154 |

请访问主页获取网络编程的其他页面

155 | 156 | - 英文里面`

`为一个层,首先需要给这个层加上`class="en"`,然后那么相应的中文为`

` 157 | 158 |

Introduction

159 | 160 | 那么加上中文应该如下: 161 | 162 |

Introduction

163 |

介绍

164 | - 英文里面`
    `为一个列表,首先对原来的ul增加一个`class="en"`,然后相应的中文为`
      ` 165 | 166 | 173 | 174 | 那么加上中文之后应该如下: 175 | 176 | 183 | 190 | 191 | ## 翻译约定 192 | 193 | 有些专用词无需翻译 194 | 195 | - channel 196 | - goroutine 197 | -------------------------------------------------------------------------------- /zh/Images/Cover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astaxie/NPWG_zh/50b2885cd8b47ff751e05f83fdb8739bec69bede/zh/Images/Cover.png -------------------------------------------------------------------------------- /zh/Images/app.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astaxie/NPWG_zh/50b2885cd8b47ff751e05f83fdb8739bec69bede/zh/Images/app.png -------------------------------------------------------------------------------- /zh/Images/continuum.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astaxie/NPWG_zh/50b2885cd8b47ff751e05f83fdb8739bec69bede/zh/Images/continuum.gif -------------------------------------------------------------------------------- /zh/Images/cs-application.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astaxie/NPWG_zh/50b2885cd8b47ff751e05f83fdb8739bec69bede/zh/Images/cs-application.gif -------------------------------------------------------------------------------- /zh/Images/cs-system.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astaxie/NPWG_zh/50b2885cd8b47ff751e05f83fdb8739bec69bede/zh/Images/cs-system.gif -------------------------------------------------------------------------------- /zh/Images/dce.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astaxie/NPWG_zh/50b2885cd8b47ff751e05f83fdb8739bec69bede/zh/Images/dce.png -------------------------------------------------------------------------------- /zh/Images/dist.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astaxie/NPWG_zh/50b2885cd8b47ff751e05f83fdb8739bec69bede/zh/Images/dist.png -------------------------------------------------------------------------------- /zh/Images/expect.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astaxie/NPWG_zh/50b2885cd8b47ff751e05f83fdb8739bec69bede/zh/Images/expect.gif -------------------------------------------------------------------------------- /zh/Images/gartner.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astaxie/NPWG_zh/50b2885cd8b47ff751e05f83fdb8739bec69bede/zh/Images/gartner.gif -------------------------------------------------------------------------------- /zh/Images/gartner1.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astaxie/NPWG_zh/50b2885cd8b47ff751e05f83fdb8739bec69bede/zh/Images/gartner1.gif -------------------------------------------------------------------------------- /zh/Images/gartner2.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astaxie/NPWG_zh/50b2885cd8b47ff751e05f83fdb8739bec69bede/zh/Images/gartner2.gif -------------------------------------------------------------------------------- /zh/Images/gartner3.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astaxie/NPWG_zh/50b2885cd8b47ff751e05f83fdb8739bec69bede/zh/Images/gartner3.gif -------------------------------------------------------------------------------- /zh/Images/gartner4.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astaxie/NPWG_zh/50b2885cd8b47ff751e05f83fdb8739bec69bede/zh/Images/gartner4.gif -------------------------------------------------------------------------------- /zh/Images/gartner5.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astaxie/NPWG_zh/50b2885cd8b47ff751e05f83fdb8739bec69bede/zh/Images/gartner5.gif -------------------------------------------------------------------------------- /zh/Images/iso.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astaxie/NPWG_zh/50b2885cd8b47ff751e05f83fdb8739bec69bede/zh/Images/iso.gif -------------------------------------------------------------------------------- /zh/Images/many-one.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astaxie/NPWG_zh/50b2885cd8b47ff751e05f83fdb8739bec69bede/zh/Images/many-one.gif -------------------------------------------------------------------------------- /zh/Images/middleware.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astaxie/NPWG_zh/50b2885cd8b47ff751e05f83fdb8739bec69bede/zh/Images/middleware.gif -------------------------------------------------------------------------------- /zh/Images/msg.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astaxie/NPWG_zh/50b2885cd8b47ff751e05f83fdb8739bec69bede/zh/Images/msg.gif -------------------------------------------------------------------------------- /zh/Images/nfs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astaxie/NPWG_zh/50b2885cd8b47ff751e05f83fdb8739bec69bede/zh/Images/nfs.png -------------------------------------------------------------------------------- /zh/Images/one-many.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astaxie/NPWG_zh/50b2885cd8b47ff751e05f83fdb8739bec69bede/zh/Images/one-many.gif -------------------------------------------------------------------------------- /zh/Images/one-one.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astaxie/NPWG_zh/50b2885cd8b47ff751e05f83fdb8739bec69bede/zh/Images/one-one.gif -------------------------------------------------------------------------------- /zh/Images/packets.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astaxie/NPWG_zh/50b2885cd8b47ff751e05f83fdb8739bec69bede/zh/Images/packets.gif -------------------------------------------------------------------------------- /zh/Images/peer.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astaxie/NPWG_zh/50b2885cd8b47ff751e05f83fdb8739bec69bede/zh/Images/peer.gif -------------------------------------------------------------------------------- /zh/Images/rpc.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astaxie/NPWG_zh/50b2885cd8b47ff751e05f83fdb8739bec69bede/zh/Images/rpc.gif -------------------------------------------------------------------------------- /zh/Images/rpc_stub.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astaxie/NPWG_zh/50b2885cd8b47ff751e05f83fdb8739bec69bede/zh/Images/rpc_stub.png -------------------------------------------------------------------------------- /zh/Images/single.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astaxie/NPWG_zh/50b2885cd8b47ff751e05f83fdb8739bec69bede/zh/Images/single.png -------------------------------------------------------------------------------- /zh/Images/tcp_stack.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astaxie/NPWG_zh/50b2885cd8b47ff751e05f83fdb8739bec69bede/zh/Images/tcp_stack.gif -------------------------------------------------------------------------------- /zh/Images/threetier.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astaxie/NPWG_zh/50b2885cd8b47ff751e05f83fdb8739bec69bede/zh/Images/threetier.gif -------------------------------------------------------------------------------- /zh/Images/version.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astaxie/NPWG_zh/50b2885cd8b47ff751e05f83fdb8739bec69bede/zh/Images/version.png -------------------------------------------------------------------------------- /zh/Styles/stylesheet.css: -------------------------------------------------------------------------------- 1 | 2 | 3 | div.chapter { 4 | text-align: center; 5 | } 6 | 7 | div.preface { 8 | margin-left: 10em; 9 | margin-right: 10em; 10 | } 11 | 12 | div.warning { 13 | color: red; 14 | } 15 | 16 | 17 | body { counter-reset: section; 18 | } 19 | 20 | h2 { counter-increment: section; } 21 | h1:before { content: "Chapter " counter(chapter) " "; } 22 | h2:before { content: counter(chapter) "." counter(section) " "; } 23 | 24 | 25 | code {color: darkblue; font-weight: bold} 26 | pre {color: darkblue; background: #DDDDDD; font-weight: bold} 27 | em {color: blue; font-style: italic} 28 | 29 | P {background: white} 30 | 31 | textarea {font-family: monospace; font-weight: bold} 32 | 33 | 34 | .interactive_program {background: white} 35 | .interactive_program {color: green} 36 | 37 | .list {background: #CCCCFF} 38 | -------------------------------------------------------------------------------- /zh/Text/chapter-arch.html: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | Distributed Systems Architecture 8 | 9 | 分布式系统架构 10 | 11 | 25 | 34 | 43 | 44 | 45 | 46 |
      47 |

      Architecture

      48 | 49 |

      架构

      50 |
      51 | 52 |
      53 | 54 |
      55 |

      This chapter covers the major architectural features of distributed sytems.

      56 | 57 |

      本章涵盖了分布式系统架构的主要特性。

      58 |
      59 | 60 |

      Introduction

      61 | 62 |

      前言

      63 | 64 |

      You can't build a system without some idea of what you want to build. And you can't build it if you don't know the environment in which it will work. GUI programs are different to batch processing programs; games programs are different to business programs; and distributed programs are different to standalone programs. They each have their approaches, their common patterns, the problems that typically arise and the solutions that are often used.

      65 | 66 |

      你若不知道你想构建什么,就无法构建一个系统。而如果你不知道它会在何种环境下工作,也同样不行。就像GUI程序不同于批处理程序,游戏程序不同于商业程序一样,分布式程序也不同于独立的程序。它们都有各自的方法,常见的模式,经常出现的问题以及常用的解决方案。

      67 | 68 |

      This chapter covers the highl evel architectural aspects of distributed systems. There are many ways of looking at such systems, and many of these are dealt with.

      69 | 70 |

      本章涵盖了分布式系统的上层架构,从多种角度考虑了这样的系统及其依赖。

      71 | 72 |

      Protocol Layers

      73 | 74 |

      协议层

      75 | 76 |

      Distributed systems are hard. There are multiple computers involved, which have to be connected in some way. Programs have to be written to run on each computer in the system and they all have to co-operate to get a distributed task done.

      77 | 78 |

      分布式系统很复杂,它涉及到多台计算机的连接方式。我们编写的程序必须能在该系统中的每一台计算机上运行,它们必须都能协同操作来完成一项分布式任务。

      79 | 80 |

      The common way to deal with complexity is to break it down into smaller and simpler parts. These parts have their own structure, but they also have defined means of communicating with other related parts. In distributed systems, the parts are called protocol layers and they have clearly defined functions. They form a stack, with each layer communicating with the layer above and the layer below. The communication between layers is defined by protocols.

      81 | 82 |

      解决这种复杂性的一般方法,就是将它分解为更小更简单的部分。这些部分都有它们自己的结构,但也定义了与其它相关部分进行通信的方式。在分布式系统中,这种部分称为协议层,它们的功能都有明确的定义。它们在一起形成层次结构,并与其各自的上下层进行通行。层次之间的通信则由协议来定义。

      83 | 84 |

      Network communications requires protocols to cover high-level application communication all the way down to wire communication and the complexity handled by encapsulation in protocol layers.

      85 | 86 |

      网络通信所需的协议覆盖了从上层应用通信一直到底层有线通信的所有方式,它们的复杂性通过在协议层中进行封装来处理。

      87 | 88 |

      ISO OSI Protocol

      89 | 90 |

      ISO OSI协议

      91 | 92 |

      Although it was never properly implemented, the OSI (Open Systems Interconnect) protocol has been a major influence in ways of talking about and influencing distributed systems design. It is commonly given in the following figure:
      93 |

      94 | 95 |

      尽管OSI(开放系统互联)协议从未被完整地实现过,但它仍对分布式系统的讨论和设计产生了十分重要的影响。它的结构大致为下图所示:
      96 |

      97 | 98 |

      OSI layers

      99 | 100 |

      OSI层

      101 | 102 |

      The function of each layer is:

      103 | 104 |

      每一层的功能为:

      105 | 106 |
        107 |
      • Network layer provides switching and routing technologies
      • 108 | 109 |
      • Transport layer provides transparent transfer of data between end systems and is responsible for end-to-end error recovery and flow control
      • 110 | 111 |
      • Session layer establishes, manages and terminates connections between applications.
      • 112 | 113 |
      • Presentation layer provides independance from differences in data representation (e.g. encryption)
      • 114 | 115 |
      • Application layer supports application and end-user processes
      • 116 |
      117 | 118 |
        119 |
      • 网络层提供交换和路由技术
      • 120 | 121 |
      • 传输层在终端系统间提供透明的数据传输,并负责端对端的错误恢复及流程控制
      • 122 | 123 |
      • 会话层在应用间建立、管理并结束连接
      • 124 | 125 |
      • 表现层提供数据表现差异的独立性(例如加密)
      • 126 | 127 |
      • 应用层支持应用与最终用户的处理
      • 128 |
      129 | 130 |

      TCP/IP Protocol

      131 | 132 |

      TCP/IP协议

      133 | 134 |

      While the OSI model was being argued, debated, partly implemented and fought over, the DARPA internet research project was busy building the TCP/IP protocols. These have been immensely succesful and have led to The Internet (with capitals). This is a much simpler stack:
      135 |

      136 | 137 |

      当OSI标准模型正在为实现细节闹得不可开交时,DARPA互联网技术项目却在忙着构建TCP/IP协议。它们取得了极大的成功,并引领了Internet(首字母大写),因为这是个更简单的层次结构:
      138 |

      139 | 140 |

      Some Alternative Protocols

      141 | 142 |

      一些可选的协议

      143 | 144 |

      Although it almost seems like it, the TCP/IP protocols are not the only ones in existence and in the long run may not even be the most successful. There are many protocols occupying significant niches, such as

      145 | 146 |

      尽管现在到处都是TCP/IP协议,但它并不是唯一存在的。从长远来看,它甚至不会是最成功的。还有些协议占有重要的地位,比如:

      147 | 148 |
        149 |
      • Firewire
      • 150 | 151 |
      • USB
      • 152 | 153 |
      • Bluetooth
      • 154 | 155 |
      • WiFi
      • 156 |
      157 | 158 |
        159 |
      • 火线
      • 160 | 161 |
      • USB
      • 162 | 163 |
      • 蓝牙
      • 164 | 165 |
      • WiFi
      • 166 |
      167 | 168 |

      Thre is active work continuing on many other protocols, even quite bizarre ones such as those for the "internet in space."

      169 | 170 |

      还有些其它的协议在继续活跃地工作,甚至还有些像“太空互联网”这样奇怪的协议。

      171 | 172 |

      The focus in this book will be on the TCP/IP, but you should be aware of these other ones.

      173 | 174 |

      本书将重点介绍TCP/IP,但你也应当了解一些其它的协议。

      175 | 176 |

      Networking

      177 | 178 |

      网络

      179 | 180 |

      A network is a communications system for connecting end systems called hosts. The mechanisms of connection might be copper wire, ethernet, fibre optic or wireless, but that won't concern us here. A local area network (LAN) connects computers that are close together, typically belonging to a home, small organisation or part of a larger organisation.

      181 | 182 |

      网络是一个通信系统,它连接了称为主机的最终系统。这种连接机制可以是铜线、以太网、光纤或无线,但这些与我们无关。局域网(LAN)将计算机紧密地连接在一起,一般为家庭、小型组织或大型组织的一部分。

      183 | 184 |

      A Wide Area Network (WAN) connects computers across a larger physical area, such as between cities. There are other types as well, such as MANs (Metropolitan Area Network), PANs (Personal Are Networks) and even BANs (Body Are Network).

      185 | 186 |

      广域网(WAN)连接起一个更大物理区域的计算机,例如城际间。还有些其它的类型,如城域网(MAN)、个人域网(PAN)甚至人体域网(BAN)。

      187 | 188 |

      An internet is a connection of two or more distinct networks, typically LANs or WANs. An intranet is an internet with all networks belonging to a single organisation.

      189 | 190 |

      互联网是多个不同网络的连接,一般为LAN或WAN。内联网是属于某个组织的所有网络加上互联网。

      191 | 192 |

      There are significant differences between an internet and an intranet. Typically an intranet will be under a single administrative control, which will impose a single set of coherent policies. An internet on the other hand will not be under the control of a single body, and the controls exercised over different parts may not even be compatable.

      193 | 194 |

      互联网与内联网之间有明显的不同。一般来说,一个内联网处在单一的管控之下,它将被应用一组统一的策略。另一方面,一个互联网则不会在单一主体的控制之下,控制的不同部分甚至可能会不兼容。

      195 | 196 |

      A trivial example of such differences is that an intranet will often be restricted to computers by a small number of vendors running a standardised version of a particular operating system. On the other hand, an internet will often have a smorgasborg of different computers and operating systems.

      197 | 198 |

      这种不同的一个例子,就是一个内联网通常被少量供应商提供的,运行着特定操作系统标准化版本的计算机所限制。另一方面,一个互联网通常有各种各样的计算机和操作系统。

      199 | 200 |

      The techniques of this book will be applicable to internets. They will also be valid for intranets, but there you will also find specialised, non-portable systems.

      201 | 202 |

      本书中的技术可应用于互联网。它们对内联网也是有效的,但你也会发现一些专有的,不可移植的系统。

      203 | 204 |

      And then there is the "mother" of all internets: The Internet. This is just a very, very large internet that connects us to Google, my computer to your computer and so on.

      205 | 206 |

      所有互联网都有一个“母网”:因特网。它其实就是个非常巨大的互联网,它将我们与Google、我们的计算机等等互相连接起来。

      207 | 208 |

      Gateways

      209 | 210 |

      网关

      211 | 212 |

      A gateway is a generic term for an entity used to connect two or more networks. A repeater operates at the physical level copies the information from one subnet to another. A bridge operates at the data link layer level and copies frames between networks. A router operates at the network level and not only moves information between networks but also decides on the route.

      213 | 214 |

      网关是一个统称,它用于连接起一个或多个网络。其中的中继器在物理层面上进行操作,它将信息从一个子网复制到另一个子网上。桥接在数据连接层面上进行操作,它在网络之间复制帧。路由器在网络层面上进行操作,它不仅在网络之间复制信息,还决定了信息的传输路线。

      215 | 216 |

      Packet encapsulation

      217 | 218 |

      数据包封装

      219 | 220 |

      The communication between layers in either the OSI or the TCP/IP stacks is done by sending packets of data from one layer to the next, and then eventually across the network. Each layer has administrative information that it has to keep about its own layer. It does this by adding header information to the packet it receives from the layer above, as the packet passes down. On the receiving side, these headers are removed as the packet moves up.

      221 | 222 |

      在OIS或TCP/IP协议栈层与层之间的通信,是通过将数据包从一个层发送到下一个层,最终穿过整个网络的。每一层都有必须保持其自身层的管理信息。从上层接收到的数据包在向下传递时,会添加头信息。在接收端,这些头信息会在向上传递时移除。

      223 | 224 |

      For example, the TFP (Trivial File Transfer Protocol) moves files from one computer to another. It uses the UDP protocol on top of the IP protocol, which may be sent over Ethernet. This looks like:
      225 |
      226 | The packet transmitted over ethernet, is of course the bottom one.

      227 | 228 |

      例如,TFTP(普通文件传输协议)将文件从一台计算机移动到另一台上。它使用IP协议上的UDP协议,该协议可通过以太网发送。看起来就像这样:
      229 |
      230 | 通过以太网发送的数据包,当然是底部那个。

      231 | 232 |

      Connection Models

      233 | 234 |

      连接模型

      235 | 236 |

      In order for two computers to communicate, they must set up a path whereby they can send at least one message in a session. There are two major models for this:

      237 | 238 |

      为了两个计算机进行通信,就必须建立一个路径,使他们能够在一个会话中发送至少一条消息。有两个主要的模型:

      239 | 240 |
        241 |
      • Connection oriented
      • 242 | 243 |
      • Connectionless
      • 244 |
      245 | 246 |
        247 |
      • 面向连接模型
      • 248 | 249 |
      • 无连接模型
      • 250 |
      251 | 252 |

      Connection oriented

      253 | 254 |

      面向连接模型

      255 | 256 |

      A single connection is established for the session. Two-way communications flow along the connection. When the session is over, the connection is broken. The analogy is to a phone conversation. An example is TCP

      257 | 258 |

      即为会话建立单个连接,沿着连接进行双向通信。当会话结束后,该连接就会断开。这类似于电话交谈。例子就是TCP。

      259 | 260 |

      Connectionless

      261 | 262 |

      无连接模型

      263 | 264 |

      In a connectionless system, messages are sent independant of each other. Ordinary mail is the analogy. Connectionless messages may arrive out of order. An example is the IP protocol. Connection oriented transports may be established on top of connectionless ones - TCP over IP. Connectionless transports my be established on top of connection oriented ones - HTTP over TCP.

      265 | 266 |

      在无连接系统中,消息的发送彼此独立。这类似于普通的邮件。无连接模型的消息可能不按顺序抵达。例子就是IP协议。面向连接的传输可通过无连接模型——基于IP的TCP协议建立。无连接传输可通过面向连接模型——基于IP的HTTP协议建立。

      267 | 268 |

      There can be variations on these. For example, a session might enforce messages arriving, but might not guarantee that they arrive in the order sent. However, these two are the most common.

      269 | 270 |

      这些是可变的。例如,会话可能会强制消息抵达,但可能无法保证它们按照发送的顺序抵达。不过这两个是最常见的。

      271 | 272 |

      Communications Models

      273 | 274 |

      通信模型

      275 | 276 |

      Message passing

      277 | 278 |

      消息传递

      279 | 280 |

      Some non-procedural languages are built on the principle of message passing. Concurrent languages often use such a mechanism, and the most well known example is probably the Unix pipeline. The Unix pipeline is a pipeline of bytes, but there is not an inherent limitation: Microsoft's PowerShell can send objects along its pipelines, and concurrent languages such as Parlog could send arbitrary logic data structures in messages between concurrent processes.

      281 | 282 |

      一些非过程化语言建立在消息传递原理上。并发语言经常使用这种机制,最有名的大概要数Unix的管道了。Unix管道就是一管字节,但它并没有固定的限制:微软的PowerShell可沿着其管道发送对象;而像Parlog这样的并发语言,则能在并发的进程之间,将任意的逻辑数据结构当做消息来发送。

      283 | 284 |

      Message passing is a primitive mechanism for distributed systems. Set up a connection and pump some data down it. At the other end, figure out what the message was and respond to it, possibly sending messages back. This is illustrated by
      285 |

      286 | 287 |

      消息传递是分布式系统最基本的机制,也就是建立连接并通过它传输一些数据。在另一端则需要理解这些消息的意思并做出响应,有时还需要返回一些消息。如下图所示:
      288 |

      289 | 290 |

      Low level event driven systems such as the X Window System function in a somewhat similar way: wait for message from a user (mouse clicks, etc), decode them and act on them.

      291 | 292 |

      诸如X窗口系统之类的底层事件驱动系统功能也采用了类似的方式:等待用户的消息(如鼠标点击等),对它们进行解码并做出反应。

      293 | 294 |

      Higher level event driven systems assume that this decoding has been done by the underlying system and the event is then dispatched to an appropriate object such as a ButtonPress handler. This can also be done in distributed message passing systems, whereby a message received across the network is partly decoded and dispatched to an appropriate handler.

      295 | 296 |

      更高层的事件驱动系统则假定底层系统已经解码完成,接着该事件被分配给适当的对象,如ButtonPress处理程序。这也适用于分布式消息传递系统,通过对从网络接收的消息进行部分解码,并分配给适当的处理程序。

      297 | 298 |

      Remote procedure call

      299 | 300 |

      远程过程调用

      301 | 302 |

      In any system, there is a transfer of information and flow control from one part of the system to another. In procedural languages this may consist of the procedure call, where information is placed on a call stack and then control flow is transferred to another part of the program.

      303 | 304 |

      在任何系统中,都有信息传输和流程控制来将该系统的一部分传到另一部分。在过程化语言中,它由过程调用来组成,其中的信息被放置到调用栈上,接着控制流程被传递至该程序的另一部分。

      305 | 306 |

      Even with procedure calls, there are variations. The code may be statically linked so that control transfers from one part of the program's executable code to another part. Due to the increasing use of library routines, it has become commonplace to have such code in dynamic link libraries (DLLs), where control transfers to an independent piece of code.

      307 | 308 |

      甚至过程调用也有变化。代码可被静态链接,以便于控制从该程序可执行代码的一部分传输到另一部分。随着库例程的使用日益增多,将这类代码作为动态链接库(DLL)也变得司空见惯了,它用来控制传输独立的代码片段。

      309 | 310 |

      DLLs run in the same machine as the calling code. it is a simple (conceptual) step to transfer control to a procedure running in a different machine. The mechanics of this are not so simple! However, this model of control has given rise to the "remote procedure call" (RPC) which is discussed in much detail in a later chapter. This is illustrated by
      311 |

      312 | 313 |

      DLL作为调用代码运行在相同的机器上。尽管对于不同机器上运行的过程传输控制来说,这种机制(在概念上)是一种简单的手段,但它实际上可不怎么简单!不过,这种控制模型却催生了“远程过程调用”(RPC),更多关于它的详情会在后面的章节中讨论。如下图所示:
      314 |

      315 | 316 |

      There is an historical oddity called the "lightweight remote procedure call" invented by Microsoft as they transitioned from 16-bit to 32-bit applications. A 16-bit application might need to transfer data to a 32-bit application on the same machine. That made it lightweight as there was no networking! But it had many of the other issues of RPC systems in data representations and conversion.

      317 | 318 |

      微软在从16位应该过渡到32位时,曾发明过一种称为“轻量远程过程调用”的奇怪东西。16位应用可能需要在相同的机器上向32位应用传输数据。由于没有网络,竟使得它很轻量!不过,它也有RPC系统在数据表达和转换上的其它问题。

      319 | 320 |

      Distributed Computing Models

      321 | 322 |

      分布式计算模型

      323 | 324 |

      At the highest lvel, we could consider the equivalence or the non-equivalence of components of a distributed system. The most common occurrence is an asymmetric one: a client sends requests to a server, and the server responds. This is a client-server system.

      325 | 326 |

      在最上层,我们可以考虑分布式系统的组件是否等价。最常见的就是不对等的情况:客户端向服务器发送请求,然后服务端响应。这就是客户端-服务器系统。

      327 | 328 |

      If both components are equivalent, both able to initiate and to respond to messages, then we have a peer-to-peer system. Note that this is a logical classification: one peer may be a 16,000 core mainframe, the other might be a mobile phone. But if both can act similarlym then they are peers.

      329 | 330 |

      若两个组件等价,且均可发起并响应信息,那么我们就有了一个点对点系统。注意这是个逻辑上的分类:一点可能是16,000个核心主机,而另一点可能只是个移动电话。但如果二者的行为类似,那么它们就都是点。

      331 | 332 |

      A third model is the so-called filter. Here one component passes information to another which modifies it before passing it to a third. This is a fairly common model: for example, the middle component gets information from a database as SQl records and transforms it into an HTML table for the third component (which might be a browser).

      333 | 334 |

      第三种模型也就是所谓的过滤器。有一个组件将信息传至另一个组件,它在修改该信息后会传至第三个组件。这是个相当普遍的模型:例如,中间组件通过SQL从数据库中获取信息,并将其转换为HTML表单提供给第三个组件(它可能是个浏览器)。

      335 | 336 |

      These are illustrated as:
      337 |

      338 | 339 |

      如下所示:
      340 |

      341 | 342 |

      Client/Server System

      343 | 344 |

      客户端/服务器系统

      345 | 346 |

      Another view of a client server system is
      347 |

      348 | 349 |

      客户端/服务器系统的另一种方式:
      350 |

      351 | 352 |

      Client/Server Application

      353 | 354 |

      客户端/服务器应用

      355 | 356 |

      And a third view is
      357 |

      358 | 359 |

      第三种方式:
      360 |

      361 | 362 |

      Server Distribution

      363 | 364 |

      服务器分布

      365 | 366 |

      A client-server systems need not be simple. The basic model is single client, single server
      367 |
      368 | but you can also have multiple clients, single server
      369 |
      370 | In this, the master receives requests and instead of handling them one at a time itself, passes them off to other servers to handle. This is a common model when concurrent clients are possible.

      371 | 372 |

      客户端/服务器系统并不简单。其基本模型是单一客户端,单一服务器:
      373 |
      374 | 不过你也可以有多个客户端,单一服务器:
      375 |
      376 | 这样,主站只需接收请求并处理一次,而无需将它们传递给其它服务器来处理。当客户端可能并发时,这就是个通用的模型。

      377 | 378 |

      There are also single client, multiple servers
      379 | which occurs frequently when a server needs to act as a client to other servers, such as a business logic server getting information from a database server. And of course, there could be multiple clients with multiple servers

      380 | 381 |

      还有单一客户端,多个服务器的情况:
      382 | 当一个服务器需要作为其它服务器的客户端时,这种情况就会经常发生,例如当业务逻辑服务器从数据库服务器获取信息时。当然,还可以有多个客户端,多个服务器的情况。

      383 | 384 |

      Component Distribution

      385 | 386 |

      组件分布

      387 | 388 |

      A simple but effective way of decomposing many applications is to consider them as made up of three parts:

      389 | 390 |

      分解一些应用的一个简单有效的方式就是把它们看做三部分:

      391 | 392 |
        393 |
      • Presentation component
      • 394 | 395 |
      • Application logic
      • 396 | 397 |
      • Data access
      • 398 |
      399 | 400 |
        401 |
      • 表现组件
      • 402 | 403 |
      • 应用逻辑
      • 404 | 405 |
      • 数据访问
      • 406 |
      407 | 408 |

      The presentation component is responsible for interactions with the user, both displaying data and gathering input. it may be a modern GUI interface with buttons, lists, menus, etc, or an older command-line style interface, asking questions and getting answers. The details are not important at this level.

      409 | 410 |

      表现组件负责与用户进行交互,即显示数据和采集输入。它可以是带有按钮、列表和菜单等等的现代GUI界面,或较老的命令行式界面,询问问题并获取答案。在这一层上,具体详情并不重要。

      411 | 412 |

      The application logic is responsible for intrepreting the users' responses, for applying business rules, for preparing queries and managing responses from the thir component.

      413 | 414 |

      应用逻辑组件负责解释用户的响应,根据应用业务规则,准备查询并管理来自其组件的响应。

      415 | 416 |

      The data access component is responsible for stroing and retrieving data. This will often be through a database, but not necessarily.

      417 | 418 |

      数据访问组件负责存储并检索数据。这一般是通过数据库进行,不过也不一定。

      419 | 420 |

      Gartner Classification

      421 | 422 |

      Gartner分类

      423 | 424 |

      Based on this threefold decomposition of applicaitons, Gartner considered how the components might be distributed in a client-server sysem. They came up with five models:
      425 |

      426 | 427 |

      基于这三部分的应用划分,Gartner公司考虑了这些组件在客户端-服务器系统中如何分布。他们想出了五种模型:
      428 |

      429 | 430 |

      Example: Distributed Database

      431 | 432 |

      示例:分布式数据库

      433 | 434 |
        435 |
      • Gartner classification: 1
      • 436 |
      437 | 438 |
        439 |
      • Gartner第一种分类:
      • 440 |
      441 | 442 |


      443 | Modern mobile phones make good examples of this: due to limited memory they may store a small part of a database locally so that they can usuall respond quickly. However, if data is required that is not held locally, then a request may be made to a remote database for that additional data.

      444 | 445 |


      446 | 现代的移动电话就是个很好的例子:由于内存有限,它们只能通过存储一小部分本地数据库,因此它们通常能快速响应。若请求的数据不在本地,那么可为该附加数据请求远程数据库。

      447 | 448 |

      Google maps forms another good example. Al of the maps reside on Google's servers. When one is requested by a user, the "nearby" maps are also downloaded into a small database in the browser. When the user moves the map a little bit, the extra bits required are already in the local store for quick response.

      449 | 450 |

      Google地图的形式是另一个很好的例子。所有的地图都在Google的服务器上。当用户请求时,“附近的”地图也会下载为一个浏览器中的小型数据库。当用户移动了一点地图时,额外的一点请求已经为快速响应在本地存储中了。

      451 | 452 |

      Example: Network File Service

      453 | 454 |

      示例:网络文件服务

      455 | 456 |

      Gartner classification 2 allows remote clients acess to a shared file system
      457 |
      458 | There are many examples of scuh systems: NFS, Microsoft shares, DCE, etc

      459 | 460 |

      Gartner第二种分类允许远程客户端访问已共享的文件系统:
      461 |
      462 | 这里有一些这类系统的例子:NFS、Microsoft共享和DCE等等。

      463 | 464 |

      Example: Web

      465 | 466 |

      示例:Web

      467 | 468 |

      An example of Gartner classification 3 is the Web with Java applets. This is a distributed hypertext system, with many additional mechanisms
      469 |

      470 | 471 |

      Gartner第三种分类的一个例子就是Web上的小型Java应用。以下为带有一些附加机制的分布式超文本系统:
      472 |

      473 | 474 |

      Example: Terminal Emulation

      475 | 476 |

      示例:终端仿真

      477 | 478 |

      An example of Gartner classification 4 is terminal emulation. This allows a remote system to act as a normal terminal on a local system.
      479 |
      480 | Telnet is the most common example of this.

      481 | 482 |

      Gartner第四种分类就是终端仿真。这允许远程系统在本地系统上作为普通的终端:
      483 |
      484 | Telnet就是最常见的例子。

      485 | 486 |

      Example: Expect

      487 | 488 |

      示例:预期(Expect)

      489 | 490 |

      Expect is a novel illustration of Gartner classification 5. It acts as a wrapper around a classical system such as a command-line interface. It builds an X Window interface around this, so that the user interacts with a GUI, and the GUI in turn interacts with the command-line interface.
      491 |

      492 | 493 |

      预期(Expect)是Gartner第五种分类的一种另类的演示。它的行为类似于命令行接口这样的经典系统。它在此之上建立了X窗口界面,以此来让用户与GUI进行交互,然后GUI转而与命令行界面进行交互。
      494 |

      495 | 496 |

      Example: X Window System

      497 | 498 |

      示例:X窗口系统

      499 | 500 |

      The X Window System itself is an example of Gartner classification 5. An application makes GUI calls such as DrawLine, but these are not handled directly but instead passed to an X Window server for rendering. This decouples the application view of windowing and the display view of windowing.
      501 |

      502 | 503 |

      X窗口系统本身也是Gartner第五种分类的一个例子。一个应用进行一次像 DrawLine 这样的GUI调用,但它并不直接进行处理,而是传递给X窗口服务来渲染。这可以解耦窗口应用视图和窗口显示视图。
      504 |

      505 | 506 |

      Three Tier Models

      507 | 508 |

      三层模型

      509 | 510 |

      of course, if you have two tiers, then you can have three, four, or more. Some of the three tier possibilities are shown in this diagram:
      511 |

      512 | 513 |

      当然,如果你有两层,你也可以有三层、四层甚至多层。下图展示了一些可能的三层模型:
      514 |

      515 | 516 |

      The modern Web is a good example of the rightmost of these. The backend is made up of a database, often running stored procedures to hold some of the database logic. The middle tier is an HTTP server such as Apache running PHP scripts (or Ruby on Rails, or JSP pages, etc). This will manage some of the logic and will have data such as HTML pages stored locally. The frontend is a browser to display the pages, under the control of some Javascript. In HTML 5, the frontend may also have a local database.

      517 | 518 |

      现代Web就是最右边那种模型很好的例子。后端建立为一个数据库,经常运行存储过程来保存一些数据库逻辑。中间层是一个Apache这样的运行PHP脚本(或Ruby on Rails,或JSP页面等)的HTTP服务器。这会管理一些逻辑和存储在本地的像HTML页面这样的数据。前端为显示由JavaScript控制的页面的浏览器。在HTML5中,前端也可以有一个本地数据库。

      519 | 520 |

      Fat vs thin

      521 | 522 |

      “胖”与“瘦”

      523 | 524 |

      A common labelling of components is "fat" or "thin". Fat components take up lots of memory and do complex processing. Thin components on the other hand, do little of either. There don't seem to be any "normal" size components, only fat or thin!

      525 | 526 |

      组件一般分为“胖”或“瘦”。“胖”组件占用大量的内存来做复杂的处理;“瘦”组件则恰恰相反,只占少量内存,做简单处理。似乎没有任何“正常”大小的组件,只有“胖”或“瘦”!

      527 | 528 |

      Fatness or thinness is a relative concept. Browsers are often laelled as thin because "all they do is diplay web pages". Firefox on my Linux box takes nearly 1/2 a gigabyte of memory, which I don't regard as small at all!

      529 | 530 |

      “胖”或“瘦”的概念是相对的。浏览器经常被分为“瘦”组件,因为“它仅仅显示Web页面”。但我的Linux盒子中的Firefox用了将近1/2GB的内存,我可一点也不觉得它很小!

      531 | 532 |

      Middleware model

      533 | 534 |

      中间件模型

      535 | 536 |

      Middleware is teh "glue" connecting components of a distributed system. The middleware model is
      537 |

      538 | 539 |

      中间件是连接器分布式系统组件的“胶水”层。中间件模型如图所示:
      540 |

      541 | 542 |

      Middleware

      543 | 544 |

      中间件

      545 | 546 |

      Components of middleware include

      547 | 548 |

      中间件组件包括:

      549 | 550 |
        551 |
      • The network services include things like TCP/IP
      • 552 | 553 |
      • The middleware layer is application-independent s/w using the network services
      • 554 | 555 |
      • Examples of middleware are: DCE, RPC, Corba
      • 556 | 557 |
      • Middleware may only perform one function (such as RPC) or many (such as DCE)
      • 558 |
      559 | 560 |
        561 |
      • 像TCP/IP这样的网络服务
      • 562 | 563 |
      • 中间件层是应用独立的,使用网络服务的软件
      • 564 | 565 |
      • 中间件的例子:DCE、RPC、Corba
      • 566 | 567 |
      • 中间件可能只执行一种功能(比如RPC)或多种功能(比如DCE)
      • 568 |
      569 | 570 |

      Middleware examples

      571 | 572 |

      中间件示例

      573 | 574 |

      Examples of middleware include

      575 | 576 |

      中间件的例子包括:

      577 | 578 |
        579 |
      • Primitive services such as terminal emulators, file transfer, email
      • 580 | 581 |
      • Basic services such as RPC
      • 582 | 583 |
      • Integrated services such as DCE, Network O/S
      • 584 | 585 |
      • Distributed object services such as CORBA, OLE/ActiveX
      • 586 | 587 |
      • Mobile object services such as RMI, Jini
      • 588 | 589 |
      • World Wide Web
      • 590 |
      591 | 592 |
        593 |
      • 像终端模拟器、文件传输或电子邮件这样的基础服务
      • 594 | 595 |
      • 像RPC这样的基础服务
      • 596 | 597 |
      • 像DCE、网络O/S这样的一体化服务
      • 598 | 599 |
      • 像CORBA、OLE/ActiveX这样的分布式对象服务
      • 600 | 601 |
      • 像RMI、Jini这样的移动对象服务
      • 602 | 603 |
      • 万维网
      • 604 |
      605 | 606 |

      Middleware functions

      607 | 608 |

      中间件的功能

      609 | 610 |

      The functions of middleware include

      611 | 612 |

      中间件的功能包括:

      613 | 614 |
        615 |
      • 在不同计算机上初始化过程
      • 616 | 617 |
      • 进行会话管理
      • 618 | 619 |
      • 允许客户端定位服务器的目录服务
      • 620 | 621 |
      • 进行远程数据访问
      • 622 | 623 |
      • 允许服务器处理多个客户端的并发控制
      • 624 | 625 |
      • 保证安全性和完整性
      • 626 | 627 |
      • 监控
      • 628 | 629 |
      • 终止本地处理和远程处理
      • 630 |
      631 | 632 |

      Continuum of Processing

      633 | 634 |

      连续处理

      635 | 636 |

      The Gartner model is based on a breakdown of an application into the components of presentation, application logic and data handling. A finer grained breakdown is
      637 |

      638 | 639 |

      Gartner模型基于将一个应用分解为表现组件、应用逻辑和数据处理。一个更细粒度的分解方式为:
      640 |

      641 | 642 |

      Points of Failure

      643 | 644 |

      故障点

      645 | 646 |

      Distributed applications run in a complex environment. This makes them much more prone to failure than standalone applications on a single computer. The points of failure include

      647 | 648 |

      分布式应用一般运行在复杂的环境中。这使得它比单一计算机上的独立应用更易发生故障。故障点包括:

      649 | 650 |
        651 |
      • The client side of the application could crash
      • 652 | 653 |
      • The client system may have h/w problems
      • 654 | 655 |
      • The client's network card could fail
      • 656 | 657 |
      • Network contention could cause timeouts
      • 658 | 659 |
      • There may be network address conflicts
      • 660 | 661 |
      • Network elements such as routers could fail
      • 662 | 663 |
      • Transmission errors may lose messages
      • 664 | 665 |
      • The client and server versions may be incompatable
      • 666 | 667 |
      • The server's network card could fail
      • 668 | 669 |
      • The server system may have h/w problems
      • 670 | 671 |
      • The server s/w may crash
      • 672 | 673 |
      • The server's database may become corrupted
      • 674 |
      675 | 676 |
        677 |
      • 应用可能会在客户端崩溃
      • 678 | 679 |
      • 客户端系统可能发生硬件问题
      • 680 | 681 |
      • 客户端的网卡可能发生故障
      • 682 | 683 |
      • 网络连接可能超时
      • 684 | 685 |
      • 网络地址可能冲突
      • 686 | 687 |
      • 像路由器这样的网络基础设备可能发生故障
      • 688 | 689 |
      • 传输错误可能会失去消息
      • 690 | 691 |
      • 客户端与服务器的版本可能不兼容
      • 692 | 693 |
      • 服务器的网卡可能发生故障
      • 694 | 695 |
      • 服务器系统可能发生硬件问题
      • 696 | 697 |
      • 服务器的软件可能崩溃
      • 698 | 699 |
      • 服务器的数据库可能损坏
      • 700 |
      701 | 702 |

      Applications have to be designed with these possible failures in mind. Any action performed by one component must be recoverable if failure occurs in some other part of the system. Techniques such as transactions and continuous error checking need to be employed to avoid errors.

      703 | 704 |

      在设计应用时必须考虑这些可能发生的故障。如果故障发生在系统的其它部分,那么由任何一个组件执行的操作都必须可恢复。这就需要采用事务和持续错误检测这类的计算来避免错误。

      705 | 706 |

      Acceptance Factors

      707 | 708 |

      接受因素

      709 | 710 |
        711 |
      • Reliability
      • 712 | 713 |
      • Performance
      • 714 | 715 |
      • Responsiveness
      • 716 | 717 |
      • Scalability
      • 718 | 719 |
      • Capacity
      • 720 | 721 |
      • Security
      • 722 |
      723 | 724 |
        725 |
      • 可靠性
      • 726 | 727 |
      • 性能
      • 728 | 729 |
      • 响应性
      • 730 | 731 |
      • 可扩展性
      • 732 | 733 |
      • 可容性
      • 734 | 735 |
      • 安全性
      • 736 |
      737 | 738 |

      Transparency

      739 | 740 |

      透明度

      741 | 742 |

      The "holy grails" of distributed systems are to provide the following:

      743 | 744 |

      分布式系统的“圣杯”就是提供以下几点:

      745 | 746 |
        747 |
      • access transparency
      • 748 | 749 |
      • location transparency
      • 750 | 751 |
      • migration transparency
      • 752 | 753 |
      • replication transparency
      • 754 | 755 |
      • concurrency transparency
      • 756 | 757 |
      • scalability transparency
      • 758 | 759 |
      • performance transparency
      • 760 | 761 |
      • failure transparency
      • 762 |
      763 | 764 |
        765 |
      • 访问透明度
      • 766 | 767 |
      • 位置透明度
      • 768 | 769 |
      • 迁移透明度
      • 770 | 771 |
      • 赋值透明度
      • 772 | 773 |
      • 并发透明度
      • 774 | 775 |
      • 扩展透明度
      • 776 | 777 |
      • 性能透明度
      • 778 | 779 |
      • 故障透明度
      • 780 |
      781 | 782 |

      Eight fallacies of distributed computing

      783 | 784 |

      分布式计算的八个误区

      785 | 786 |

      Sun Microsystems was a company that performed much of the early work in distributed systems, and even had a mantra "The network is the computer." Based on their experience over many years a number of the scientists at Sun came up with the following list of fallacies commonly assumed:

      787 | 788 |

      Sun微系统公司在分布式系统上做很很多早期的工作,他们甚至有一个口头禅:“网络就是计算机”。基于他们多年的经验,Sun的科学家总结了以下常见误区:

      789 | 790 |
        791 |
      1. The network is reliable.
      2. 792 | 793 |
      3. Latency is zero.
      4. 794 | 795 |
      5. Bandwidth is infinite.
      6. 796 | 797 |
      7. The network is secure.
      8. 798 | 799 |
      9. Topology doesn't change.
      10. 800 | 801 |
      11. There is one administrator.
      12. 802 | 803 |
      13. Transport cost is zero.
      14. 804 | 805 |
      15. The network is homogeneous.
      16. 806 |
      807 | 808 |
        809 |
      1. 网络是可靠的。
      2. 810 | 811 |
      3. 延迟为零。
      4. 812 | 813 |
      5. 带宽是无限的。
      6. 814 | 815 |
      7. 网络是安全的。
      8. 816 | 817 |
      9. 拓扑结构不会改变。
      10. 818 | 819 |
      11. 没有管理员。
      12. 820 | 821 |
      13. 传输成本为零。
      14. 822 | 823 |
      15. 网络是均等的。
      16. 824 |
      825 | 826 |

      Many of these directly impact on network programming. For example, the design of most remote procedure call systems is based on the premise that the network is reliable so that a remote procedure call will behave in the same way as a local call. The fallacies of zero latency and infinite bandwidth also lead to assumptions about the time duration of an RPC call being the same as a local call, whereas they are magnitudes of order slower.

      827 | 828 |

      这些问题直接影响着网络编程。例如,大部分远程过程调用系统的设计都基于网络是可靠的前提,从而导致了远程过程调用的行为与本地调用如出一辙。零风险和无限带宽的误区也导致了RPC调用的持续时间与本地调用相同的臆断,但实际上它要比本地调用慢很多。

      829 | 830 |

      The recognition of these fallacies led Java's RMI (remote method invocation) model to require every RPC call to potentially throw a RemoteException. This forced programmers to at least recognise the possibility of network error and to remind them that they could not expect the same speeds as local calls.

      831 | 832 |

      对于这些错误的认识导致了Java的RMI(远程方法调用)模型要求每一个潜在的RPC调用都要抛出一个 RemoteException 异常。这迫使程序员至少认识到了网络错误的可能性,并提醒他们不要期望这会与本地调用的速度相同。

      833 | 834 |

      Copyright Jan Newmarch, jan@newmarch.name

      835 | 836 |

      版权所有 © Jan Newmarch, jan@newmarch.name

      837 | 838 |

      If you like this book, please contribute using Flattr
      839 | or donate using PayPal

      840 | 841 |

      如果你喜欢本书,请通过Flattr
      842 | 或使用PayPal捐赠。

      843 | 844 |
      845 | 846 |
      847 | 848 | 849 | -------------------------------------------------------------------------------- /zh/Text/chapter-channel.html: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | Network channels 8 | 9 | 18 | 33 | 47 | 48 | 49 | 50 |
      51 |

      Network channels

      52 |

      网络 channels

      53 |
      54 | 55 |
      56 | 57 |
      58 | 59 |
      60 |

      Warning

      61 |

      警告

      62 | 63 |

      The netchan package is being reworked. While it was in earlier versions of Go, it is not in Go 1. It is available in the old/netchan package if you still need it. This chapter describes this old version. Do not use it for new code.

      64 |

      现在netchan包正在重新设计。出于对Go 1之前版本的兼容性考虑,可以在old/netchan下找到它。这一章描述的是旧版本的使用。请不要在新代码中使用它.

      65 |
      66 | 67 |

      Introduction

      68 |

      简介

      69 | 70 |

      There are many models for sharing information between communicating processes. One of the more elegant is Hoare's concept of channels. In this, there is no shared memory, so that none of the issues of accessing common memory arise. Instead, one process will send a message along a channel to another process. Channels may be synchronous, or asynchronous, buffered or unbuffered.

      71 |

      关于进程间共享信息有过许多模型。其中较为优美的是Hoare提出的channels模型。在这一模型中,不需要共享内存,因此读取共享内存引起的问题都可以避免。取而代之的是使用channel传递消息:一个进程通过一个channel向另一个进程发送消息,channels可以是同步的,也可以是异步的,可以是带缓冲的,也可以是不带缓冲的。

      72 | 73 |

      Go has channels as first order data types in the language. The canonical example of using channels is Erastophene's prime sieve: one goroutine generates integers from 2 upwards. These are pumped into a series of channels that act as sieves. Each filter is distinguished by a different prime, and it removes from its stream each number that is divisible by its prime. So the '2' goroutine filters out even numbers, while the '3' goroutine filters out multiples of 3. The first number that comes out of the current set of filters must be a new prime, and this is used to start a new filter with a new channel.

      74 |

      Go内建channel作为第一等数据类型。一个使用channel的经典例子是Erastophene的素数筛选器:使用一个goroutine从2开始生成整数,将这些数字送入一系列作为过滤器的channel,每一个过滤器由一个不同的素数标识,它们把能被自身代表素数整除的数从流中删除,所以“2”goroutine过滤掉所有偶数,“3”goroutine过滤掉所有3的倍数。第一个从这一系列过滤器中走出来的必然是一个新的素数,然后再开启新的channel,用新素数生成一个新的过滤器,循环往复。

      75 | 76 |

      The efficacy of many thousands of goroutines communicating by many thousands of channels depends on how well the implementation of these primitives is done. Go is designed to optimise these, so this type of program is feasible.

      77 |

      大量goroutine之间通过channel通信的效率取决于原语设计的好坏。Go天生为此优化,所以这种程序是可行的

      78 | 79 |

      Go also supports distributed channels using the netchan package. But network communications are thousands of times slower than channel communications on a single computer. Running a sieve on a network over TCP would be ludicrously slow. Nevertheless, it gives a programming option that may be useful in many situations.

      80 |

      Go也通过netchan包支持分布式channel。但是网络间channel通信的效率远比单一电脑上channel间通信的效率低。在网络上通过TCP协议运行一个筛选器更是慢的可怕。然而,这还是给程序员多了一个选择,而且这在某些情况下十分有用。

      81 | 82 |

      Go's network channel model is somewhat similar in concept to the RPC model: a server creates channels and registers them with the network channel API. A client does a lookup for channels on a server. At this point both sides have a shared channel over which they can communicate. Note that communication is one-way: if you want to send information both ways, open two channels one for each direction.

      83 |

      Go的网络channel模型某种意义上和RPC模型类似:服务器创建channel然后用网络channel API注册它们,客户端在服务器上查询channel。这样服务器和客户端就有了一个可以相互通信的共享channel。注意这种通信是单向的,如果你想要双向发送信息,为每个方向单独创建一个channel。

      84 | 85 |

      Channel server

      86 |

      服务器端Channel

      87 | 88 |

      In order to make a channel visible to clients, you need to export it. This is done by creating an exporter using NewExporter with no parameters. The server then calls ListenAndServe to lsiten and handle responses. This takes two parameters, the first being the underlying transport mechanism such as "tcp" and the second being the network listening address (usually just a port number.

      89 |

      要让一个channel对客户端可见,你需要导出它。这可以通过不带参数NewExporter创建一个新的导出,之后服务器调用ListenAndServe监听、处理请求。ListenAndServe带有两个参数,第一个是底层的传输机制,比如“tcp”;第二个是监听地址(通常只是一个端口号)。

      90 | 91 |

      For each channel, the server creates a normal local channel and then calls Export to bind this to the network channel. At the time of export, the direction of communication must be specified. Clients search for channels by name, which is a string. This is specified to the exporter.

      92 |

      对每个channel,服务器创建一个普通的本地channel,然后调用Export将它绑定到网络channel上。在导出的同时,必须指定通信的方向。客户端可以通过名字(一个字符串)搜寻channel。

      93 | 94 |

      The server then uses the local channels in the normal way, reading or writing on them. We illustrate with an "echo" server which reads lines and sends them back. It needs two channels for this. The channel that the client writes to we name "echo-out". On the server side this is a read channel. Similarly, the channel that the client reads from we call "echo-in", which is a write channel to the server.

      95 |

      服务器之后可以像使用本地channel一样,读取或是写入数据。我们以一个“echo”服务器为例,它读入文本,再原样发送回去。它需要两个channel,我们把客户端用来写入数据的channel叫做“echo-out”,在服务器端,用它来读取数据。相似的,客户端用来读取数据的channel叫做“echo-in”,服务器往里写入数据。

      96 | 97 |

      The server program is

      98 |

      服务器程序如下

      99 |
      
      100 | /* EchoServer
      101 |  */
      102 | package main
      103 | 
      104 | import (
      105 |         "fmt"
      106 |         "os"
      107 |         "old/netchan"
      108 | )
      109 | 
      110 | func main() {
      111 | 
      112 |         // exporter, err := netchan.NewExporter("tcp", ":2345")
      113 |  exporter := netchan.NewExporter()
      114 |         err := exporter.ListenAndServe("tcp", ":2345")
      115 |         checkError(err)
      116 | 
      117 |         echoIn := make(chan string)
      118 |         echoOut := make(chan string)
      119 |         exporter.Export("echo-in", echoIn, netchan.Send)
      120 |         exporter.Export("echo-out", echoOut, netchan.Recv)
      121 |         for {
      122 |                 fmt.Println("Getting from echoOut")
      123 |                 s, ok := <-echoOut
      124 |                 if !ok {
      125 |                         fmt.Printf("Read from channel failed")
      126 |                         os.Exit(1)
      127 |                 }
      128 |                 fmt.Println("received", s)
      129 | 
      130 |                 fmt.Println("Sending back to echoIn")
      131 |                 echoIn <- s
      132 |                 fmt.Println("Sent to echoIn")
      133 |         }
      134 | 
      135 | }
      136 | 
      137 | func checkError(err error) {
      138 |         if err != nil {
      139 |                 fmt.Println("Fatal error ", err.Error())
      140 |                 os.Exit(1)
      141 |         }
      142 | }
      143 | 
      144 | 145 |

      Note: at the time of writing, the server will sometimes fail with an error message "netchan export: error encoding client response". This is logged as Issue 1805

      146 |

      注意:在这篇教程写下的时候,服务器可能会收到“netchan export: error encoding client response”的错误消息,这个问题被登记为Issue 1805。

      147 | 148 |

      Channel client

      149 |

      客户端Channel

      150 | 151 |

      In order to find an exported channel, the client must import it. This is created using Import which takes a protocol and a network service address of "host:port". This is then used to import a network channel by name and bind it to a local channel. Note that channel variables are references, so you do not need to pass their addresses to functions that change them.

      152 |

      为了找到导出的channel,客户端必须导入它。使用Import方法可以完成,它接受两个参数:协议名和形如“host:port”网络服务地址。之后就能通过名字导入,并绑定到本地channel上。注意,channel变量是引用变量,不用向改变他们的函数传递channel的地址。

      153 | 154 |

      The following client gets two channels to and from the echo server, and then writes and reads ten messages:

      155 |

      以下客户端使用两个channel向/从服务器发送、接受消息,然后写入并读取收到的十条信息。

      156 |
      
      157 | /* EchoClient
      158 |  */
      159 | package main
      160 | 
      161 | import (
      162 |         "fmt"
      163 |         "old/netchan"
      164 |         "os"
      165 | )
      166 | 
      167 | func main() {
      168 |         if len(os.Args) != 2 {
      169 |                 fmt.Println("Usage: ", os.Args[0], "host:port")
      170 |                 os.Exit(1)
      171 |         }
      172 |         service := os.Args[1]
      173 | 
      174 |         importer, err := netchan.Import("tcp", service)
      175 |         checkError(err)
      176 | 
      177 |         fmt.Println("Got importer")
      178 |         echoIn := make(chan string)
      179 |         importer.Import("echo-in", echoIn, netchan.Recv, 1)
      180 |         fmt.Println("Imported in")
      181 | 
      182 |         echoOut := make(chan string)
      183 |         importer.Import("echo-out", echoOut, netchan.Send, 1)
      184 |         fmt.Println("Imported out")
      185 | 
      186 |         for n := 0; n < 10; n++ {
      187 |                 echoOut <- "hello "
      188 |                 s, ok := <-echoIn
      189 |                 if !ok {
      190 |                         fmt.Println("Read failure")
      191 |                         break
      192 |                 }
      193 |                 fmt.Println(s, n)
      194 |         }
      195 |         close(echoOut)
      196 |         os.Exit(0)
      197 | }
      198 | 
      199 | func checkError(err error) {
      200 |         if err != nil {
      201 |                 fmt.Println("Fatal error ", err.Error())
      202 |                 os.Exit(1)
      203 |         }
      204 | }
      205 | 
      206 | 207 |

      Handling Timeouts

      208 |

      处理超时

      209 | 210 |

      Because these channels use the network, there is alwasy the possibility of network errors leading to timeouts. Andrew Gerrand points out a solution using timeouts: "[Set up a timeout channel.] We can then use a select statement to receive from either ch or timeout. If nothing arrives on ch after one second, the timeout case is selected and the attempt to read from ch is abandoned."

      211 |

      因为channel使用网络通信,存在因为网络错误导致超时的可能性。Andrew Gerrand提出了一个办法timeouts: "[Set up a timeout channel.]我们可以使用select语句从ch或是timeout接受信息。如果超过1秒钟ch没有收到信息,timeout通道被选择,放弃从ch获取信息。

      212 |
      213 | 
      214 | timeout := make(chan bool, 1)
      215 | go func() {
      216 |     time.Sleep(1e9) // one second
      217 |     timeout <- true
      218 | }()
      219 | 
      220 | select {
      221 | case <- ch:
      222 |     // a read from ch has occurred
      223 | case <- timeout:
      224 |     // the read from ch has timed out
      225 | }
      226 | 
      227 | 
      228 | 229 |

      Channels of channels

      230 |

      传递channel的channel

      231 | 232 |

      The online Go tutorial at http://golang.org/doc/go_tutorial.html has an example of multiplexing, where channels of channels are used. The idea is that instread of sharing one channel, a new communicator is given their own channel to have a privagye conversation. That is, a client is sent a channel from a server through a shared channel, and uses that private channel.

      233 |

      在线Go指导(http://golang.org/doc/go_tutorial.html)中展示了一个有点复杂的例子,其中使用了传递channel的channel。这个方法避免了总是使用共享channel,新的进程被赋予他们自己的channel进行私有交流。即,客户端通过共享channel从服务器端获得一个channel,之后使用这个私有channel进行通信。

      234 | 235 |

      This doesn't work directly with network channels: a channel cannot be sent over a network channel. So we have to be a little more indirect. Each time a client connects to a server, the server builds new network channels and exports them with new names. Then it sends the names of these new channels to the client which imports them. It uses these new channels for communicaiton.

      236 |

      然而这对网络channel不起作用,网络channel不能发送channel,所以我们要稍微绕点弯路。每次客户端连接服务器,服务器建立一个新的channel,然后用新名字导出他们。之后向导入它们的客户端发送这些新channel的名字。最后使用这些新channel进行通信。

      237 | 238 |

      A server is

      239 |

      服务器代码如下

      240 |
      
      241 | /* EchoChanServer
      242 |  */
      243 | package main
      244 | 
      245 | import (
      246 |         "fmt"
      247 |         "os"
      248 |         "old/netchan"
      249 |         "strconv"
      250 | )
      251 | 
      252 | var count int = 0
      253 | 
      254 | func main() {
      255 | 
      256 |         exporter := netchan.NewExporter()
      257 |         err := exporter.ListenAndServe("tcp", ":2345")
      258 |         checkError(err)
      259 | 
      260 |         echo := make(chan string)
      261 |         exporter.Export("echo", echo, netchan.Send)
      262 |         for {
      263 |                 sCount := strconv.Itoa(count)
      264 |                 lock := make(chan string)
      265 |                 go handleSession(exporter, sCount, lock)
      266 | 
      267 |                 <-lock
      268 |                 echo <- sCount
      269 |                 count++
      270 |                 exporter.Drain(-1)
      271 |         }
      272 | }
      273 | 
      274 | func handleSession(exporter *netchan.Exporter, sCount string, lock chan string) {
      275 |         echoIn := make(chan string)
      276 |         exporter.Export("echoIn"+sCount, echoIn, netchan.Send)
      277 | 
      278 |         echoOut := make(chan string)
      279 |         exporter.Export("echoOut"+sCount, echoOut, netchan.Recv)
      280 |         fmt.Println("made " + "echoOut" + sCount)
      281 | 
      282 |         lock <- "done"
      283 | 
      284 |         for {
      285 |                 s := <-echoOut
      286 |                 echoIn <- s
      287 |         }
      288 |         // should unexport net channels
      289 | }
      290 | 
      291 | func checkError(err error) {
      292 |         if err != nil {
      293 |                 fmt.Println("Fatal error ", err.Error())
      294 |                 os.Exit(1)
      295 |         }
      296 | }
      297 | 
      298 | 299 |

      and a client is

      300 |

      客户端代码如下

      301 |
      
      302 | /* EchoChanClient
      303 |  */
      304 | package main
      305 | 
      306 | import (
      307 |         "fmt"
      308 |         "old/netchan"
      309 |         "os"
      310 | )
      311 | 
      312 | func main() {
      313 |         if len(os.Args) != 2 {
      314 |                 fmt.Println("Usage: ", os.Args[0], "host:port")
      315 |                 os.Exit(1)
      316 |         }
      317 |         service := os.Args[1]
      318 | 
      319 |         importer, err := netchan.Import("tcp", service)
      320 |         checkError(err)
      321 | 
      322 |         fmt.Println("Got importer")
      323 |         echo := make(chan string)
      324 |         importer.Import("echo", echo, netchan.Recv, 1)
      325 |         fmt.Println("Imported in")
      326 | 
      327 |         count := <-echo
      328 |         fmt.Println(count)
      329 | 
      330 |         echoIn := make(chan string)
      331 |         importer.Import("echoIn"+count, echoIn, netchan.Recv, 1)
      332 | 
      333 |         echoOut := make(chan string)
      334 |         importer.Import("echoOut"+count, echoOut, netchan.Send, 1)
      335 | 
      336 |         for n := 1; n < 10; n++ {
      337 |                 echoOut <- "hello "
      338 |                 s := <-echoIn
      339 |                 fmt.Println(s, n)
      340 |         }
      341 |         close(echoOut)
      342 |         os.Exit(0)
      343 | }
      344 | 
      345 | func checkError(err error) {
      346 |         if err != nil {
      347 |                 fmt.Println("Fatal error ", err.Error())
      348 |                 os.Exit(1)
      349 |         }
      350 | }
      351 | 
      352 | 353 |

      Conclusion

      354 |

      总结

      355 | 356 |

      Network channels are a distributed analogue of local channels. They behave approximately the same, but due to limitations of the model some things have to be done a little differently.

      357 |

      网络channel是本地channel的分布式模拟。它们表现的近乎相同,但是由于模型的限制,存在一些不同。

      358 | 359 |

      Copyright Jan Newmarch, jan@newmarch.name

      360 |

      版权所有 Jan Newmarch, jan@newmarch.name

      361 | 362 |

      If you like this book, please contribute using Flattr
      363 | or donate using PayPal

      364 |

      如果你喜欢这本书,请通过Flattr支持
      365 | 或是使用Paypal捐助,

      366 | 367 |
      368 | 369 |
      370 | 371 | 372 | -------------------------------------------------------------------------------- /zh/Text/chapter-golang.html: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | Network Channels 8 | 9 | 18 | 33 | 41 | 42 | 43 | 44 |
      45 |

      Overview of the Go language

      46 |

      Go语言概括

      47 |
      48 | 49 |
      50 | 51 |
      52 | 53 |

      Introduction

      54 |

      介绍

      55 | 56 |

      Please go to the main index for the content pages for network computing.

      57 |

      请访问导航页获取Go网络编程的其他页面

      58 | 59 |

      I don't feel like writing a chapter introducing Go right now, as there are other materials already available. There are several tutorials on the Go web site:

      60 |

      我目前还不想写介绍Go的章节,因为目前已经有很多这方面的材料存在,在Go官方网站上有很多这样的入门:

      61 | 62 | 69 | 70 | 77 | 78 |

      There is an introductory textbook on Go: "Go Programming" by John P. Baugh available from Amazon

      79 | 80 |

      目前有一本介绍Go的书: "Go Programming" 作者 John P. Baugh Amazon

      81 | 82 |

      There is a #golang group on Google+

      83 |

      Google+上有一个讨论组:#golang

      84 | 85 |

      Copyright Jan Newmarch, jan@newmarch.name

      86 | 87 |

      If you like this book, please contribute using Flattr
      88 | or donate using PayPal

      89 | 90 |
      91 | 92 |
      93 | 94 | 95 | -------------------------------------------------------------------------------- /zh/Text/chapter-html.html: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | HTML 8 | 9 | 18 | 33 | 41 | 42 | 43 | 44 |
      45 |

      HTML

      46 | 47 |

      关于HTML

      48 |
      49 | 50 |
      51 |

      The Web Server was originally created to serve HTML documents. Now it is used to serve all sorts of documents as well as data of different kinds. Nevertheless, HTML is still the main document type delivered over the Web. Go has basic mechanisms for parsing HTML documents, which are covered in this chapter

      52 |

      Web服务器的建立最开始是用来提供HTML文件服务的。现在它能为各种类型的文档和各种不同类型的数据提供服务。然而,HTML依然是互联网网络中传递的主要文档类型。Go有一套基本机制来解析HTML,本章主要阐述此内容。

      53 |
      54 | 55 |
      56 | 57 |

      Introduction

      58 | 59 |

      介绍

      60 | 61 |

      The Web was originally created to serve HTML documents. Now it is used to serve all sorts of documents as well as data of dirrent kinds. Nevertheless, HTML is still the main document type delivered over the Web

      62 | 63 |

      Web服务器的建立最开始是用来提供HTML文件服务的。现在它为各种类型的文档和各种不同类型的数据提供服务。然而,HTML仍然是互联网网络中传递的主要文档类型。

      64 | 65 |

      HTML has been through a large number of versions, and HTML 5 is currently under development. There have also been many "vendor" versions of HTML, introducing tags that never made it into standards.

      66 | 67 |

      HTML经历了大量的版本变迁,HTML5目前还在开发阶段。此外出现不少“独立供应商”版的HTML,但引入的标签从来没有做成标准。

      68 | 69 |

      HTML is simple enough to be edited by hand. Consequently, many HTML documents are "ill formed", not following the syntax of the language. HTML parsers generally are not very strict, and will accept many "illegal" documents.

      70 | 71 |

      HTML足够简单,以至于可以纯手工编写。因此,许多HTML文件格式不规范,没有遵守标准准则的语法。HTML解析器通常也不是很严格,而且能接受大多数格式“不严格”的文件。 72 | 73 |

      74 | 75 |

      There wasn't much in earlier versions of Go about handling HTML documents - basically, just a tokenizer. The incomplete nature of the package has led to its removal for Go 1. It can still be found in the exp (experimental) package if you really need it. No doubt some improved form will become available in a later version of Go, and then it will be added back into this book.

      76 | 77 |

      在早期版本的Go没有太多关于处理HTML文件的细节--基本上只是一个分词器。不完整的原始包在Go 1的版本中已移除。如果你真的需要它,仍然可以在exp(试验)包中找到它。 毫无疑问,Go未来版本在这方面会有一些改进的地方,那么到时将会添加到本书中。 78 | 79 |

      80 | 81 |

      There is limited support for HTML in the XML package, discussed in the next chapter.

      82 | 83 |

      在XML包中对HTML的支持是有限的,在下一章将会讨论。

      84 | 163 | 164 |

      Conclusion

      165 | 166 |

      结论

      167 | 168 |

      There isn't anything to this package at present as it is still under development.

      169 | 170 |

      目前这个包没有内容,因为它目前仍处于开发阶段。

      171 | 172 |

      Copyright Jan Newmarch, jan@newmarch.name

      173 | 174 |

      If you like this book, please contribute using Flattr
      175 | or donate using PayPal

      176 | 177 |
      178 | 179 |
      180 | 181 | 182 | -------------------------------------------------------------------------------- /zh/Text/chapter-rpc.html: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | RPC 8 | 9 | 18 | 33 | 49 | 50 | 51 | 52 |
      53 |

      Remote Procedure Call

      54 |

      远程过程调用

      55 |

      56 | 57 |
      58 | 59 |
      60 | 61 |

      Introduction

      62 |

      介绍

      63 |

      Socket and HTTP programming use a message-passing paradigm. A client sends a message to a server which usually sends a message back. Both sides are responsible for creating messages in a format understood by both sides, and in reading the data out of those messages.

      64 |

      Socket 和 HTTP 编程 使用的是一种消息传递模式. 一个客户端发送了一个消息给服务器,通常会等回一个响应消息。两边都要创建出一种双方可理解的格式,然后从里面读出数据的实体。

      65 | 66 |

      However, most standalone applications do not make so much use of message passing techniques. Generally the preferred mechanism is that of the function (or method or procedure) call. In this style, a program will call a function with a list of parameters, and on completion of the function call will have a set of return values. These values may be the function value, or if addresses have been passed as parameters then the contents of those addresses might have been changed.

      67 |

      然而,大多数独立主机应用不会做太多的消息传递技术。一般来说,函数调用(或者被称作method/procedure)的使用更为普遍。在函数风格下,程序会调用函数时会传入一系列参数,然后函数调用完毕后会返回一系列返回值。这些返回值会成为函数的值,或者传递进函数的是参数的地址引用,那么参数值可能最后会被修改。

      68 |

      The remote procedure call is an attempt to bring this style of programming into the network world. Thus a client will make what looks to it like a normal procedure call. The client-side will package this into a network message and transfer it to the server. The server will unpack this and turn it back into a procedure call on the server side. The results of this call will be packaged up for return to the client.

      69 |

      远程过程调用的初衷就是把这种风格带入网络世界。客户调用时候会让这一切看起来像是函数调用,而客户端会打包这些数据成为消息,然后传递到远端服务器。服务器再拆解包,然后把它变成在服务器端的过程调用,而最后的返回结果会被打包传回给客户。

      70 | 71 |

      Diagrammatically it looks like
      72 |
      73 | where the steps are

      74 | 75 |

      用图示表示的话,看起来就会是这个样子
      76 |
      77 | 经历如下几个步骤

      78 | 79 |
        80 |
      1. The client calls the local stub procedure. The stub packages up the parameters into a network message. This is called marshalling.
      2. 81 | 82 |
      3. Networking functions in the O/S kernel are called by the stub to send the message.
      4. 83 | 84 |
      5. The kernel sends the message(s) to the remote system. This may be connection-oriented or connectionless.
      6. 85 | 86 |
      7. A server stub unmarshals the arguments from the network message.
      8. 87 | 88 |
      9. The server stub executes a local procedure call.
      10. 89 | 90 |
      11. The procedure completes, returning execution to the server stub.
      12. 91 | 92 |
      13. The server stub marshals the return values into a network message.
      14. 93 | 94 |
      15. The return messages are sent back.
      16. 95 | 96 |
      17. The client stub reads the messages using the network functions.
      18. 97 | 98 |
      19. The message is unmarshalled. and the return values are set on the stack for the local process.
      20. 99 |
      100 |
        101 |
      1. 客户调用本地存根节点过程, 存根节点会把参数打包成网络消息,这个过程被称为编组
      2. 102 | 103 |
      3. OS内核里的网络通信函数会被存根节点调用来发送消息。
      4. 104 | 105 |
      5. 内核把消息传递给远端系统。这个可以使面向连接的或者是无连接传输模式。
      6. 106 | 107 |
      7. 服务器端的存根节点会把参数从网络消息中拆解出来。
      8. 108 | 109 |
      9. 服务器端的存根节点会执行一个本地过程调用
      10. 110 | 111 |
      11. 等到过程完成,返回之行结果给服务器端的存根节点。
      12. 112 | 113 |
      13. 服务器存根节点会把返回值编组成网络消息。
      14. 114 | 115 |
      15. 消息被返回
      16. 116 | 117 |
      17. 客户端存根节点用网络通信函数读取消息
      18. 118 | 119 |
      19. 消息被拆解。然后返回值被放到本地程序的堆栈内。
      20. 120 |
      121 | 122 | 123 |

      There are two common styles for implementing RPC. The first is typified by Sun's RPC/ONC and by CORBA. In this, a specification of the service is given in some abstract language such as CORBA IDL (interface definition language). This is then compiled into code for the client and for the server. The client then writes a normal program containing calls to a procedure/function/method which is linked to the generated client-side code. The server-side code is actually a server itself, which is linked to the procedure implementation that you write.

      124 |

      远程过程调用有两种普遍使用的风格。第一个是以SUN开发的CORBA的RPC/ONC为代表。这里,服务的描述被某种像CORBA IDL(接口定义语言)抽象语言提供,然后编译成可执行代码分别部署在client端和server端。客户接着就可以写一个常规的程序去连接那个生成出来的方法,而server端的代码实际上就是server服务的实体,然后连接到你实现的程序。 125 | 126 |

      In this way, the client-side code is almost identical in appearance to a normal procedure call. Generally there is a little extra code to locate the server. In Sun's ONC, the address of the server must be known; in CORBA a naming service is called to find the address of the server; In Java RMI, the IDL is Java itself and a naming service is used to find the address of the service.

      127 |

      这样,客户端代码就基本上跟一个普通的程序调用没什么区别了。一般来说,在server端部署的代码量会有点多。在SUN开发的的ONC上,server端的地址必须是公开的。在CORBA里面,一个命名服务会启动去寻找服务器的地址。而在JAVA RMI中,IDL由Java类库实现,然后命名服务会被调用去寻找服务器地址。

      128 | 129 |

      In the second style, you have to make use of a special client API. You hand the function name and its parameters to this library on the client side. On the server side, you have to explicitly write the server yourself, as well as the remote procedure implementation.

      130 |

      在第二种风格中,你会用到一些特别的client端API,这些API,包括函数名,和参数是在生成的client代码中的。与此不同的是,在server端,你必须用你的手把代码敲出来,包括这些远程函数的实现。

      131 | 132 |

      This approach is used by many RPC systems, such as Web Services. It is also the approach used by Go's RPC.

      133 |

      很多RPC系统都采用了这种方法,比如Web Services. 当然,Go的PRC也采用了这样的方法。 134 | 135 |

      Go RPC

      136 |

      Go RPC

      137 | 138 |

      Go's RPC is so far unique to Go. It is different to the other RPC systems, so a Go client will only talk to a Go server. It uses the Gob serialisation system discussed in chapter X, which defines the data types which can be used.

      139 |

      Go的RPC是非常独特的。它与别的RPC系统不同,所以Go的client只能跟Go的server对话。它被用在第十章讨论的Gob序列化系统里面,用来定义可被使用的数据类型。

      140 | 141 |

      RPC systems generally make some restrictions on the functions that can be called across the network. This is so that the RPC system can properly determine what are value arguments to be sent, what are reference arguments to receive answers, and how to signal errors.

      142 |

      RPC系统一般来说是对远程的函数调用的一些限定。这也就是为什么RPC系统可以恰当地决定哪些参数要被传递,哪些引用参数来接受数据,以及如何做错误警报。

      143 | 144 |
        145 |
      • the function must be public (begin with a capital letter);
      • 146 | 147 |
      • have exactly two arguments, the first is a pointer to value data to be received by the function from the client, and the second is a pointer to hold the answers to be returned to the client; and
      • 148 | 149 |
      • have a return value of type os.Error
      • 150 |
      151 |
        152 |
      • 函数必须是公共的(也就是首字母大写)
      • 153 | 154 |
      • 有且仅有2个指针参数,第一个指向 “接收器”——接受从client端发过来的数据值,第二个指向 "发送器"——存放向client端发送的返回值。
      • 155 | 156 |
      • 有一个os.Error类型返回值
      • 157 |
      158 | 159 |

      For example, a valid function is

      160 |

      比方说,一个合法的函数应该是如下这样的

      161 |
      
      162 |       F(&T1, &T2) os.Error
      163 |     
      164 | 165 |

      The restriction on arguments means that you typically have to define a structure type. Go's RPC uses the gob package for marshalling and unmarshalling data, so the argument types have to follow the rules of gob as discussed in an earlier chapter.

      166 |

      所谓的对参数的限定指的是你只需要定义数据类型。Go的RPC会用gob 包来编组和解编组数据,所以对于参数类型,你只需要按照之前讨论过的gob的规则定义就可以。

      167 | 168 | 169 |

      We shall follow the example given in the Go documentation, as this illustrates the important points. The server performs two operations which are trivial - they do not require the "grunt" of RPC, but are simple to understand. The two operations are to multiply two integers, and the second is to find the quotient and remainder after dividing the first by the second.

      170 |

      我们应该参考Go的官方文档的例子,因为这些例子展示了一些关键点。Server端执行2种操作,这些操作看起来非常浅显易懂,这里没用RPC的那些难懂的细节,而是非常易于理解。 第一种操作是两个整数相乘,第二个则是第一个数字除以第二个数字然后求商取余。

      171 | 172 |

      The two values to be manipulated are given in a structure:

      173 |

      2个操作数被放在了一个结构体里:

      174 |
      
      175 | type Values struct {
      176 |     X, Y int
      177 | }
      178 |     
      179 | 180 |

      The sum is just an int, while the quotient/remainder is another structure

      181 |

      两数之和是一个 int, 而商数和余数则在另一个结构体里

      182 |
      
      183 | type Quotient struct {
      184 |     Quo, Rem int
      185 | }
      186 |     
      187 | 188 |

      We will have two functions, multiply and divide to be callable on the RPC server. These functions will need to be registered with the RPC system. The function Register takes a single parameter, which is an interface. So we need a type with these two functions:

      189 |

      我们会把这两个程序,也就是乘法和除法, 部署在RPC的server端等待调用。这些函数过会儿会被注册到RPC系统里去。函数Register会带一个interface类型的参数。 所以我们要给这两个函数定义一个类型。

      190 |
      
      191 | type Arith int
      192 | 
      193 | func (t *Arith) Multiply(args *Args, reply *int) os.Error {
      194 |         *reply = args.A * args.B
      195 |         return nil
      196 | }
      197 | 
      198 | func (t *Arith) Divide(args *Args, quo *Quotient) os.Error {
      199 |         if args.B == 0 {
      200 |                 return os.ErrorString("divide by zero")
      201 |         }
      202 |         quo.Quo = args.A / args.B
      203 |         quo.Rem = args.A % args.B
      204 |         return nil
      205 | }
      206 |     
      207 | 208 |

      The underlying type of Arith is given as int. That doesn't matter - any type could have done.

      209 |

      Arith背后的实际类型是 int. 这不要紧 - 任何类型都可以。

      210 | 211 |

      An object of this type can now be registered using Register, and then its methods can be called by the RPC system.

      212 |

      这个类型的对象现在可以用Register函数来注册, 之后,RPC系统就可以调用这个方法了。

      213 | 214 |

      HTTP RPC Server

      215 |

      HTTP RPC 服务器

      216 | 217 |

      Any RPC needs a transport mechanism to get messages across the network. Go can use HTTP or TCP. The advantage of the HTTP mechanism is that it can leverage off the HTTP suport library. You need to add an RPC handler to the HTTP layer which is done using HandleHTTP and then start an HTTP server. The complete code is

      218 |

      任何RPC系统都需要一个传输机制来跨网络地传递消息。Go可以用HTTP或TCP。用HTTP机制的优势就是可以借助HTTP来支持库文件。 你需要通过HandleHTTP在HTTP层上加一个RPC处理器, 然后启动一个HTTP 服务器。完整的代码是这样

      219 |
      
      220 | /**
      221 | * ArithServer
      222 |  */
      223 | 
      224 | package main
      225 | 
      226 | import (
      227 |         "fmt"
      228 |         "net/rpc"
      229 |         "errors"
      230 |         "net/http"
      231 | )
      232 | 
      233 | type Args struct {
      234 |         A, B int
      235 | }
      236 | 
      237 | type Quotient struct {
      238 |         Quo, Rem int
      239 | }
      240 | 
      241 | type Arith int
      242 | 
      243 | func (t *Arith) Multiply(args *Args, reply *int) error {
      244 |         *reply = args.A * args.B
      245 |         return nil
      246 | }
      247 | 
      248 | func (t *Arith) Divide(args *Args, quo *Quotient) error {
      249 |         if args.B == 0 {
      250 |                 return errors.New("divide by zero")
      251 |         }
      252 |         quo.Quo = args.A / args.B
      253 |         quo.Rem = args.A % args.B
      254 |         return nil
      255 | }
      256 | 
      257 | func main() {
      258 | 
      259 |         arith := new(Arith)
      260 |         rpc.Register(arith)
      261 |         rpc.HandleHTTP()
      262 | 
      263 |         err := http.ListenAndServe(":1234", nil)
      264 |         if err != nil {
      265 |                 fmt.Println(err.Error())
      266 |         }
      267 | }
      268 | 
      269 | 270 |

      HTTP RPC client

      271 |

      HTTP RPC 客户端

      272 | 273 |

      The client needs to set up an HTTP connection to the RPC server. It needs to prepare a structure with the values to be sent, and the address of a variable to store the results in. Then it can make a Call with arguments:

      274 |

      客户端需要设置一个HTTP连接,来连接RPC服务器。客户端需要发起一个对RPC服务器的连接。它需要准备一个包含要发送数据的结构体, 以及一个接受返回值的变量地址。之后,它就可以用参数来 调用了,参数如下

      275 | 276 |
        277 |
      • The name of the remote function to execute
      • 278 | 279 |
      • The values to be sent
      • 280 | 281 |
      • The address of a variable to store the result in
      • 282 |
      283 |
        284 |
      • 想要调用的远程函数的名字
      • 285 | 286 |
      • 被发送的数据结构体
      • 287 | 288 |
      • 储存返回值的变量地址
      • 289 |
      290 | 291 |

      A client that calls both functions of the arithmetic server is

      292 |

      一个调用在远端服务器上的这两个计算函数的客户端是这样的

      293 |
      
      294 | /**
      295 | * ArithClient
      296 |  */
      297 | 
      298 | package main
      299 | 
      300 | import (
      301 |         "net/rpc"
      302 |         "fmt"
      303 |         "log"
      304 |         "os"
      305 | )
      306 | 
      307 | type Args struct {
      308 |         A, B int
      309 | }
      310 | 
      311 | type Quotient struct {
      312 |         Quo, Rem int
      313 | }
      314 | 
      315 | func main() {
      316 |         if len(os.Args) != 2 {
      317 |                 fmt.Println("Usage: ", os.Args[0], "server")
      318 |                 os.Exit(1)
      319 |         }
      320 |         serverAddress := os.Args[1]
      321 | 
      322 |         client, err := rpc.DialHTTP("tcp", serverAddress+":1234")
      323 |         if err != nil {
      324 |                 log.Fatal("dialing:", err)
      325 |         }
      326 |         // Synchronous call
      327 |  args := Args{17, 8}
      328 |         var reply int
      329 |         err = client.Call("Arith.Multiply", args, &reply)
      330 |         if err != nil {
      331 |                 log.Fatal("arith error:", err)
      332 |         }
      333 |         fmt.Printf("Arith: %d*%d=%d\n", args.A, args.B, reply)
      334 | 
      335 |         var quot Quotient
      336 |         err = client.Call("Arith.Divide", args, ")
      337 |         if err != nil {
      338 |                 log.Fatal("arith error:", err)
      339 |         }
      340 |         fmt.Printf("Arith: %d/%d=%d remainder %d\n", args.A, args.B, quot.Quo, quot.Rem)
      341 | 
      342 | }
      343 | 
      344 | 345 |

      TCP RPC server

      346 |

      TCP RPC 服务端

      347 | 348 |

      A version of the server that uses TCP sockets is

      349 |

      一个使用TCP socket的服务器是这样的

      350 | 351 |
      
      352 | /**
      353 | * TCPArithServer
      354 |  */
      355 | 
      356 | package main
      357 | 
      358 | import (
      359 |         "fmt"
      360 |         "net/rpc"
      361 |         "errors"
      362 |         "net"
      363 |         "os"
      364 | )
      365 | 
      366 | type Args struct {
      367 |         A, B int
      368 | }
      369 | 
      370 | type Quotient struct {
      371 |         Quo, Rem int
      372 | }
      373 | 
      374 | type Arith int
      375 | 
      376 | func (t *Arith) Multiply(args *Args, reply *int) error {
      377 |         *reply = args.A * args.B
      378 |         return nil
      379 | }
      380 | 
      381 | func (t *Arith) Divide(args *Args, quo *Quotient) error {
      382 |         if args.B == 0 {
      383 |                 return errors.New("divide by zero")
      384 |         }
      385 |         quo.Quo = args.A / args.B
      386 |         quo.Rem = args.A % args.B
      387 |         return nil
      388 | }
      389 | 
      390 | func main() {
      391 | 
      392 |         arith := new(Arith)
      393 |         rpc.Register(arith)
      394 | 
      395 |         tcpAddr, err := net.ResolveTCPAddr("tcp", ":1234")
      396 |         checkError(err)
      397 | 
      398 |         listener, err := net.ListenTCP("tcp", tcpAddr)
      399 |         checkError(err)
      400 | 
      401 |         /* This works:
      402 |         rpc.Accept(listener)
      403 |         */
      404 |  /* and so does this:
      405 |          */
      406 |  for {
      407 |                 conn, err := listener.Accept()
      408 |                 if err != nil {
      409 |                         continue
      410 |                 }
      411 |                 rpc.ServeConn(conn)
      412 |         }
      413 | 
      414 | }
      415 | 
      416 | func checkError(err error) {
      417 |         if err != nil {
      418 |                 fmt.Println("Fatal error ", err.Error())
      419 |                 os.Exit(1)
      420 |         }
      421 | }
      422 | 
      423 | 424 |

      Note that the call to Accept is blocking, and just handles client connections. If the server wishes to do other work as well, it should call this in a goroutine.

      425 |

      留心一点,对于Accept的调用是阻塞式的,用来处理客户端连接。如果服务端希望也做点别的事情,那么就应该在goroutine中调用它。

      426 | 427 |

      TCP RPC client

      428 |

      TCP RPC 客户端

      429 | 430 |

      A client that uses the TCP server and calls both functions of the arithmetic server is

      431 |

      一个使用TCP连接,调用在远端计算服务器的两个函数的客户端是这样的。

      432 |
      
      433 | /**
      434 | * TCPArithClient
      435 |  */
      436 | 
      437 | package main
      438 | 
      439 | import (
      440 |         "net/rpc"
      441 |         "fmt"
      442 |         "log"
      443 |         "os"
      444 | )
      445 | 
      446 | type Args struct {
      447 |         A, B int
      448 | }
      449 | 
      450 | type Quotient struct {
      451 |         Quo, Rem int
      452 | }
      453 | 
      454 | func main() {
      455 |         if len(os.Args) != 2 {
      456 |                 fmt.Println("Usage: ", os.Args[0], "server:port")
      457 |                 os.Exit(1)
      458 |         }
      459 |         service := os.Args[1]
      460 | 
      461 |         client, err := rpc.Dial("tcp", service)
      462 |         if err != nil {
      463 |                 log.Fatal("dialing:", err)
      464 |         }
      465 |         // Synchronous call
      466 |  args := Args{17, 8}
      467 |         var reply int
      468 |         err = client.Call("Arith.Multiply", args, &reply)
      469 |         if err != nil {
      470 |                 log.Fatal("arith error:", err)
      471 |         }
      472 |         fmt.Printf("Arith: %d*%d=%d\n", args.A, args.B, reply)
      473 | 
      474 |         var quot Quotient
      475 |         err = client.Call("Arith.Divide", args, ")
      476 |         if err != nil {
      477 |                 log.Fatal("arith error:", err)
      478 |         }
      479 |         fmt.Printf("Arith: %d/%d=%d remainder %d\n", args.A, args.B, quot.Quo, quot.Rem)
      480 | 
      481 | }
      482 | 
      483 | 484 |

      Matching values

      485 |

      数据值匹配

      486 | 487 |

      We note that the types of the value arguments are not the same on the client and server. In the server, we have used Values while in the client we used Args. That doesn't matter, as we are following the rules of gob serialisation, and the names an types of the two structures' fields match. Better programming practise would say that the names should be the same!*

      488 |

      我们注意到在server端和client端的数据类型并不相同。在服务器端,我们用的是Values 而在客户端我们用了 Args。这并不成问题,因为我们按照了gob串行化规则,而且在两个结构体字段中的名称能匹配。但更好的编程实践却告诉我们,名字也应该相同。*

      489 | 490 |

      However, this does point out a possible trap in using Go RPC. If we change the structure in the client to be, say,

      491 |

      然而,这指出了go中可能存在的陷阱的可能性。要是我们改变了client端的结构体,比方说,

      492 |
      
      493 | type Values struct {
      494 |         C, B int
      495 | }
      496 |     
      497 | 498 |

      then gob has no problems: on the server-side the unmarshalling will ignore the value of C given by the client, and use the default zero value for A.

      499 |

      而这对于 gob 来说却没有什么疑问: 在server端解编组的时候会忽略来自client的C,然后将默认值零值赋给A.

      500 | 501 |

      Using Go RPC will require a rigid enforcement of the stability of field names and types by the programmer. We note that there is no version control mechanism to do this, and no mechanism in gob to signal any possible mismatches.

      502 |

      用Go RPC会要求对字段名称和类型的一致性都进行严格加强。我们注意到,没有任何的版本控制机制或是gob本身,都没有任何提示数据不匹配的保护机制。

      503 | 504 |

      JSON

      505 |

      JSON

      506 | 507 |

      This section adds nothing new to the earlier concepts. It just uses a different "wire" format for the data, JSON instead of gob. As such, clients or servers could be written in other language that understand sockets and JSON.

      508 |

      这部分每增加什么新的概念。只是用了另一种数据的 "电报" 格式, 用JSON来代替gob。由于这样做了,那么client端和server端要用另一种语言来来理解socket和JSON。

      509 | 510 |

      JSON RPC client

      511 |

      JSON RPC 客户端

      512 | 513 |

      A client that calls both functions of the arithmetic server is

      514 |

      客户端调用计算服务器的两个函数如下

      515 |
      
      516 | /* JSONArithCLient
      517 |  */
      518 | 
      519 | package main
      520 | 
      521 | import (
      522 |         "net/rpc/jsonrpc"
      523 |         "fmt"
      524 |         "log"
      525 |         "os"
      526 | )
      527 | 
      528 | type Args struct {
      529 |         A, B int
      530 | }
      531 | 
      532 | type Quotient struct {
      533 |         Quo, Rem int
      534 | }
      535 | 
      536 | func main() {
      537 |         if len(os.Args) != 2 {
      538 |                 fmt.Println("Usage: ", os.Args[0], "server:port")
      539 |                 log.Fatal(1)
      540 |         }
      541 |         service := os.Args[1]
      542 | 
      543 |         client, err := jsonrpc.Dial("tcp", service)
      544 |         if err != nil {
      545 |                 log.Fatal("dialing:", err)
      546 |         }
      547 |         // Synchronous call
      548 |  args := Args{17, 8}
      549 |         var reply int
      550 |         err = client.Call("Arith.Multiply", args, &reply)
      551 |         if err != nil {
      552 |                 log.Fatal("arith error:", err)
      553 |         }
      554 |         fmt.Printf("Arith: %d*%d=%d\n", args.A, args.B, reply)
      555 | 
      556 |         var quot Quotient
      557 |         err = client.Call("Arith.Divide", args, ")
      558 |         if err != nil {
      559 |                 log.Fatal("arith error:", err)
      560 |         }
      561 |         fmt.Printf("Arith: %d/%d=%d remainder %d\n", args.A, args.B, quot.Quo, quot.Rem)
      562 | 
      563 | }
      564 | 
      565 | 566 |

      JSON RPC server

      567 |

      JSON RPC 服务器

      568 | 569 |

      A version of the server that uses JSON encoding is

      570 |

      JSON版的服务器代码如下

      571 |
      
      572 | /* JSONArithServer
      573 |  */
      574 | 
      575 | package main
      576 | 
      577 | import (
      578 |         "fmt"
      579 |         "net/rpc"
      580 |         "net/rpc/jsonrpc"
      581 |         "os"
      582 |         "net"
      583 |         "errors"
      584 | )
      585 | //import ("fmt"; "rpc"; "os"; "net"; "log"; "http")
      586 | 
      587 | type Args struct {
      588 |         A, B int
      589 | }
      590 | 
      591 | type Quotient struct {
      592 |         Quo, Rem int
      593 | }
      594 | 
      595 | type Arith int
      596 | 
      597 | func (t *Arith) Multiply(args *Args, reply *int) error {
      598 |         *reply = args.A * args.B
      599 |         return nil
      600 | }
      601 | 
      602 | func (t *Arith) Divide(args *Args, quo *Quotient) error {
      603 |         if args.B == 0 {
      604 |                 return errors.New("divide by zero")
      605 |         }
      606 |         quo.Quo = args.A / args.B
      607 |         quo.Rem = args.A % args.B
      608 |         return nil
      609 | }
      610 | 
      611 | func main() {
      612 | 
      613 |         arith := new(Arith)
      614 |         rpc.Register(arith)
      615 | 
      616 |         tcpAddr, err := net.ResolveTCPAddr("tcp", ":1234")
      617 |         checkError(err)
      618 | 
      619 |         listener, err := net.ListenTCP("tcp", tcpAddr)
      620 |         checkError(err)
      621 | 
      622 |         /* This works:
      623 |         rpc.Accept(listener)
      624 |         */
      625 |  /* and so does this:
      626 |          */
      627 |  for {
      628 |                 conn, err := listener.Accept()
      629 |                 if err != nil {
      630 |                         continue
      631 |                 }
      632 |                 jsonrpc.ServeConn(conn)
      633 |         }
      634 | 
      635 | }
      636 | 
      637 | func checkError(err error) {
      638 |         if err != nil {
      639 |                 fmt.Println("Fatal error ", err.Error())
      640 |                 os.Exit(1)
      641 |         }
      642 | }
      643 | 
      644 | 645 |

      Conclusion

      646 |

      总结

      647 | 648 |

      RPC is a popular means of distributing applications. Several ways of doing it have been presented here. What is missing from Go is support for the currently fashionable (but extremely badly enginereed) SOAP RPC mechanism.

      649 |

      RPC 是一个流行的分布应用的方法。这里展示了许多实现它的方法。Go所不支持的实现下很火的(却也是实现地很不好的) SOAP RPC 机制。

      650 | 651 |

      Copyright Jan Newmarch, jan@newmarch.name

      652 |

      版权属于 Jan Newmarch, jan@newmarch.name

      653 | 654 |

      If you like this book, please contribute using Flattr
      655 | or donate using PayPal

      656 |

      如果你喜欢这本书,请用我Flattr支持我
      657 | 或者用PayPal捐助我。

      658 | 659 |
      660 | 661 |
      662 | 663 | -------------------------------------------------------------------------------- /zh/Text/chapter-template.html: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | Templates 8 | 9 | 24 | 39 | 40 | 41 | 42 |
      43 |

      Templates

      44 | 45 |

      模板

      46 | 47 | 48 |
      49 | 50 |
      51 |

      Many languages have mechanisms to convert strings from one form to another. Go has a template mechanism to convert strings based on the content of an object supplied as an argument. While this is often used in rewriting HTML to insert object values, it can be used in other situations. Note that this material doesn't have anything explicitly to do with networking, but may be useful to network programs.

      52 | 53 |

      很多编程语言都有字符串之间转换的机制,而GO语言则是通过模板来将一个对象的内容来作为参数传递从而字符串的转换。此方式不仅可以在重写HTML时插入对象值,也适用于其他方面。注意,本章内容并没有明确给出网络的工作方式,但对于网络编程方式很有用处。

      54 | 55 |
      56 | 57 |

      Introduction

      58 | 59 |

      介绍

      60 | 61 |

      Most server-side languages have a mechanism for taking predominantly static pages and inserting a dynamically generated component, such as a list of items. Typical examples are scripts in Java Server Pages, PHP scripting and many others. Go has adopted a relatively simple scripting language in the template package.

      62 | 63 |

      大多数服务器端语言的机制主要是在静态页面插入一个动态生成的组件,如清单列表项目。典型的例子是在JSP、PHP和许多其他语言的脚本中。GO的template包中采取了相对简单的脚本化语言。

      64 | 65 |

      At the time of writing a new template package has been adopted. There is very little documentation on the template packages. There is a small amount on the old package, which is currently still available in the old/template. There is no documentation on the new package as yet apart from the reference page. The template package changed with r60 (released 2011/09/07).

      66 | 67 |

      因为新的template包是刚刚被采用的,所有现在的template包中的文档少的可怜,旧的old/template包中也还存有少量的旧模板。新发布的帮助页面还没有关于新包的文档。关于template包的更改请参阅r60 (released 2011/09/07).

      68 | 69 |

      We describe the new package here. The package is designed to take text as input and output different text, based on transforming the original text using the values of an object. Unlike JSP or similar, it is not restricted to HTML files but it is likely to find greatest use there.

      70 | 71 |

      在这里,我们描述了这个新包。该包是描述了通过使用对象值改变了原来文本的方式从而在输入和输出时获取不同的文本。与JSP或类似的不同,它的作用不仅限于HTML文件,但在那可能会有更大的作用。

      72 | 73 | 74 | 75 |

      The original source is called a template and will consist of text that is transmitted unchanged, and embedded commands which can act on and change text. The commands are delimited by {{ ... }} , similar to the JSP commands <%= ... =%> and PHPs <?php ... ?>.

      76 | 77 |

      源文件被称作 template ,包括文本传输方式不变,以嵌入命令可以作用于和更改文本。命令规定如 {{ ... }} ,类似于JSP命令 <%= ... =%> 和PHP命令 <?php ... ?>

      78 | 79 |

      Inserting object values

      80 | 81 |

      插入对象值

      82 | 83 |

      A template is applied to a Go object. Fields from that Go object can be inserted into the template, and you can 'dig" into the object to find subfields, etc. The current object is represented as '.', so that to insert the value of the current object as a string, you use {{.}}. The package uses the fmt package by default to work out the string used as inserted values.

      84 | 85 |

      模板应用于GO对象中.GO对象的字段被插入到模板后,你就能从域中“挖”到他的子域,等等。当前对象以'.'代替, 所以把当前对象当做字符串插入时,你可以采用{{.}}的方式。这个包默认采用 fmt 包来作为插入值的字符串输出。

      86 | 87 |

      To insert the value of a field of the current object, you use the field name prefixed by '.'. For example, if the object is of type

      88 | 89 |

      要插入当前对象的一个字段的值,你使用的字段名前加前缀 '.'。 例如, 如果要插入的对象的类型为

      90 | 91 |
       92 | 
       93 | type Person struct {
       94 |         Name      string
       95 |         Age       int
       96 |         Emails     []string
       97 |         Jobs       []*Jobs
       98 | }
       99 | 
      100 | 
      101 | 102 |

      then you insert the values of Name and Age by

      103 | 104 |

      那么你要插入的字段 NameAge 如下

      105 | 106 |
      107 | 
      108 | The name is {{.Name}}.
      109 | The age is {{.Age}}.
      110 | 
      111 | 
      112 | 113 |

      We can loop over the elements of an array or other list using the range command. So to access the contents of the Emails array we do

      114 | 115 |

      我们可以使用range命令来循环一个数组或者链表中的元素。所以要获取 Emails 数组的信息,我们可以这么干

      116 |
      117 | 
      118 | {{range .Emails}}
      119 |         ...
      120 | {{end}}
      121 | 
      122 | 
      123 | 124 |

      if Job is defined by

      125 | 126 |

      如果Job定义为

      127 | 128 |
      129 | 
      130 | type Job struct {
      131 |     Employer string
      132 |     Role     string
      133 | }
      134 | 
      135 | 
      136 | 137 |

      and we want to access the fields of a Person's Jobs, we can do it as above with a {{range .Jobs}}. An alternative is to switch the current object to the Jobs field. This is done using the {{with ...}} ... {{end}} construction, where now {{.}} is the Jobs field, which is an array:

      138 | 139 |

      如果我们想访问 Person字段中的 Jobs, 我们可以这么干 {{range .Jobs}}。这是一种可以将当前对象转化为Jobs 字段的方式. 通过 {{with ...}} ... {{end}} 这种方式, 那么{{.}} 就可以拿到Jobs 字段了,如下:

      140 | 141 |
      142 | 
      143 | {{with .Jobs}}
      144 |     {{range .}}
      145 |         An employer is {{.Employer}}
      146 |         and the role is {{.Role}}
      147 |     {{end}}
      148 | {{end}}
      149 | 
      150 | 
      151 | 152 |

      You can use this with any field, not just an array. Using templates

      153 | 154 |

      你可以用这种方法操作任何类型的字段,而不仅限于数组。亲,用模板吧!

      155 | 156 |

      Once we have a template, we can apply it to an object to generate a new string, using the object to fill in the template values. This is a two-step process which involves parsing the template and then applying it to an object. The result is output to a Writer, as in

      157 | 158 |

      当我们拥有了模板,我们将它应用在对象中生成一个字符串,用这个对象来填充这个模板的值。分两步来实现模块的转化和应用,并且输出一个Writer, 如下

      159 | 160 |
      161 | 
      162 | t := template.New("Person template")
      163 | t, err := t.Parse(templ)
      164 | if err == nil {
      165 |         buff := bytes.NewBufferString("")
      166 |         t.Execute(buff, person)
      167 | }
      168 | 
      169 | 
      170 | 171 |

      An example program to apply a template to an object and print to standard output is

      172 | 173 |

      下面是一个例子来演示模块应用在对象上并且标准输入:

      174 | 175 |
      
      176 | /**
      177 |  * PrintPerson
      178 |  */
      179 | 
      180 | package main
      181 | 
      182 | import (
      183 |         "fmt"
      184 |         "html/template"
      185 |         "os"
      186 | )
      187 | 
      188 | type Person struct {
      189 |         Name   string
      190 |         Age    int
      191 |         Emails []string
      192 |         Jobs   []*Job
      193 | }
      194 | 
      195 | type Job struct {
      196 |         Employer string
      197 |         Role     string
      198 | }
      199 | 
      200 | const templ = `The name is {{.Name}}.
      201 | The age is {{.Age}}.
      202 | {{range .Emails}}
      203 |         An email is {{.}}
      204 | {{end}}
      205 | 
      206 | {{with .Jobs}}
      207 |     {{range .}}
      208 |         An employer is {{.Employer}}
      209 |         and the role is {{.Role}}
      210 |     {{end}}
      211 | {{end}}
      212 | `
      213 | 
      214 | func main() {
      215 |         job1 := Job{Employer: "Monash", Role: "Honorary"}
      216 |         job2 := Job{Employer: "Box Hill", Role: "Head of HE"}
      217 | 
      218 |         person := Person{
      219 |                 Name:   "jan",
      220 |                 Age:    50,
      221 |                 Emails: []string{"jan@newmarch.name", "jan.newmarch@gmail.com"},
      222 |                 Jobs:   []*Job{&job1, &job2},
      223 |         }
      224 | 
      225 |         t := template.New("Person template")
      226 |         t, err := t.Parse(templ)
      227 |         checkError(err)
      228 | 
      229 |         err = t.Execute(os.Stdout, person)
      230 |         checkError(err)
      231 | }
      232 | 
      233 | func checkError(err error) {
      234 |         if err != nil {
      235 |                 fmt.Println("Fatal error ", err.Error())
      236 |                 os.Exit(1)
      237 |         }
      238 | }
      239 | 
      240 | 241 |

      The output from this is

      242 | 243 |

      输出如下:

      244 | 245 |
      246 | 
      247 | The name is jan.
      248 | The age is 50.
      249 | 
      250 |         An email is jan@newmarch.name
      251 | 
      252 |         An email is jan.newmarch@gmail.com
      253 | 
      254 | 
      255 | 
      256 |     
      257 |         An employer is Monash
      258 |         and the role is Honorary
      259 |     
      260 |         An employer is Box Hill
      261 |         and the role is Head of HE
      262 |     
      263 | 
      264 | 
      265 | 266 |

      Note that there is plenty of whitespace as newlines in this printout. This is due to the whitespace we have in our template. If we wish to reduce this, eliminate newlines in the template as in

      267 | 268 |

      注意,上面有很多空白的输出,这是因为我们的模板中有很多空白。如果想消除它, 模板设置如下:

      269 | 270 |
      271 | 
      272 | {{range .Emails}} An email is {{.}} {{end}}
      273 | 
      274 | 
      275 | 276 |

      In the example, we used a string in the program as the template. You can also load templates from a file using the function template.ParseFiles(). For some reason that I don't understand (and which wasn't required in earlier versions), the name assigned to the template must be the same as the basename of the first file in the list of files. Is this a bug?

      277 | 278 |

      在这个示例例中,我们用字符串应用于模板。你同样也可以用方法template.ParseFiles()来从文件中下载模板。因为某些原因,我还不没搞清楚(在早期版本没有强制要求),关联模板的名字必须要与文件列表的第一个文件的基名相同。话说,这个是BUG吗?

      279 | 280 |

      Pipelines

      281 | 282 |

      管道

      283 | 284 |

      The above transformations insert pieces of text into a template. Those pieces of text are essentially arbitrary, whatever the string values of the fields are. If we want them to appear as part of an HTML document (or other specialised form) then we will have to escape particular sequences of characters. For example, to display arbitrary text in an HTML document we have to change "<" to "&lt;". The Go templates have a number of builtin functions, and one of these is the function html. These functions act in a similar manner to Unix pipelines, reading from standard input and writing to standard output.

      285 | 286 |

      上述转换到模板中插入的文本块。这些字符基本上是任意的,是任何字符串的字段值。如果我们希望它们出现的是HTML文档(或其他的特殊形式)的一部分,那么我们将不得不脱离特定的字符序列。例如,要显示任意文本在HTML文档中,我们要将“<”改成“&lt”。GO模板有一些内建函数,其中之一是html。这些函数的作用与Unix的管道类似,从标准输入读取和写入到标准输出。

      287 | 288 |

      To take the value of the current object '.' and apply HTML escapes to it, you write a "pipeline" in the template

      289 | 290 |

      如果想用“.”来获取当前对象值并且应用于HTML转义,你可以在模板里写个“管道”:

      291 | 292 |
      293 | 
      294 | {{. | html}}
      295 | 
      296 | 
      297 | 298 |

      and similarly for other functions.

      299 | 300 |

      其他方法类似。

      301 | 302 |

      Mike Samuel has pointed out a convenience function currently in the exp/template/html package. If all of the entries in a template need to be passed through the html template function, then the Go function Escape(t *template.Template) can take a template and add the html function to each node in the template that doesn't already have one. This will be useful for templates used for HTML documents and can form a pattern for similar function uses elsewhere.

      303 | 304 |

      Mike Samuel指出,目前在exp/template/html 包里有一个方便的方法。如果所有的模板中的条目需要通过html 模板函数,那么Go语言方法 Escape(t *template.Template)就能获取模板而后将html 函数添加到模板中不存在该函数的每个节点中。用于HTML文档的模板是非常有用的,并能在其他使用场合生成相似的方法模式。

      305 | 306 |

      Defining functions

      307 | 308 |

      定义方法

      309 | 310 |

      The templates use the string representation of an object to insert values, using the fmt package to convert the object to a string. Sometimes this isn't what is needed. For example, to avoid spammers getting hold of email addresses it is quite common to see the symbol '@' replaced by the word " at ", as in "jan at newmarch.name". If we want to use a template to display email addresses in that form, then we have to build a custom function to do this transformation.

      311 | 312 |

      模板使用对象化的字符串表示形式插入值,使用fmt包将对象转换为字符串。有时候,这并不是必需。例如,为了避免被垃圾邮件发送者掌握电子邮件地址,常见的方式是把字符号“@”替换为“at”,如“jan at newmarch.name”。如果我们要使用一个模板,显示在该表单中的电子邮件地址,那么我们就必须建立一个自定义的功能做这种转变。

      313 | 314 |

      Each template function has a name that is used in the templates themselves, and an associated Go function. These are linked by the type

      315 | 316 |

      每个模板函数中使用的模板本身有的一个名称,以及相关联的函数。他们用下面方式进行关联如下

      317 | 318 |
      319 | 
      320 | type FuncMap map[string]interface{}
      321 | 
      322 | 
      323 | 324 |

      For example, if we want our template function to be "emailExpand" which is linked to the Go function EmailExpander then we add this to the functions in a template by

      325 | 326 |

      例如,如果我们希望我们的模板函数是“emailExpand”,用来关联到Go函数EmailExpander,然后,我们像这样添加函数到到模板中

      327 | 328 |
      329 | 
      330 | t = t.Funcs(template.FuncMap{"emailExpand": EmailExpander})
      331 | 
      332 | 
      333 | 334 |

      The signature for EmailExpander is typically

      335 | 336 |

      EmailExpander通常像这样标记:

      337 | 338 |
      339 | 
      340 | func EmailExpander(args ...interface{}) string
      341 | 
      342 | 
      343 | 344 |

      In the use we are interested in, there should only be one argument to the function which will be a string. Existing functions in the Go template library have some initial code to handle non-conforming cases, so we just copy that. Then it is just simple string manipulation to change the format of the email address. A program is

      345 | 346 |

      我们感兴趣的是在使用过程中,那是一个只有一个参数的函数,并且是个字符串。在Go模板库的现有功能有初步的代码来处理不符合要求的情况,所以我们只需要复制。然后,它就能通过简单的字符串操作来改变格式的电子邮件地址。程序如

      347 | 348 |
      
      349 | /**
      350 |  * PrintEmails
      351 |  */
      352 | 
      353 | package main
      354 | 
      355 | import (
      356 |         "fmt"
      357 |         "os"
      358 |         "strings"
      359 |         "text/template"
      360 | )
      361 | 
      362 | type Person struct {
      363 |         Name   string
      364 |         Emails []string
      365 | }
      366 | 
      367 | const templ = `The name is {{.Name}}.
      368 | {{range .Emails}}
      369 |         An email is "{{. | emailExpand}}"
      370 | {{end}}
      371 | `
      372 | 
      373 | func EmailExpander(args ...interface{}) string {
      374 |         ok := false
      375 |         var s string
      376 |         if len(args) == 1 {
      377 |                 s, ok = args[0].(string)
      378 |         }
      379 |         if !ok {
      380 |                 s = fmt.Sprint(args...)
      381 |         }
      382 | 
      383 |         // find the @ symbol
      384 |  substrs := strings.Split(s, "@")
      385 |         if len(substrs) != 2 {
      386 |                 return s
      387 |         }
      388 |         // replace the @ by " at "
      389 |  return (substrs[0] + " at " + substrs[1])
      390 | }
      391 | 
      392 | func main() {
      393 |         person := Person{
      394 |                 Name:   "jan",
      395 |                 Emails: []string{"jan@newmarch.name", "jan.newmarch@gmail.com"},
      396 |         }
      397 | 
      398 |         t := template.New("Person template")
      399 | 
      400 |         // add our function
      401 |  t = t.Funcs(template.FuncMap{"emailExpand": EmailExpander})
      402 | 
      403 |         t, err := t.Parse(templ)
      404 | 
      405 |         checkError(err)
      406 | 
      407 |         err = t.Execute(os.Stdout, person)
      408 |         checkError(err)
      409 | }
      410 | 
      411 | func checkError(err error) {
      412 |         if err != nil {
      413 |                 fmt.Println("Fatal error ", err.Error())
      414 |                 os.Exit(1)
      415 |         }
      416 | }
      417 | 
      418 | 419 |

      The output is

      420 | 421 |

      输出为:

      422 |
      423 | 
      424 | The name is jan.
      425 | 
      426 |         An email is "jan at newmarch.name"
      427 | 
      428 |         An email is "jan.newmarch at gmail.com"
      429 | 
      430 | 
      431 | 432 |

      Variables

      433 | 434 |

      变量

      435 | 436 |

      The template package allows you to define and use variables. As motivation for this, consider how we might print each person's email address prefixed by their name. The type we use is again

      437 | 438 |

      template包,允许您定义和使用变量。这样做的动机,可能我们会考虑通过把他们的名字当做电子邮件地址前缀打印出来。我们又使用这个类型

      439 | 440 |
      441 | 
      442 | type Person struct {
      443 |         Name      string
      444 |         Emails     []string
      445 | }
      446 | 
      447 | 
      448 | 449 |

      To access the email strings, we use a range statement such as

      450 | 451 |

      为了访问email的所有字符串, 可以用 range,如下

      452 | 453 |
      454 | 
      455 | {{range .Emails}}
      456 |     {{.}}
      457 | {{end}}
      458 | 
      459 | 
      460 | 461 |

      But at that point we cannot access the Name field as '.' is now traversing the array elements and the Name is outside of this scope. The solution is to save the value of the Name field in a variable that can be accessed anywhere in its scope. Variables in templates are prefixed by '$'. So we write

      462 | 463 |

      但是需要指出的是,我们无法用'.' 的形式来访问字段 Name,因为当他被转化成数组元素时,字段Name并不包括其中。解决方法是,将字段Name 存储为一个变量,那么它就能在任意范围内被访问。变量在模板中用法是加前缀'$'。所以可以这样

      464 | 465 |
      466 | 
      467 | {{$name := .Name}}
      468 | {{range .Emails}}
      469 |     Name is {{$name}}, email is {{.}}
      470 | {{end}}
      471 | 
      472 | 
      473 | 474 |

      The program is

      475 | 476 |

      程序如下:

      477 | 478 |
      
      479 | /**
      480 |  * PrintNameEmails
      481 |  */
      482 | 
      483 | package main
      484 | 
      485 | import (
      486 |         "html/template"
      487 |         "os"
      488 |         "fmt"
      489 | )
      490 | 
      491 | type Person struct {
      492 |         Name   string
      493 |         Emails []string
      494 | }
      495 | 
      496 | const templ = `{{$name := .Name}}
      497 | {{range .Emails}}
      498 |     Name is {{$name}}, email is {{.}}
      499 | {{end}}
      500 | `
      501 | 
      502 | func main() {
      503 |         person := Person{
      504 |                 Name:   "jan",
      505 |                 Emails: []string{"jan@newmarch.name", "jan.newmarch@gmail.com"},
      506 |         }
      507 | 
      508 |         t := template.New("Person template")
      509 |         t, err := t.Parse(templ)
      510 |         checkError(err)
      511 | 
      512 |         err = t.Execute(os.Stdout, person)
      513 |         checkError(err)
      514 | }
      515 | 
      516 | func checkError(err error) {
      517 |         if err != nil {
      518 |                 fmt.Println("Fatal error ", err.Error())
      519 |                 os.Exit(1)
      520 |         }
      521 | }
      522 | 
      523 | 524 |

      with output

      525 | 526 |

      输出为

      527 | 528 |
      529 | 
      530 |     Name is jan, email is jan@newmarch.name
      531 | 
      532 |     Name is jan, email is jan.newmarch@gmail.com
      533 | 
      534 | 
      535 | 536 |

      Conditional statements

      537 | 538 |

      条件语句

      539 | 540 |

      Continuing with our Person example, supposing we just want to print out the list of emails, without digging into it. We can do that with a template

      541 | 542 |

      继续我们那个Person的例子,假设我们只是想打印出来的邮件列表,而不关心其中的字段。我们可以用模板这么干

      543 | 544 | 545 | 546 |
      
      547 | Name is {{.Name}}
      548 | Emails are {{.Emails}}
      549 | 
      550 | 551 |

      This will print

      552 |
      553 | 
      554 | Name is jan
      555 | Emails are [jan@newmarch.name jan.newmarch@gmail.com]
      556 | 
      557 | 
      558 | 559 |

      because that is how the fmt package will display a list.

      560 | 561 |

      因为这个fmt包会显示一个列表。

      562 | 563 |

      In many circumstances that may be fine, if that is what you want. Let's consider a case where it is almost right but not quite. There is a JSON package to serialise objects, which we looked at in Chapter 4. This would produce

      564 | 565 |

      在许多情况下,这样做也没有问题,如果那是你想要的。让我们考虑下一种情况,它 几乎是对的但不是必须的。有一个JSON序列化对象的包,让我们看看第4章。它是这样的

      566 | 567 |
      568 | 
      569 | {"Name": "jan",
      570 |  "Emails": ["jan@newmarch.name", "jan.newmarch@gmail.com"]
      571 | }
      572 | 
      573 | 
      574 | 575 |

      The JSON package is the one you would use in practice, but let's see if we can produce JSON output using templates. We can do something similar just by the templates we have. This is almost right as a JSON serialiser:

      576 | 577 |

      JSON包是一个你会在实践中使用,但是让我们看看我们是否能够使用JSON输出模板。我们可以做一些我们有的类似的模板。这几乎就是一个JSON串行器: 578 |

      579 | 580 | 581 |
      582 | 
      583 | {"Name": "{{.Name}}",
      584 |  "Emails": {{.Emails}}
      585 | }
      586 | 
      587 | 
      588 | 589 |

      It will produce

      590 | 591 |

      像这样组装

      592 |
      593 | 
      594 | {"Name": "jan",
      595 |  "Emails": [jan@newmarch.name jan.newmarch@gmail.com]
      596 | }
      597 | 
      598 | 
      599 | 600 |

      which has two problems: the addresses aren't in quotes, and the list elements should be ',' separated.

      601 | 602 |

      其中有两个问题:地址没有在引号中,列表中的元素应该是','分隔。

      603 | 604 |

      How about this: looking at the array elements, putting them in quotes and adding commas?

      605 | 606 |

      这样如何:在数组中的元素,把它们放在引号中并用逗号分隔?

      607 | 608 |
      609 | 
      610 | {"Name": {{.Name}},
      611 |   "Emails": [
      612 |    {{range .Emails}}
      613 |       "{{.}}",
      614 |    {{end}}
      615 |   ]
      616 | }
      617 | 
      618 | 
      619 | 620 |

      It will produce

      621 | 622 |

      像这样组装

      623 | 624 |
      625 | 
      626 | {"Name": "jan",
      627 |  "Emails": ["jan@newmarch.name", "jan.newmarch@gmail.com",]
      628 | }
      629 | 
      630 | 
      631 | 632 |

      (plus some white space.).

      633 | 634 |

      (再加上一些空白)。

      635 | 636 |

      Again, almost correct, but if you look carefully, you will see a trailing ',' after the last list element. According to the JSON syntax (see http://www.json.org/, this trailing ',' is not allowed. Implementations may vary in how they deal with this.

      637 | 638 |

      同样,这样貌似几乎是正确的,但如果你仔细看,你会看到尾有“,”在最后的列表元素。根据JSON的语法(请参阅 http://www.json.org/,这个结尾的','是不允许的。这样实现结果可能会有所不同。

      639 | 640 |

      What we want is "print every element followed by a ',' except for the last one." This is actually a bit hard to do, so a better way is "print every element preceded by a ',' except for the first one." (I got this tip from "brianb" at Stack Overflow.). This is easier, because the first element has index zero and many programming languages, including the Go template language, treat zero as Boolean false.

      641 | 642 |

      我们想要打印所有在后面带','的元素除了最后一个。"这个确实有点难搞, 一个好方法"在',' 之前打印所有元素除了第一个。" (我在 "brianb"的 Stack Overflow上提了建议)。这样更易于实现,因为第一个元素索引为0,很多编程语言包括GO模板都将0当做布尔型的false

      643 | 644 |

      One form of the conditional statement is {{if pipeline}} T1 {{else}} T0 {{end}}. We need the pipeline to be the index into the array of emails. Fortunately, a variation on the range statement gives us this. There are two forms which introduce variables

      645 | 646 |

      条件语句的一种形式是{{if pipeline}} T1 {{else}} T0 {{end}}。我们需要通过pipeline来获取电子邮件到数组的索引。幸运的是, range的变化语句为我们提供了这一点。有两种形式,引进变量

      647 | 648 | 649 |
      650 | 
      651 | {{range $elmt := array}}
      652 | {{range $index, $elmt := array}}
      653 | 
      654 | 
      655 | 656 |

      So we set up a loop through the array, and if the index is false (0) we just print the element, otherwise print it preceded by a ','. The template is

      657 | 658 |

      所以我们遍历数组,如果该索引是false(0),我们只是打印的这个索引的元素,否则打印它前面是','的元素。模板是这样的

      659 | 660 | 661 |
      662 | 
      663 | {"Name": "{{.Name}}",
      664 |  "Emails": [
      665 |  {{range $index, $elmt := .Emails}}
      666 |     {{if $index}}
      667 |         , "{{$elmt}}"
      668 |     {{else}}
      669 |          "{{$elmt}}"
      670 |     {{end}}
      671 |  {{end}}
      672 |  ]
      673 | }
      674 | 
      675 | 
      676 | 677 |

      and the full program is

      678 | 679 |

      完整的程序如下

      680 | 681 |
      
      682 | /**
      683 |  * PrintJSONEmails
      684 |  */
      685 | 
      686 | package main
      687 | 
      688 | import (
      689 |         "html/template"
      690 |         "os"
      691 |         "fmt"
      692 | )
      693 | 
      694 | type Person struct {
      695 |         Name   string
      696 |         Emails []string
      697 | }
      698 | 
      699 | const templ = `{"Name": "{{.Name}}",
      700 |  "Emails": [
      701 | {{range $index, $elmt := .Emails}}
      702 |     {{if $index}}
      703 |         , "{{$elmt}}"
      704 |     {{else}}
      705 |          "{{$elmt}}"
      706 |     {{end}}
      707 | {{end}}
      708 |  ]
      709 | }
      710 | `
      711 | 
      712 | func main() {
      713 |         person := Person{
      714 |                 Name:   "jan",
      715 |                 Emails: []string{"jan@newmarch.name", "jan.newmarch@gmail.com"},
      716 |         }
      717 | 
      718 |         t := template.New("Person template")
      719 |         t, err := t.Parse(templ)
      720 |         checkError(err)
      721 | 
      722 |         err = t.Execute(os.Stdout, person)
      723 |         checkError(err)
      724 | }
      725 | 
      726 | func checkError(err error) {
      727 |         if err != nil {
      728 |                 fmt.Println("Fatal error ", err.Error())
      729 |                 os.Exit(1)
      730 |         }
      731 | }
      732 | 
      733 | 734 |

      This gives the correct JSON output.

      735 | 736 |

      上面给出的是正确的JSON输出

      737 | 738 |

      Before leaving this section, we note that the problem of formatting a list with comma separators can be approached by defining suitable functions in Go that are made available as template functions. To re-use a well known saying, "There's more than one way to do it!". The following program was sent to me by Roger Peppe:

      739 | 740 |

      在结束本节之前,我们强调了用逗号分隔的列表格式的问题,解决方式是可以在模板函数中定义适当的函数。正如俗话说的,“道路不止一条!”下面的程序是Roger Peppe给我的: 741 |

      742 | 743 |
      
      744 | /**
      745 |  * Sequence.go
      746 |  * Copyright Roger Peppe
      747 |  */
      748 | 
      749 | package main
      750 | 
      751 | import (
      752 |         "errors"
      753 |         "fmt"
      754 |         "os"
      755 |         "text/template"
      756 | )
      757 | 
      758 | var tmpl = `{{$comma := sequence "" ", "}}
      759 | {{range $}}{{$comma.Next}}{{.}}{{end}}
      760 | {{$comma := sequence "" ", "}}
      761 | {{$colour := cycle "black" "white" "red"}}
      762 | {{range $}}{{$comma.Next}}{{.}} in {{$colour.Next}}{{end}}
      763 | `
      764 | 
      765 | var fmap = template.FuncMap{
      766 |         "sequence": sequenceFunc,
      767 |         "cycle":    cycleFunc,
      768 | }
      769 | 
      770 | func main() {
      771 |         t, err := template.New("").Funcs(fmap).Parse(tmpl)
      772 |         if err != nil {
      773 |                 fmt.Printf("parse error: %v\n", err)
      774 |                 return
      775 |         }
      776 |         err = t.Execute(os.Stdout, []string{"a", "b", "c", "d", "e", "f"})
      777 |         if err != nil {
      778 |                 fmt.Printf("exec error: %v\n", err)
      779 |         }
      780 | }
      781 | 
      782 | type generator struct {
      783 |         ss []string
      784 |         i  int
      785 |         f  func(s []string, i int) string
      786 | }
      787 | 
      788 | func (seq *generator) Next() string {
      789 |         s := seq.f(seq.ss, seq.i)
      790 |         seq.i++
      791 |         return s
      792 | }
      793 | 
      794 | func sequenceGen(ss []string, i int) string {
      795 |         if i >= len(ss) {
      796 |                 return ss[len(ss)-1]
      797 |         }
      798 |         return ss[i]
      799 | }
      800 | 
      801 | func cycleGen(ss []string, i int) string {
      802 |         return ss[i%len(ss)]
      803 | }
      804 | 
      805 | func sequenceFunc(ss ...string) (*generator, error) {
      806 |         if len(ss) == 0 {
      807 |                 return nil, errors.New("sequence must have at least one element")
      808 |         }
      809 |         return &generator{ss, 0, sequenceGen}, nil
      810 | }
      811 | 
      812 | func cycleFunc(ss ...string) (*generator, error) {
      813 |         if len(ss) == 0 {
      814 |                 return nil, errors.New("cycle must have at least one element")
      815 |         }
      816 |         return &generator{ss, 0, cycleGen}, nil
      817 | }
      818 | 
      819 | 820 | 821 |

      Conclusion

      822 | 823 |

      结论

      824 | 825 |

      The Go template package is useful for certain kinds of text transformations involving inserting values of objects. It does not have the power of, say, regular expressions, but is faster and in many cases will be easier to use than regular expressions

      826 | 827 |

      template包在对于某些类型的文本转换涉及插入对象值的情况是非常有用的。虽然它没有正则表达式功能强大,但它执行比正则表达式速度更快,在许多情况下比正则表达式更容易使用。

      828 | 829 |
      830 | 831 |

      Copyright © Jan Newmarch, jan@newmarch.name

      832 | 833 |

      If you like this book, please contribute using Flattr
      834 | or donate using PayPal

      835 | 836 |
      837 | 838 |
      839 | 840 | 841 | -------------------------------------------------------------------------------- /zh/Text/chapter-xml.html: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | XML 8 | 9 | 18 | 33 | 47 | 48 | 49 | 50 |
      51 |

      XML

      52 |
      53 | 54 |
      55 | 56 |
      57 |

      XML is a significant markup language mainly intended as a means of serialising data structures as a text document. Go has basic support for XML document processing.

      58 |

      XML是一种重要的标记语言,旨在把数据结构序列化成文本文档。Go基本支持XML文档处理。

      59 |
      60 | 61 |

      Introduction

      62 |

      介绍

      63 | 64 |

      XML is now a widespread way of representing complex data structures serialised into text format. It is used to describe documents such as DocBook and XHTML. It is used in specialised markup languages such as MathML and CML (Chemistry Markup Language). It is used to encode data as SOAP messages for Web Services, and the Web Service can be specified using WSDL (Web Services Description Language).

      65 |

      现在XML是一个用序列化的文本格式表现复杂数据结构的普遍方式。它被用来描述文档例如DocBook和XHTML。它还用于描述专用标记语言如MathML和CML(化学标记语言)。Web服务中它还用来将数据编码成SOAP消息,Web服务也可以指定使用WSDL(Web服务描述语言)。

      66 |

      At the simplest level, XML allows you to define your own tags for use in text documents. Tags can be nested and can be interspersed with text. Each tag can also contain attributes with values. For example,

      67 |

      在最简单的层次上,XML允许您定义您个人标记用于文本文档。标签可以嵌套,也穿插在文本里。每个标记也可以包含属性与值。例如,

      68 |
      
       69 | <person>
       70 |   <name>
       71 |     <family> Newmarch </family>
       72 |     <personal> Jan </personal>
       73 |   </name>
       74 |   <email type="personal">
       75 |     jan@newmarch.name
       76 |   </email>
       77 |   <email type="work">
       78 |     j.newmarch@boxhill.edu.au
       79 |   </email>
       80 | </person>
       81 |     
      82 | 83 |

      The structure of any XML document can be described in a number of ways:

      84 |

      任何XML文档的结构可以用多种方式描述:

      85 |
        86 |
      • A document type definition DTD is good for describing structure
      • 87 | 88 |
      • XML schema are good for describing the data types used by an XML document
      • 89 | 90 |
      • RELAX NG is proposed as an alternative to both
      • 91 |
      92 |
        93 |
      • 一个文档类型定义DTD有利于表现数据结构
      • 94 | 95 |
      • 在一个XML文档中,使用XML模式有利于描述数据类型
      • 96 | 97 |
      • RELAX NG提出了替代方案
      • 98 |
      99 |

      There is argument over the relative value of each way of defining the structure of an XML document. We won't buy into that, as Go does not suport any of them. Go cannot check for validity of any document against a schema, but only for well-formedness.

      100 |

      人们总会争论定义XML文档结构的每一个方式的好坏。我们不会陷入其中,因为Go不支持其中任何一个。Go不能检查任何文档模式的有效性,但只知道良构性。

      101 |

      Four topics are discussed in this chapter: parsing an XML stream, marshalling and unmarshalling Go data into XML, and XHTML.

      102 |

      在本章中讨论四个主题:解析一个XML流,编组和解组Go数据成为XML和XHTML。

      103 |

      Parsing XML

      104 |

      解析XML

      105 |

      Go has an XML parser which is created using NewParser. This takes an io.Reader as parameter and returns a pointer to Parser. The main method of this type is Token which returns the next token in the input stream. The token is one of the types StartElement, EndElement, CharData, Comment, ProcInst or Directive.

      106 |

      Go有一个使用 NewParser.创建的XML解析器。这需要一个io.Reader 作为参数,并返回一个指向Parser 的指针。这个类型的主要方法是 Token ,这个方法返回输入流中的下一个标记。该标记是 StartElement, EndElement, CharData, Comment, ProcInstDirective 其中一种。

      107 |

      The types are

      108 |

      这些类有

      109 | 110 |
      111 |
      StartElement
      112 | 113 |
      114 |

      The type StartElement is a structure with two field types:

      115 |

      StartElement 类型是一个包含两个字段的结构:

      116 |
      
      117 | type StartElement struct {
      118 |     Name Name
      119 |     Attr []Attr
      120 | }
      121 | 
      122 | type Name struct {
      123 |     Space, Local string
      124 | }
      125 | 
      126 | type Attr struct {
      127 |     Name  Name
      128 |     Value string
      129 | }
      130 |         
      131 |
      132 | 133 |
      EndElement
      134 | 135 |
      136 |

      This is also a structure

      137 |

      同样也是一个结构

      138 |
      
      139 | type EndElement struct {
      140 |     Name Name
      141 | }
      142 |         
      143 |
      144 | 145 |
      CharData
      146 | 147 |
      148 |

      This type represents the text content enclosed by a tag and is a simple type

      149 |

      这个类表示一个被标签包住的文本内容,是一个简单类。

      150 |
      
      151 | type CharData []byte
      152 |         
      153 |
      154 | 155 |
      Comment
      156 | 157 |
      158 |

      Similarly for this type

      159 |

      这个类也很简洁

      160 |
      
      161 | type Comment []byte
      162 |         
      163 |
      164 | 165 |
      ProcInst
      166 | 167 |
      168 |

      A ProcInst represents an XML processing instruction of the form <?target inst?>

      169 |

      一个ProcInst表示一个XML处理指令形式,如<target inst?>

      170 |
      
      171 | type ProcInst struct {
      172 |     Target string
      173 |     Inst   []byte
      174 | }
      175 |         
      176 |
      177 | 178 |
      Directive
      179 | 180 |
      181 |

      A Directive represents an XML directive of the form <!text>. The bytes do not include the <! and > markers.

      182 |

      一个指令用XML指令<!文本>的形式表示,内容不包含< !和> 构成部分。

      183 |
      
      184 | type Directive []byte
      185 |         
      186 |
      187 |
      188 | 189 |

      A program to print out the tree structure of an XML document is

      190 |

      打印XML文档的树结构的一个程序,代码如下

      191 |
      
      192 | /* Parse XML
      193 |  */
      194 | 
      195 | package main
      196 | 
      197 | import (
      198 |         "encoding/xml"
      199 |         "fmt"
      200 |         "io/ioutil"
      201 |         "os"
      202 |         "strings"
      203 | )
      204 | 
      205 | func main() {
      206 |         if len(os.Args) != 2 {
      207 |                 fmt.Println("Usage: ", os.Args[0], "file")
      208 |                 os.Exit(1)
      209 |         }
      210 |         file := os.Args[1]
      211 |         bytes, err := ioutil.ReadFile(file)
      212 |         checkError(err)
      213 |         r := strings.NewReader(string(bytes))
      214 | 
      215 |         parser := xml.NewDecoder(r)
      216 |         depth := 0
      217 |         for {
      218 |                 token, err := parser.Token()
      219 |                 if err != nil {
      220 |                         break
      221 |                 }
      222 |                 switch t := token.(type) {
      223 |                 case xml.StartElement:
      224 |                         elmt := xml.StartElement(t)
      225 |                         name := elmt.Name.Local
      226 |                         printElmt(name, depth)
      227 |                         depth++
      228 |                 case xml.EndElement:
      229 |                         depth--
      230 |                         elmt := xml.EndElement(t)
      231 |                         name := elmt.Name.Local
      232 |                         printElmt(name, depth)
      233 |                 case xml.CharData:
      234 |                         bytes := xml.CharData(t)
      235 |                         printElmt("\""+string([]byte(bytes))+"\"", depth)
      236 |                 case xml.Comment:
      237 |                         printElmt("Comment", depth)
      238 |                 case xml.ProcInst:
      239 |                         printElmt("ProcInst", depth)
      240 |                 case xml.Directive:
      241 |                         printElmt("Directive", depth)
      242 |                 default:
      243 |                         fmt.Println("Unknown")
      244 |                 }
      245 |         }
      246 | }
      247 | 
      248 | func printElmt(s string, depth int) {
      249 |         for n := 0; n < depth; n++ {
      250 |                 fmt.Print("  ")
      251 |         }
      252 |         fmt.Println(s)
      253 | }
      254 | 
      255 | func checkError(err error) {
      256 |         if err != nil {
      257 |                 fmt.Println("Fatal error ", err.Error())
      258 |                 os.Exit(1)
      259 |         }
      260 | }
      261 | 
      262 | 263 |

      Note that the parser includes all CharData, including the whitespace between tags.

      264 |

      注意,解析器包括所有文本节点,包括标签之间的空白。

      265 |

      If we run this program against the person data structure given earlier, it produces

      266 |

      如果我们运行这个程序对前面给出的 person数据结构,它就会打印出

      267 |
      
      268 | person
      269 |   "
      270 |   "
      271 |   name
      272 |     "
      273 |     "
      274 |     family
      275 |       " Newmarch "
      276 |     family
      277 |     "
      278 |     "
      279 |     personal
      280 |       " Jan "
      281 |     personal
      282 |     "
      283 |   "
      284 |   name
      285 |   "
      286 |   "
      287 |   email
      288 |     "
      289 |     jan@newmarch.name
      290 |   "
      291 |   email
      292 |   "
      293 |   "
      294 |   email
      295 |     "
      296 |     j.newmarch@boxhill.edu.au
      297 |   "
      298 |   email
      299 |   "
      300 | "
      301 | person
      302 | "
      303 | "
      304 | 
      305 | 306 |

      Note that as no DTD or other XML specification has been used, the tokenizer correctly prints out all the white space (a DTD may specify that the whitespace can be ignored, but without it that assumption cannot be made.)

      307 |

      注意,因为没有使用DTD或其他XML规范, tokenizer 正确地打印出所有的空白(一个DTD可能指定可以忽略空格,但是没有它假设就不能成立。)

      308 |

      There is a potential trap in using this parser. It re-uses space for strings, so that once you see a token you need to copy its value if you want to refer to it later. Go has methods such as func (c CharData) Copy() CharData to make a copy of data.

      309 |

      在使用这个解析器过程中有一个潜在的陷阱值得注意:它会为字符串重新利用空间,所以,一旦你看到一个你想要复制它的值的标记,假设你想稍后引用它的话,Go有类似的方法如 func (c CharData) Copy() CharData 来复制数据。

      310 |

      Unmarshalling XML

      311 |

      反编排XML

      312 |

      Go provides a function Unmarshal and a method func (*Parser) Unmarshal to unmarshal XML into Go data structures. The unmarshalling is not perfect: Go and XML are different languages.

      313 |

      Go提供一个函数 Unmarshal 和一个方法调用 func (*Parser) Unmarshal 解组XML转化为Go数据结构。解组并不是完美的:Go和XML毕竟是是两个不同的语言。

      314 |

      We consider a simple example before looking at the details. We take the XML document given earlier of

      315 |

      我们先考虑一个简单的例子再查看细节。我们用前面给出的XML文档

      316 |
      
      317 | <person>
      318 |   <name>
      319 |     <family> Newmarch </family>
      320 |     <personal> Jan </personal>
      321 |   </name>
      322 |   <email type="personal">
      323 |     jan@newmarch.name
      324 |   </email>
      325 |   <email type="work">
      326 |     j.newmarch@boxhill.edu.au
      327 |   </email>
      328 | </person>
      329 |     
      330 | 331 |

      We would like to map this onto the Go structures

      332 |

      接下来我们想把这个文档映射到Go结构

      333 |
      
      334 | type Person struct {
      335 |         Name Name
      336 |         Email []Email
      337 | }
      338 | 
      339 | type Name struct {
      340 |         Family string
      341 |         Personal string
      342 | }
      343 | 
      344 | type Email struct {
      345 |         Type string
      346 |         Address string
      347 | }
      348 |     
      349 | 350 |

      This requires several comments:

      351 |

      这里需要一些说明:

      352 |
        353 |
      1. Unmarshalling uses the Go reflection package. This requires that all fields by public i.e. start with a capital letter. Earlier versions of Go used case-insensitive matching to match fields such as the XML string "name" to the field Name. Now, though, case-sensitive matching is used. To perform a match, the structure fields must be tagged to show the XML string that will be matched against. This changes Person to 354 |
        
        355 | type Person struct {
        356 |         Name Name `xml:"name"`
        357 |         Email []Email `xml:"email"`
        358 | }
        359 |         
        360 |
      2. 361 | 362 |
      3. While tagging of fields can attach XML strings to fields, it can't do so with the names of the structures. An additional field is required, with field name "XMLName". This only affects the top-level struct, Person 363 |
        
        364 | type Person struct {
        365 |         XMLName Name `xml:"person"`
        366 |         Name Name `xml:"name"`
        367 |         Email []Email `xml:"email"`
        368 | }
        369 |         
        370 |
      4. 371 | 372 |
      5. Repeated tags in the map to a slice in Go
      6. 373 | 374 |
      7. Attributes within tags will match to fields in a structure only if the Go field has the tag ",attr". This occurs with the field Type of Email, where matching the attribute "type" of the "email" tag requires `xml:"type,attr"`
      8. 375 | 376 |
      9. If an XML tag has no attributes and only has character data, then it matches a string field by the same name (case-sensitive, though). So the tag `xml:"family"` with character data "Newmarch" maps to the string field Family
      10. 377 | 378 |
      11. But if the tag has attributes, then it must map to a structure. Go assigns the character data to the field with tag ,chardata. This occurs with the "email" data and the field Address with tag ,chardata
      12. 379 |
      380 |
        381 |
      1. 使用Go reflection包去解组。这要求所有字段是公有,也就是以一个大写字母开始。早期版本的Go使用不区分大小写匹配来匹配字段,例如XML标签“name”对应Name字段。但是现在使用case-sensitive匹配,要执行一个匹配,结构字段后必须用标记来显示XML标签名,以应付匹配。Person修改下应该是 382 |
        
        383 | type Person struct {
        384 |         Name Name `xml:"name"`
        385 |         Email []Email `xml:"email"`
        386 | }
        387 |         
      2. 388 |
      3. 虽然标记结构字段可以使用XML字符串,但是对于结构名不能这么做 ,这个解决办法是增加一个额外字段,命名“XMLName”。这只会影响上级结构,修改Person 如下 389 |
        
        390 | type Person struct {
        391 |         XMLName Name `xml:"person"`
        392 |         Name Name `xml:"name"`
        393 |         Email []Email `xml:"email"`
        394 | }
        395 |         
        396 |
      4. 397 |
      5. 重复标记会映射到Go的slice
      6. 398 |
      7. 要包含属性的标签准确匹配对应的结构字段,只有在Go字段后标记”,attr”。举个下面例子中 Email类型的Type字段,需要标记`xml:"type,attr"`才能匹配带有“type”属性的“email”
      8. 399 |
      9. 如果一个XML标签没有属性而且只有文本内容,那么它匹配一个string 字段是通过相同的名称(区分大小写的,不过如此)。所以标签`xml:"family"`将对应着文本”Newmarch”映射到Family的string字段中 400 |
      10. 401 |
      11. 但如果一个标签带有属性,那么它这个特征必须反映到一个结构。Go在字段后标记着 ,chardata的文字。如下面例子中通过 Address 后标记,chardata的字段来获取email的文本值
      12. 402 |
      403 |

      A program to unmarshal the document above is

      404 |

      解组上面文档的一个程序

      405 |
      
      406 | /* Unmarshal
      407 |  */
      408 | 
      409 | package main
      410 | 
      411 | import (
      412 |         "encoding/xml"
      413 |         "fmt"
      414 |         "os"
      415 |         //"strings"
      416 | )
      417 | 
      418 | type Person struct {
      419 |         XMLName Name    `xml:"person"`
      420 |         Name    Name    `xml:"name"`
      421 |         Email   []Email `xml:"email"`
      422 | }
      423 | 
      424 | type Name struct {
      425 |         Family   string `xml:"family"`
      426 |         Personal string `xml:"personal"`
      427 | }
      428 | 
      429 | type Email struct {
      430 |         Type    string `xml:"type,attr"`
      431 |         Address string `xml:",chardata"`
      432 | }
      433 | 
      434 | func main() {
      435 |         str := `<?xml version="1.0" encoding="utf-8"?>
      436 | <person>
      437 |   <name>
      438 |     <family> Newmarch </family>
      439 |     <personal> Jan </personal>
      440 |   </name>
      441 |   <email type="personal">
      442 |     jan@newmarch.name
      443 |   </email>
      444 |   <email type="work">
      445 |     j.newmarch@boxhill.edu.au
      446 |   </email>
      447 | </person>`
      448 | 
      449 |         var person Person
      450 | 
      451 |         err := xml.Unmarshal([]byte(str), &person)
      452 |         checkError(err)
      453 | 
      454 |         // now use the person structure e.g.
      455 |  fmt.Println("Family name: \"" + person.Name.Family + "\"")
      456 |         fmt.Println("Second email address: \"" + person.Email[1].Address + "\"")
      457 | }
      458 | 
      459 | func checkError(err error) {
      460 |         if err != nil {
      461 |                 fmt.Println("Fatal error ", err.Error())
      462 |                 os.Exit(1)
      463 |         }
      464 | }
      465 | 
      466 | 467 |

      (Note the spaces are correct.). The strict rules are given in the package specification.

      468 |

      (注意空间是正确的)。Go在包详解中给出了严格的规则。

      469 |

      Marshalling XML

      470 |

      编组 XML

      471 |

      Go 1 also has support for marshalling data structures into an XML document. The function is

      472 |

      Go1也支持将数据结构编组为XML文档的。这个函数是

      473 |
      474 |     
      475 | func Marshal(v interface}{) ([]byte, error)
      476 |     
      477 |   
      478 | 479 |

      This was used as a check in the last two lines of the previous program.

      480 |

      这是用来检查前面程序的最后两行

      481 | 706 |

      XHTML

      707 |

      XHTML

      708 | 709 |

      HTML does not conform to XML syntax. It has unterminated tags such as '<br>'. XHTML is a cleanup of HTML to make it compliant to XML. Documents in XHTML can be managed using the techniques above for XML.

      710 |

      HTML并不符合XML语法。 它包含无闭端的标签如“< br >”。XHTML是HTML的一个自身兼容XML的子集。 在XHTML文档中可以使用操作XML的技术。

      711 |

      HTML

      712 | 713 |

      There is some support in the XML package to handle HTML documents even though they are not XML-compliant. The XML parser discussed earlier can handle many HTML documents if it is modified by

      714 |

      XML包的部分方法可支持处理HTML文档,即使他们本身不具备XML兼容性。前面讨论的XML解析器修改下就可以处理大部分HTML文件

      715 |
      716 |     
      717 |         parser := xml.NewDecoder(r)
      718 |         parser.Strict = false
      719 |         parser.AutoClose = xml.HTMLAutoClose
      720 |         parser.Entity = xml.HTMLEntity
      721 |     
      722 |   
      723 | 724 |

      Conclusion

      725 |

      结论

      726 |

      Go has basic support for dealing with XML strings. It does not as yet have mechanisms for dealing with XML specification languages such as XML Schema or Relax NG.

      727 |

      Go基本支持对XML字符的处理,而且它不像有着针对XML专用语言如XML Schema或Relax NG的处理机制。

      728 |

      Copyright Jan Newmarch, jan@newmarch.name

      729 | 730 |

      If you like this book, please contribute using Flattr
      731 | or donate using PayPal

      732 | 733 |
      734 | 735 |
      736 | 737 | 738 | -------------------------------------------------------------------------------- /zh/index.html: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | Network Programming with Go 9 | 10 | 11 | 12 | 13 | 24 | 25 | 26 | 27 | 28 |

      Network programming with Go

      29 |

      Go网络编程

      30 |

      v1.0, 27 April 2012 31 | An e-book on building network applications using the Google Go 32 | programming language (golang) 33 |

      34 |

      v1.0, 27 April 2012 35 | 一本关于如何使用Google的Go语言编写网络应用的电子书 36 |

      37 | 38 |

      39 | 40 | This book has been revised to cover Go 1. The book is not complete, and also Go is expected 41 | to introduce more packages as it evolves. 42 | 43 |

      44 |

      45 | 46 | 这本书最新修订版支持Go1,这本书目前还不完善,需要介绍更多的Go的内置包。 47 | 48 |

      49 | 50 | 51 |

      Contents

      52 |
        53 |
      1. 54 |

        Architecture/架构

        55 |
      2. 56 |
      3. 57 |

        Overview of the Go language /Go语言概括

        58 |
      4. 59 |
      5. 60 |

        Socket-level Programming/Socket编程

        61 |
      6. 62 |
      7. 63 |

        Data serialisation/数据序列化

        64 |
      8. 65 |
      9. 66 |

        Application-Level Protocols/应用层协议

        67 |
      10. 68 |
      11. 69 |

        Managing character sets and encodings/管理字符编码

        70 |
      12. 71 |
      13. 72 |

        Security/安全

        73 |
      14. 74 |
      15. 75 |

        HTTP

        76 |
      16. 77 |
      17. 78 |

        Templates/模板

        79 |
      18. 80 |
      19. 81 |

        A Complete Web Server/一个完整的Web服务

        82 |
      20. 83 |
      21. 84 |

        HTML

        85 |
      22. 86 |
      23. 87 |

        XML

        88 |
      24. 89 |
      25. 90 |

        Remote Procedure Call/远程过程调用

        91 |
      26. 92 |
      27. 93 |

        Network Channels/网络 Channels

        94 |
      28. 95 |
      29. 96 |

        Web Sockets

        97 |
      30. 98 |
      99 | 100 | 101 | --------------------------------------------------------------------------------