Go的標(biāo)準(zhǔn)包encoding/json對(duì)JSON的編解碼提供了完整的支持。
創(chuàng)新互聯(lián)建站致力于互聯(lián)網(wǎng)網(wǎng)站建設(shè)與網(wǎng)站營銷,提供做網(wǎng)站、網(wǎng)站制作、網(wǎng)站開發(fā)、seo優(yōu)化、網(wǎng)站排名、互聯(lián)網(wǎng)營銷、小程序制作、公眾號(hào)商城、等建站開發(fā),創(chuàng)新互聯(lián)建站網(wǎng)站建設(shè)策劃專家,為不同類型的客戶提供良好的互聯(lián)網(wǎng)應(yīng)用定制解決方案,幫助客戶在新的全球化互聯(lián)網(wǎng)環(huán)境中保持優(yōu)勢。在編碼過程中,json包會(huì)將Go的類型轉(zhuǎn)換為JSON類型,轉(zhuǎn)換規(guī)則如下:
bool 轉(zhuǎn)換為JSON boolean
浮點(diǎn)數(shù), 整數(shù), Number 轉(zhuǎn)換為:JSON number
string轉(zhuǎn)換為:JSON string
數(shù)組、切片 轉(zhuǎn)換為:JSON數(shù)組
[]byte 轉(zhuǎn)換為:base64 string
struct、map轉(zhuǎn)換為:JSON object
func Marshal(v interface{}) ([]byte, error)
Marshal函數(shù)返回v的json編碼。Marshal函數(shù)會(huì)遞歸的處理值。如果一個(gè)值實(shí)現(xiàn)了Marshaler接口且非nil指針,會(huì)調(diào)用其MarshalJSON方法來生成json編碼。否則,Marshal函數(shù)使用默認(rèn)編碼格式。
(1)結(jié)構(gòu)體編碼
json包通過反射機(jī)制來實(shí)現(xiàn)編解碼,因此結(jié)構(gòu)體必須導(dǎo)出所轉(zhuǎn)換的字段,不導(dǎo)出的字段不會(huì)被json包解析。
package main
import (
"encoding/json"
"fmt"
)
type Person struct {
Name string
Age int
sex string
}
func main() {
bauer := Person{"Bauer", 25, "Man"}
bytes, err := json.Marshal(bauer)
if err != nil {
fmt.Println("Marshal failed.")
}
fmt.Println(string(bytes)) // {"Name":"Bauer","Age":25}
}
(2)結(jié)構(gòu)體字段標(biāo)簽
json包在解析結(jié)構(gòu)體時(shí),如果遇到key為json的字段標(biāo)簽,則會(huì)按照一定規(guī)則解析該標(biāo)簽:第一個(gè)字段是在JSON串中使用的名字,后續(xù)字段為其它選項(xiàng),例如omitempty指定空值字段不出現(xiàn)在JSON中。如果整個(gè)value為"-",則不解析該字段。
package main
import (
"encoding/json"
"fmt"
)
type Person struct {
Name string `json:"name,omitempty"`
Age int `json:"age"`
Sex string `json:"-"`
}
func main() {
bauer := Person{"Bauer", 25, "Man"}
bytes, err := json.Marshal(bauer)
if err != nil {
fmt.Println("Marshal failed.")
}
fmt.Println(string(bytes)) // {"name":"Bauer","age":25}
}
(3)匿名字段
json包在解析匿名字段時(shí),會(huì)將匿名字段的字段當(dāng)成該結(jié)構(gòu)體的字段處理。
package main
import (
"encoding/json"
"fmt"
)
type Point struct{ X, Y int }
type Circle struct {
Point
Radius int
}
func main() {
data, err := json.Marshal(Circle{Point{50, 50}, 25})
if err == nil {
fmt.Println(string(data))
}
}
// output:
//{"X":50,"Y":50,"Radius":25}
(4)轉(zhuǎn)換接口
在調(diào)用Marshal(v interface{})函數(shù)時(shí),Marshal函數(shù)會(huì)判斷v是否滿足json.Marshaler接口或encoding.TextMarshaler接口。如果滿足,則會(huì)調(diào)用json.Marshaler接口或encoding.TextMarshaler接口來進(jìn)行轉(zhuǎn)換(如果兩個(gè)都滿足,優(yōu)先調(diào)用json.Marshaler)。json.Marshaler接口或encoding.TextMarshaler接口定義如下:
type Marshaler interface {
MarshalJSON() ([]byte, error)
}
type TextMarshaler interface {
MarshalText() (text []byte, err error)
}
json.Marshaler示例如下:
package main
import (
"encoding/json"
"fmt"
)
type Point struct{ X, Y int }
func (pt Point) MarshalJSON() ([]byte, error) {
return []byte(fmt.Sprintf(`{"X":%d,"Y":%d}`, pt.X, pt.Y)), nil
}
func main() {
data, err := json.Marshal(Point{50, 50})
if err == nil {
fmt.Printf("%s\n", data)
}
}
// output:
// {"X":50,"Y":50}
encoding.TextMarshaler示例如下:
package main
import (
"encoding/json"
"fmt"
)
type Point struct{ X, Y int }
func (pt Point) MarshalText() ([]byte, error) {
return []byte(fmt.Sprintf("{\"X\":%d,\"Y\":%d}", pt.X, pt.Y)), nil
}
func main() {
data, err := json.Marshal(Point{50, 50})
if err == nil {
fmt.Printf("%s\n", data)
}
}
// output:
// "{\"X\":50,\"Y\":50}"
(5)編碼到輸出流func NewEncoder(w io.Writer) *Encoder
NewEncoder創(chuàng)建一個(gè)將數(shù)據(jù)寫入w的*Encoder
。func (enc *Encoder) Encode(v interface{}) error
Encode將v的json編碼寫入輸出流,并會(huì)寫入一個(gè)換行符。
使用示例如下:
package main
import (
"encoding/json"
"os"
)
type Person struct {
Name string
Age int
}
func main() {
persons := []Person{
{"Bauer", 30},
{"Bob", 20},
{"Lee", 24},
}
encoder := json.NewEncoder(os.Stdout)
for _, person := range persons {
encoder.Encode(person)
}
}
// output:
// {"Name":"Bauer","Age":30}
// {"Name":"Bob","Age":20}
// {"Name":"Lee","Age":24}
解碼將JSON轉(zhuǎn)換為Go數(shù)據(jù)類型。在解碼過程中,json包會(huì)將JSON類型轉(zhuǎn)換為Go類型,轉(zhuǎn)換規(guī)則如下:
JSON boolean 轉(zhuǎn)換為 bool
JSON number 轉(zhuǎn)換為 float64
JSON string 轉(zhuǎn)換為 string
JSON數(shù)組 轉(zhuǎn)換為 []interface{}
JSON object 轉(zhuǎn)換為 map
null 轉(zhuǎn)換為 nilfunc Unmarshal(data []byte, v interface{}) error
Unmarshal函數(shù)解析json編碼的數(shù)據(jù)data并將結(jié)果存入v指向的值,v通常傳入指針,否則解析雖不報(bào)錯(cuò),但數(shù)據(jù)無法賦值到接受體中。
要將json數(shù)據(jù)解碼寫入一個(gè)指針對(duì)象,Unmarshal函數(shù)首先處理json數(shù)據(jù)中json字面值null的情況。此時(shí),函數(shù)將指針設(shè)為nil;否則,函數(shù)將json數(shù)據(jù)解碼寫入指針指向的值;如果指針本身是nil,函數(shù)會(huì)先申請(qǐng)一個(gè)值并使指針指向它。
要將json數(shù)據(jù)解碼寫入一個(gè)結(jié)構(gòu)體,函數(shù)會(huì)匹配輸入對(duì)象的鍵和Marshal使用的鍵(結(jié)構(gòu)體字段名或者字段標(biāo)簽指定的鍵名),優(yōu)先選擇精確的匹配,但也接受大小寫不敏感的匹配。
如果一個(gè)JSON值不匹配給出的目標(biāo)類型,或者如果一個(gè)json數(shù)字寫入目標(biāo)類型時(shí)溢出,Unmarshal函數(shù)會(huì)跳過該字段并盡量完成其余的解碼操作。如果沒有出現(xiàn)更加嚴(yán)重的錯(cuò)誤,函數(shù)會(huì)返回一個(gè)描述第一個(gè)此類錯(cuò)誤的詳細(xì)信息的UnmarshalTypeError。
JSON的null值解碼為go的接口、指針、切片時(shí)會(huì)將其值設(shè)為nil,null在json一般表示“不存在”。解碼json的null值到go類型時(shí),不會(huì)造成任何改變,也不會(huì)產(chǎn)生錯(cuò)誤。
當(dāng)解碼字符串時(shí),不合法的utf-8或utf-16字符不視為錯(cuò)誤,而是將非法字符替換為unicode字符。
(1)JSON轉(zhuǎn)結(jié)構(gòu)體
JSON可以轉(zhuǎn)換成結(jié)構(gòu)體。json包通過反射機(jī)制來實(shí)現(xiàn)解碼,因此結(jié)構(gòu)體必須導(dǎo)出所轉(zhuǎn)換的字段,不導(dǎo)出的字段不會(huì)被json包解析,另外解析時(shí)不區(qū)分大小寫:
package main
import (
"encoding/json"
"fmt"
)
type Person struct {
Name string
Age int
sex string
}
func main() {
data := []byte(`{"Name":"Bauer","Age":25,"sex":"Man"}`)
var bauer Person
json.Unmarshal(data, &bauer)
fmt.Printf("Name:%s Age:%d sex:%s\n", bauer.Name, bauer.Age, bauer.sex)
}
// output:
// Name:Bauer Age:25 sex:
(2)結(jié)構(gòu)體字段標(biāo)簽
解碼時(shí)依然支持結(jié)構(gòu)體字段標(biāo)簽,規(guī)則和編碼相同。
package main
import (
"encoding/json"
"fmt"
)
type Person struct {
Name string `json:"name,omitempty"`
Age int `json:"age"`
Sex string `json:"-"`
}
func main() {
data := []byte(`{"name":"Bauer","age":25,"Sex":"Man"}`)
var bauer Person
json.Unmarshal(data, &bauer)
fmt.Printf("Name:%s, Age:%d, Sex:%s\n", bauer.Name, bauer.Age, bauer.Sex)
}
// output:
// Name:Bauer, Age:25, Sex:
(3)匿名字段
在解碼JSON時(shí),如果找不到字段,則查找字段的字段。
package main
import (
"encoding/json"
"fmt"
)
type Point struct {
X, Y int
}
type Circle struct {
Point
Radius int
}
func main() {
data := []byte(`{"X":80,"Y":80,"Radius":40}`)
var c Circle
json.Unmarshal(data, &c)
fmt.Printf("X:%d,Y:%d,Radius:%d\n", c.X, c.Y, c.Radius)
}
// output:
// X:80,Y:80,Radius:40
(4)轉(zhuǎn)換接口
解碼時(shí)根據(jù)參數(shù)類型是否滿足json.Unmarshaler和encoding.TextUnmarshaler來調(diào)用相應(yīng)函數(shù)(若兩個(gè)函數(shù)都存在,則優(yōu)先調(diào)用json.Unmarshaler)。json.Unmarshaler和encoding.TextUnmarshaler接口定義如下:
type Unmarshaler interface {
UnmarshalJSON([]byte) error
}
type TextUnmarshaler interface {
UnmarshalText(text []byte) error
}
json.Unmarshaler接口示例:
package main
import (
"encoding/json"
"fmt"
)
type Point struct{ X, Y int }
func (pt Point) UnmarshalJSON(data []byte) error {
fmt.Println(string(data))
return nil
}
func main() {
data := []byte(`{"X":50,"Y":50}`)
var point Point
json.Unmarshal(data, &point)
}
// output:
// {"X":50,"Y":50}
encoding.TextUnmarshaler接口示例:
package main
import (
"encoding/json"
"fmt"
)
type Point struct{ X, Y int }
func (pt Point) UnmarshalText(text []byte) error {
fmt.Println(string(text))
return nil
}
func main() {
data := []byte(`"{\"X\":50,\"Y\":50}"`)
var point Point
json.Unmarshal(data, &point)
}
// output:
// {"X":50,"Y":50}
(5)從輸入流解碼func NewDecoder(r io.Reader) *Decoder
NewDecoder創(chuàng)建一個(gè)從r讀取并解碼json對(duì)象的*Decoder
,Decoder有自己的緩沖,并可能超前讀取部分json數(shù)據(jù)。func (dec *Decoder) Buffered() io.Reader
Buffered方法返回保存在dec緩存里數(shù)據(jù)的讀取器,該返回值在下次調(diào)用Decode方法前有效。func (dec *Decoder) UseNumber()
UseNumber方法將dec設(shè)置為當(dāng)接收端是interface{}接口時(shí)將json數(shù)字解碼為Number類型而不是float64類型。func (dec *Decoder) Decode(v interface{}) error
Decode從輸入流讀取下一個(gè)json編碼值并保存在v指向的值里
package main
import (
"encoding/json"
"fmt"
"io"
"strings"
)
type Person struct {
Name string
Age int
}
func main() {
const dataStream = `
{ "Name" : "Bauer" , "Age" : 30}
{ "Name" : "Bob" , "Age" : 24 }
{ "Name" : "Lee" , "Age": 20}
`
dec := json.NewDecoder(strings.NewReader(dataStream))
for {
var person Person
if err := dec.Decode(&person); err == io.EOF {
break
}
fmt.Printf("Name: %s, Age: %d\n", person.Name, person.Age)
}
}
// output:
// Name: Bauer, Age: 30
// Name: Bob, Age: 24
// Name: Lee, Age: 20
Go的標(biāo)準(zhǔn)庫encoding/xml提供了對(duì)XML的操作。xml包提供了兩種方式來操作XML,一種是高階的方式,一種是低階的方式。高階的方式提供了Marshal和Unmarshal兩個(gè)函數(shù)分別來編碼(將Go數(shù)據(jù)結(jié)構(gòu)轉(zhuǎn)換成XML)和解碼(將XML轉(zhuǎn)換成Go數(shù)據(jù)結(jié)構(gòu))。低階的方法則基于token來進(jìn)行編碼和解碼。
低階方法是以Token為單位操縱XML,Token有四種類型:StartElement用來表示XML開始節(jié)點(diǎn);EndElement用來表示XML結(jié)束節(jié)點(diǎn);CharData即為XML的原始文本(raw text);Comment表示注釋。低階方法通常用在解析XML中的若干節(jié)點(diǎn)場景。
<!-- comment -->
<action application="answer">raw text</action>
上述xml文件中, < action application="answer" > 為StartElement, < /action > 為EndElement,raw text為CharData, < !-- -- > 為Comment。
Go語言xml包對(duì)Token的數(shù)據(jù)結(jié)構(gòu)進(jìn)行了封裝,代碼如下:
type Name struct {
Space, Local string
}
type Attr struct {
Name Name
Value string
}
type Token interface{}
type StartElement struct {
Name Name
Attr []Attr
}
func (e StartElement) Copy() StartElement {
attrs := make([]Attr, len(e.Attr))
copy(attrs, e.Attr)
e.Attr = attrs
return e
}
func (e StartElement) End() EndElement {
return EndElement{e.Name}
}
type EndElement struct {
Name Name
}
type CharData []byte
func (c CharData) Copy() CharData { return CharData(makeCopy(c)) }
type Comment []byte
xml包提供對(duì)xml文件的編碼解碼常用方法如下:
type TokenReader interface {
Token() (Token, error)
}
type Decoder struct {
Strict bool
AutoClose []string
Entity map[string]string
CharsetReader func(charset string, input io.Reader) (io.Reader, error)
DefaultSpace string
r io.ByteReader
t TokenReader
buf bytes.Buffer
saved *bytes.Buffer
stk *stack
free *stack
needClose bool
toClose Name
nextToken Token
nextByte int
ns map[string]string
err error
line int
offset int64
unmarshalDepth int
}
func NewDecoder(r io.Reader) *Decoder
NewDecoder從io.Reader對(duì)象讀取xml數(shù)據(jù),創(chuàng)建一個(gè)Decoderfunc NewTokenDecoder(t TokenReader) *Decoder
NewTokenDecoder使用底層Token流創(chuàng)建一個(gè)XML解析器func (d *Decoder) Token() (Token, error)
Token返回解析器的下一個(gè)Token,解析結(jié)束返回io.EOF
type Encoder struct {
p printer
}
func NewEncoder(w io.Writer) *Encoder
創(chuàng)建編碼器,參數(shù)為io.Writerfunc (enc *Encoder) EncodeToken(t Token) error
編碼Tokenfunc (enc *Encoder) Flush() error
刷新緩沖區(qū),將已經(jīng)編碼內(nèi)容寫入io.Writerfunc (enc *Encoder) Indent(prefix, indent string)
縮進(jìn)
示例如下:
package main
import (
"bytes"
"encoding/xml"
"fmt"
"io"
)
var file string = `<person id="13"><name><first>John</first><last>Doe</last></name><age>42</age><Married>false</Married><City>Hanga Roa</City><State>Easter Island</State><!-- Need more details. --></person>`
func parseXMLFromToken(xmlFile string) {
// 創(chuàng)建一個(gè)io.Reader
reader := bytes.NewReader([]byte(xmlFile))
// 創(chuàng)建×××
dec := xml.NewDecoder(reader)
// 開始遍歷解碼
indent := "" // 控制縮進(jìn)
sep := " " // 每層的縮進(jìn)量為四個(gè)空格
for {
tok, err := dec.Token() // 返回下一個(gè)Token
// 錯(cuò)誤處理
if err == io.EOF { // 如果讀到結(jié)尾,則退出循環(huán)
break
}
switch tok := tok.(type) { // Type switch
case xml.StartElement: // 開始節(jié)點(diǎn),打印名字和屬性
fmt.Print(indent)
fmt.Printf("<%s ", tok.Name.Local)
s := ""
for _, v := range tok.Attr {
fmt.Printf(`%s%s="%s"`, s, v.Name.Local, v.Value)
s = " "
}
fmt.Println(">")
indent += sep // 遇到開始節(jié)點(diǎn),則增加縮進(jìn)量
case xml.EndElement: // 結(jié)束節(jié)點(diǎn),打印名字
indent = indent[:len(indent)-len(sep)] // 遇到結(jié)束節(jié)點(diǎn),則減少縮進(jìn)量
fmt.Printf("%s</%s>\n", indent, tok.Name.Local)
case xml.CharData: // 原始字符串,直接打印
fmt.Printf("%s%s\n", indent, tok)
case xml.Comment: // 注釋,直接打印
fmt.Printf("%s<!-- %s -->\n", indent, tok)
}
}
}
type AttrMap map[string]string // 屬性的鍵值對(duì)容器
// start()用來構(gòu)建開始節(jié)點(diǎn)
func start(tag string, attrs AttrMap) xml.StartElement {
var a []xml.Attr
for k, v := range attrs {
a = append(a, xml.Attr{xml.Name{"", k}, v})
}
return xml.StartElement{xml.Name{"", tag}, a}
}
func generateXMLFile() {
// 創(chuàng)建編碼器
buffer := new(bytes.Buffer)
enc := xml.NewEncoder(buffer)
// 開始生成XML
startPerson := start("person", AttrMap{"id": "13"})
enc.EncodeToken(startPerson)
startName := start("name", AttrMap{})
enc.EncodeToken(startName)
startFirstName := start("first", AttrMap{})
enc.EncodeToken(startFirstName)
enc.EncodeToken(xml.CharData("John"))
enc.EncodeToken(startFirstName.End())
starLastName := start("last", AttrMap{})
enc.EncodeToken(starLastName)
enc.EncodeToken(xml.CharData("Doe"))
enc.EncodeToken(starLastName.End())
enc.EncodeToken(startName.End())
startAge := start("age", AttrMap{})
enc.EncodeToken(startAge)
enc.EncodeToken(xml.CharData("42"))
enc.EncodeToken(startAge.End())
startMarried := start("Married", AttrMap{})
enc.EncodeToken(startMarried)
enc.EncodeToken(xml.CharData("false"))
enc.EncodeToken(startMarried.End())
startCity := start("City", AttrMap{})
enc.EncodeToken(startCity)
enc.EncodeToken(xml.CharData("Hanga Roa"))
enc.EncodeToken(startCity.End())
startState := start("State", AttrMap{})
enc.EncodeToken(startState)
enc.EncodeToken(xml.CharData("Easter Island"))
enc.EncodeToken(startState.End())
enc.EncodeToken(xml.Comment("Need more details."))
enc.EncodeToken(startPerson.End())
// 寫入XML
enc.Flush()
// 打印結(jié)果
fmt.Println(buffer)
}
func main() {
fmt.Println("Decode XML:")
parseXMLFromToken(file)
fmt.Println("Encode XML:")
generateXMLFile()
}
xml包以反射機(jī)制實(shí)現(xiàn)的編解碼,因此自定義的結(jié)構(gòu)體必須導(dǎo)出所要轉(zhuǎn)換的字段。xml包定義了結(jié)構(gòu)體和XML數(shù)據(jù)的轉(zhuǎn)換規(guī)則。xml包根據(jù)字段的命名,字段的標(biāo)簽來映射XML元素,轉(zhuǎn)換規(guī)則如下:
1、xml:"value,value,..."結(jié)構(gòu)體標(biāo)簽為xml包所解析,第一個(gè)value對(duì)應(yīng)XML中的名字(節(jié)點(diǎn)名、屬性名)。
2、字段與XML節(jié)點(diǎn)名對(duì)應(yīng)關(guān)系:
A、如果存在名為XMLName的字段,并且標(biāo)簽中存在名字值,則該名字值為節(jié)點(diǎn)名稱,否則
B、如果存在名為XMLName的字段,并且類型為xml.Name,則該字段的值為節(jié)點(diǎn)名稱,否則
C、結(jié)構(gòu)體名稱。
3、字段標(biāo)簽的解析
A、"-"忽略該字段
B、"name,attr"字段映射為XML屬性,name為屬性名
C、",attr"字段映射為XML屬性,字段名為屬性名
D、",chardata"字段映射為原始字符串
E、"omitempty"若包含此標(biāo)簽則在字段值為0值時(shí)忽略此字段
4、視匿名字段的字段為結(jié)構(gòu)體的字段
xml高階方式常用方法如下:func Marshal(v interface{}) ([]byte, error)
接收一個(gè)interface{},遍歷其結(jié)構(gòu),編碼為XML
type Marshaler interface {
MarshalXML(e *Encoder, start StartElement) error
}
func MarshalIndent(v interface{}, prefix, indent string) ([]byte, error)
接收一個(gè)interface{},遍歷其結(jié)構(gòu),編碼為XML,增加縮進(jìn)func Unmarshal(data []byte, v interface{}) error
將data解碼為v,v通常為結(jié)構(gòu)體
高階方法適用于需要編碼和解碼整個(gè)XML并且需要以結(jié)構(gòu)化的數(shù)據(jù)操縱XML的場景。高階方法必須導(dǎo)出結(jié)構(gòu)體,會(huì)破壞封裝。
創(chuàng)建一個(gè)test.xml,內(nèi)容如下:
<?xml version="1.0" encoding="UTF-8"?>
<person id="13">
<name>
<first>John</first>
<last>Doe</last>
</name>
<age>42</age>
<Married>false</Married>
<City>Hanga Roa</City>
<State>Easter Island</State>
</person>
示例如下:
package main
import (
"encoding/xml"
"fmt"
"io/ioutil"
"os"
)
type Person struct {
XMLName string `xml:"person"`
ID string `xml:"id,attr"`
Name Name
Age Age
Married Married
City City
State State
}
type Name struct {
XMLName string `xml:"name"`
First FirstName
Last LastName
}
type FirstName struct {
XMLName string `xml:"first"`
Data string `xml:",chardata"`
}
type LastName struct {
XMLName string `xml:"last"`
Data string `xml:",chardata"`
}
type Age struct {
XMLName string `xml:"age"`
Data int `xml:",chardata"`
}
type Married struct {
XMLName string `xml:"Married"`
Data bool `xml:",chardata"`
}
type City struct {
XMLName string `xml:"City"`
Data string `xml:",chardata"`
}
type State struct {
XMLName string `xml:"State"`
Data string `xml:",chardata"`
}
func generateXMLFile(xmlFile string) {
first := FirstName{"first", "John"}
last := LastName{"last", "Doe"}
name := Name{"name", first, last}
age := Age{"age", 42}
married := Married{"Married", false}
city := City{"City", "Hanga Roa"}
state := State{"State", "Easter Island"}
person := Person{"person", "13", name, age, married, city, state}
data, _ := xml.MarshalIndent(person, "", " ")
headerBytes := []byte(xml.Header) //加入XML頭
outputData := append(headerBytes, data...)
ioutil.WriteFile("test.xml", outputData, os.ModeAppend)
}
func parseXMLFile(xmlFile string) {
bytes, err := ioutil.ReadFile(xmlFile)
if err != nil {
fmt.Println(err)
}
var person Person
xml.Unmarshal(bytes, &person)
fmt.Println("FirstName: ", person.Name.First.Data)
fmt.Println("LastName: ", person.Name.Last.Data)
fmt.Println("ID: ", person.ID)
fmt.Println("Married: ", person.Married.Data)
fmt.Println("Age: ", person.Age.Data)
fmt.Println("City: ", person.City.Data)
fmt.Println("State: ", person.State.Data)
}
func main() {
generateXMLFile("test.xml")
parseXMLFile("test.xml")
}
Base64是網(wǎng)絡(luò)上最常見的用于傳輸8Bit字節(jié)代碼的編碼方式之一。Base64編碼可用于在HTTP環(huán)境下傳遞較長的標(biāo)識(shí)信息。在Java Persistence系統(tǒng)Hibernate中,就采用了Base64來將一個(gè)較長的唯一標(biāo)識(shí)符(一般為128-bit的UUID)編碼為一個(gè)字符串,用作HTTP表單和HTTP GET URL中的參數(shù)。采用Base64編碼具有不可讀性,即所編碼的數(shù)據(jù)不會(huì)被人用肉眼所直接看到。
Go語言中encoding/base64提供了對(duì)base64編解碼支持,encoding/base64定義了一個(gè)Encoding結(jié)構(gòu)體,表示Base64的Encoding。并且導(dǎo)出了四個(gè)常用的Encoding對(duì)象:StdEncoding、URLEncoding、RawStdEncoding、RawURLEncoding。StdEncoding表示標(biāo)準(zhǔn)的Encoding,URLEncoding用于對(duì)URL編解碼,編解碼過程中會(huì)將Base64編碼中的特殊標(biāo)記+和/替換為-和_
,RawStdEncoding和RawURLEncoding是StdEncoding和URLEncoding的非padding版本。
type Encoding struct {
encode [64]byte
decodeMap [256]byte
padChar rune
strict bool
}
// 四個(gè)導(dǎo)出的編碼/×××
var StdEncoding = NewEncoding(encodeStd)
var URLEncoding = NewEncoding(encodeURL)
var RawStdEncoding = StdEncoding.WithPadding(NoPadding)
var RawURLEncoding = URLEncoding.WithPadding(NoPadding)
func (enc *Encoding) Encode(dst, src []byte)
將src編碼為dstfunc (enc *Encoding) EncodeToString(src []byte) string
將src編碼,返回stringfunc (enc *Encoding) Decode(dst, src []byte) (n int, err error)
將src解碼并寫入dst,成功返回寫入的字節(jié)數(shù)和errorfunc (enc *Encoding) DecodeString(s string) ([]byte, error)
將字符串s解碼并返回[]bytefunc (enc Encoding) WithPadding(padding rune) *Encoding
設(shè)置enc的padding,返回Encoding指針,NoPadding表示不進(jìn)行padding操作func NewDecoder(enc *Encoding, r io.Reader) io.Reader
創(chuàng)建一個(gè)base64的輸入流×××func NewEncoder(enc *Encoding, w io.Writer) io.WriteCloser
創(chuàng)建一個(gè)base64的輸出流編碼器
package main
import (
"encoding/base64"
"fmt"
"io"
"os"
"strings"
)
func StdEncodingExample() {
data := "Hello world!"
encoded := base64.StdEncoding.EncodeToString([]byte(data))
fmt.Println(encoded)
decoded, err := base64.StdEncoding.DecodeString(encoded)
if err == nil {
fmt.Println(string(decoded))
}
// Output:
// SGVsbG8gd29ybGQh
// Hello world!
}
func URLEncodingExample() {
url := []byte("https://blog.51cto.com/9291927")
encoded := base64.URLEncoding.EncodeToString(url)
fmt.Println(encoded)
decoded, err := base64.URLEncoding.DecodeString(encoded)
if err == nil {
fmt.Println(string(decoded))
}
// Output:
// aHR0cDovL2Jsb2cuNTFjdG8uY29tLzkyOTE5Mjc=
// https://blog.51cto.com/9291927
}
func ExampleStream() {
data := []byte("Hello Hyperledger Fabric")
encoder := base64.NewEncoder(base64.StdEncoding, os.Stdout)
encoder.Write(data)
encoder.Close()
fmt.Println()
input := "SGVsbG8gSHlwZXJsZWRnZXIgRmFicmlj"
reader := strings.NewReader(input)
decoder := base64.NewDecoder(base64.StdEncoding, reader)
io.Copy(os.Stdout, decoder)
// output:
// SGVsbG8gSHlwZXJsZWRnZXIgRmFicmlj
// Hello Hyperledger Fabric
}
func main() {
StdEncodingExample()
URLEncodingExample()
ExampleStream()
}
utf8實(shí)現(xiàn)了函數(shù)和常量來支持UTF-8編碼的文本。
const (
RuneError = '\uFFFD' // 錯(cuò)誤的 Rune 或 Unicode 代理字符
RuneSelf = 0x80 // ASCII 字符范圍
MaxRune = '\U0010FFFF' // Unicode 碼點(diǎn)的大值
UTFMax = 4 // 一個(gè)字符編碼的大長度
)
func EncodeRune(p []byte, r rune) int
將r轉(zhuǎn)換為UTF-8編碼寫入p中(p必須足夠長,通常為4個(gè)字節(jié))
如果r是無效的Unicode字符,則寫入RuneError
返回寫入的字節(jié)數(shù)func DecodeRune(p []byte) (r rune, size int)
解碼p中的第一個(gè)字符,返回解碼后的字符和p中被解碼的字節(jié)數(shù)
如果p為空,則返回(RuneError, 0)
如果p中的編碼無效,則返回(RuneError, 1)
無效編碼:UTF-8 編碼不正確(比如長度不夠)、結(jié)果超出Unicode范圍、編碼不是最短的。func DecodeRuneInString(s string) (r rune, size int)
解碼s中的第一個(gè)字符,返回解碼后的字符和p中被解碼的字節(jié)數(shù)func DecodeLastRune(p []byte) (r rune, size int)
解碼p中的最后一個(gè)字符,返回解碼后的字符和p中被解碼的字節(jié)數(shù)
如果p為空,則返回(RuneError, 0)
如果p中的編碼無效,則返回(RuneError, 1)func DecodeLastRuneInString(s string) (r rune, size int)
解碼p中的最后一個(gè)字符,返回解碼后的字符和p中被解碼的字節(jié)數(shù)func FullRune(p []byte) bool
FullRune檢測p中第一個(gè)字符的UTF-8編碼是否完整(完整并不表示有效)。
一個(gè)無效的編碼也被認(rèn)為是完整字符,將被轉(zhuǎn)換為一個(gè)RuneError字符。func FullRuneInString(s string) bool
FullRune檢測s中第一個(gè)字符的UTF-8編碼是否完整(完整并不表示有效)。func RuneCount(p []byte) int
返回p中的字符個(gè)數(shù)
錯(cuò)誤的UTF8編碼和長度不足的UTF8編碼將被當(dāng)作單字節(jié)的RuneError處理func RuneCountInString(s string) (n int)
返回s中的字符個(gè)數(shù)func RuneLen(r rune) int
RuneLen返回需要多少字節(jié)來編碼字符r,如果r是無效的字符,則返回-1func RuneStart(b byte) bool
判斷b是否為UTF8字符的首字節(jié)編碼,最高位(bit)是不是10的字節(jié)就是首字節(jié)。func Valid(p []byte) bool
Valid判斷p是否為完整有效的UTF8編碼序列。func ValidString(s string) bool
Valid判斷s是否為完整有效的UTF8編碼序列。func ValidRune(r rune) bool
ValidRune判斷r能否被正確的轉(zhuǎn)換為UTF8編碼。
超出Unicode范圍的碼點(diǎn)或UTF-16代理區(qū)中的碼點(diǎn)不能轉(zhuǎn)換。
package main
import (
"fmt"
"unicode/utf8"
)
func ExampleDecodeLastRune() {
b := []byte("Hello, 世界")
for len(b) > 0 {
r, size := utf8.DecodeLastRune(b)
fmt.Printf("%c %v\n", r, size)
b = b[:len(b)-size]
}
// Output:
// 界 3
// 世 3
// 1
// , 1
// o 1
// l 1
// l 1
// e 1
// H 1
}
func ExampleDecodeLastRuneInString() {
str := "Hello, 世界"
for len(str) > 0 {
r, size := utf8.DecodeLastRuneInString(str)
fmt.Printf("%c %v\n", r, size)
str = str[:len(str)-size]
}
// Output:
// 界 3
// 世 3
// 1
// , 1
// o 1
// l 1
// l 1
// e 1
// H 1
}
func ExampleDecodeRune() {
b := []byte("Hello, 世界")
for len(b) > 0 {
r, size := utf8.DecodeRune(b)
fmt.Printf("%c %v\n", r, size)
b = b[size:]
}
// Output:
// H 1
// e 1
// l 1
// l 1
// o 1
// , 1
// 1
// 世 3
// 界 3
}
func ExampleDecodeRuneInString() {
str := "Hello, 世界"
for len(str) > 0 {
r, size := utf8.DecodeRuneInString(str)
fmt.Printf("%c %v\n", r, size)
str = str[size:]
}
// Output:
// H 1
// e 1
// l 1
// l 1
// o 1
// , 1
// 1
// 世 3
// 界 3
}
func ExampleEncodeRune() {
r := '世'
buf := make([]byte, 3)
n := utf8.EncodeRune(buf, r)
fmt.Println(buf)
fmt.Println(n)
// Output:
// [228 184 150]
// 3
}
func ExampleFullRune() {
buf := []byte{228, 184, 150} // 世
fmt.Println(utf8.FullRune(buf))
fmt.Println(utf8.FullRune(buf[:2]))
// Output:
// true
// false
}
func ExampleFullRuneInString() {
str := "世"
fmt.Println(utf8.FullRuneInString(str))
fmt.Println(utf8.FullRuneInString(str[:2]))
// Output:
// true
// false
}
func ExampleRuneCount() {
buf := []byte("Hello, 世界")
fmt.Println("bytes =", len(buf))
fmt.Println("runes =", utf8.RuneCount(buf))
// Output:
// bytes = 13
// runes = 9
}
func ExampleRuneCountInString() {
str := "Hello, 世界"
fmt.Println("bytes =", len(str))
fmt.Println("runes =", utf8.RuneCountInString(str))
// Output:
// bytes = 13
// runes = 9
}
func ExampleRuneLen() {
fmt.Println(utf8.RuneLen('a'))
fmt.Println(utf8.RuneLen('界'))
// Output:
// 1
// 3
}
func ExampleRuneStart() {
buf := []byte("a界")
fmt.Println(utf8.RuneStart(buf[0]))
fmt.Println(utf8.RuneStart(buf[1]))
fmt.Println(utf8.RuneStart(buf[2]))
// Output:
// true
// true
// false
}
func ExampleValid() {
valid := []byte("Hello, 世界")
invalid := []byte{0xff, 0xfe, 0xfd}
fmt.Println(utf8.Valid(valid))
fmt.Println(utf8.Valid(invalid))
// Output:
// true
// false
}
func ExampleValidRune() {
valid := 'a'
invalid := rune(0xfffffff)
fmt.Println(utf8.ValidRune(valid))
fmt.Println(utf8.ValidRune(invalid))
// Output:
// true
// false
}
func ExampleValidString() {
valid := "Hello, 世界"
invalid := string([]byte{0xff, 0xfe, 0xfd})
fmt.Println(utf8.ValidString(valid))
fmt.Println(utf8.ValidString(invalid))
// Output:
// true
// false
}
func main() {
ExampleDecodeLastRune()
ExampleDecodeLastRuneInString()
ExampleDecodeRune()
ExampleDecodeRuneInString()
ExampleEncodeRune()
ExampleFullRune()
ExampleFullRuneInString()
ExampleRuneCount()
ExampleRuneCountInString()
ExampleRuneLen()
ExampleRuneStart()
ExampleValid()
ExampleValidRune()
ExampleValidString()
}
RPC(Remote Procedure Call,遠(yuǎn)程過程調(diào)用)是一種通過網(wǎng)絡(luò)從遠(yuǎn)程計(jì)算機(jī)程序上請(qǐng)求服務(wù),而不需要了解底層網(wǎng)絡(luò)細(xì)節(jié)的應(yīng)用程序通信協(xié)議。在OSI網(wǎng)絡(luò)通信模型中,RPC跨越了傳輸層和應(yīng)用層。RPC使得開發(fā)包括網(wǎng)絡(luò)分布式多程序在內(nèi)的應(yīng)用程序更加容易。
RPC采用客戶機(jī)/服務(wù)器模式。請(qǐng)求程序是一個(gè)客戶機(jī),而服務(wù)提供程序是一個(gè)服務(wù)器。首先,客戶機(jī)調(diào)用進(jìn)程發(fā)送一個(gè)有進(jìn)程參數(shù)的調(diào)用信息到服務(wù)進(jìn)程,然后等待應(yīng)答信息。在服務(wù)器端,進(jìn)程保持睡眠狀態(tài)直到調(diào)用信息到達(dá)為止。當(dāng)一個(gè)調(diào)用信息到達(dá),服務(wù)器獲得進(jìn)程參數(shù),計(jì)算結(jié)果,發(fā)送答復(fù)信息,然后等待下一個(gè)調(diào)用信息,最后,客戶端調(diào)用進(jìn)程接收答復(fù)信息,獲得進(jìn)程結(jié)果,然后調(diào)用執(zhí)行繼續(xù)進(jìn)行。
RPC調(diào)用過程如下:
1、調(diào)用客戶端句柄;執(zhí)行傳送參數(shù)
2、調(diào)用本地系統(tǒng)內(nèi)核發(fā)送網(wǎng)絡(luò)消息
3、消息傳送到遠(yuǎn)程主機(jī)
4、服務(wù)器句柄得到消息并取得參數(shù)
5、執(zhí)行遠(yuǎn)程過程
6、執(zhí)行的過程將結(jié)果返回服務(wù)器句柄
7、服務(wù)器句柄返回結(jié)果,調(diào)用遠(yuǎn)程系統(tǒng)內(nèi)核
8、消息傳回本地主機(jī)
9、客戶句柄由內(nèi)核接收消息
10、客戶接收句柄返回的數(shù)據(jù)
Go的rpc支持三個(gè)級(jí)別的RPC:TCP、HTTP、JSONRPC。但Go的RPC包是獨(dú)一無二的RPC,與傳統(tǒng)的RPC系統(tǒng)不同,只支持Go開發(fā)的服務(wù)器與客戶端之間的交互,因?yàn)閮?nèi)部采用Gob編碼。Gob是Golang包自帶的一個(gè)數(shù)據(jù)結(jié)構(gòu)序列化的編碼/解碼工具,編碼使用Encoder,解碼使用Decoder,其典型應(yīng)用場景就是RPC。
Go RPC的函數(shù)只有符合下面的條件才能被遠(yuǎn)程訪問,不然會(huì)被忽略,詳細(xì)的要求如下:
(1)函數(shù)必須是導(dǎo)出的(首字母大寫)
(2)必須有兩個(gè)導(dǎo)出類型的參數(shù),第一個(gè)參數(shù)是接收的參數(shù),第二個(gè)參數(shù)是返回給客戶端的參數(shù),第二個(gè)參數(shù)必須是指針類型的。
(3)函數(shù)還要有一個(gè)返回值errorfunc (t *T) MethodName(argType T1, replyType *T2) error
T、T1和T2類型必須能被encoding/gob包編解碼。
net/rpc定義了一個(gè)缺省的DefaultServer,實(shí)現(xiàn)一個(gè)簡單的Server,可以直接調(diào)用Server的很多方法。
var DefaultServer = NewServer()
func HandleHTTP() {
DefaultServer.HandleHTTP(DefaultRPCPath, DefaultDebugPath)
}
如果需要配置不同的Server,如不同的監(jiān)聽地址或端口,需要自己創(chuàng)建Server。func NewServer() *Server
Server的監(jiān)聽方式如下:
func (server *Server) Accept(lis net.Listener)
func (server *Server) HandleHTTP(rpcPath, debugPath string)
func (server *Server) ServeCodec(codec ServerCodec)
func (server *Server) ServeConn(conn io.ReadWriteCloser)
func (server *Server) ServeHTTP(w http.ResponseWriter, req *http.Request)
func (server *Server) ServeRequest(codec ServerCodec) error
ServeHTTP?用于處理http請(qǐng)求的業(yè)務(wù)邏輯,首先處理http的?CONNECT請(qǐng)求,通過http.Hijacker創(chuàng)建連接conn, 然后調(diào)用ServeConn處理連接上 客戶端的請(qǐng)求。
func (server *Server) ServeHTTP(w http.ResponseWriter, req *http.Request) {
if req.Method != "CONNECT" {
w.Header().Set("Content-Type", "text/plain; charset=utf-8")
w.WriteHeader(http.StatusMethodNotAllowed)
io.WriteString(w, "405 must CONNECT\n")
return
}
conn, _, err := w.(http.Hijacker).Hijack()
if err != nil {
log.Print("rpc hijacking ", req.RemoteAddr, ": ", err.Error())
return
}
io.WriteString(conn, "HTTP/1.0 "+connected+"\n\n")
server.ServeConn(conn)
}
Server.HandleHTTP用于設(shè)置rpc的上下文路徑,rpc.HandleHTTP使用默認(rèn)的上下文路徑。
當(dāng)使用http.ListenAndServe啟動(dòng)一個(gè)http server的時(shí),HandleHTTP設(shè)置的上下文將用作RPC傳輸,上下文的請(qǐng)求由ServeHTTP來處理。
func (server *Server) HandleHTTP(rpcPath, debugPath string) {
http.Handle(rpcPath, server)
http.Handle(debugPath, debugHTTP{server})
}
Accept用來處理一個(gè)監(jiān)聽器,監(jiān)聽客戶端的連接,一旦監(jiān)聽器接收了一個(gè)連接,則交給ServeConn在另外一個(gè)goroutine中處理。
func (server *Server) Accept(lis net.Listener) {
for {
conn, err := lis.Accept()
if err != nil {
log.Print("rpc.Serve: accept:", err.Error())
return
}
go server.ServeConn(conn)
}
}
func (server *Server) ServeConn(conn io.ReadWriteCloser) {
buf := bufio.NewWriter(conn)
srv := &gobServerCodec{
rwc: conn,
dec: gob.NewDecoder(conn),
enc: gob.NewEncoder(buf),
encBuf: buf,
}
server.ServeCodec(srv)
}
連接最終由ServerCodec處理,默認(rèn)使用gobServerCodec處理,可以使用其它的Coder。
客戶端建立和服務(wù)器的連接
func Dial(network, address string) (*Client, error)
func DialHTTP(network, address string) (*Client, error)
func DialHTTPPath(network, address, path string) (*Client, error)
func NewClient(conn io.ReadWriteCloser) *Client
func NewClientWithCodec(codec ClientCodec) *Client
DialHTTP?和?DialHTTPPath通過HTTP的方式和服務(wù)器建立連接。
func DialHTTPPath(network, address, path string) (*Client, error) {
var err error
conn, err := net.Dial(network, address)
if err != nil {
return nil, err
}
io.WriteString(conn, "CONNECT "+path+" HTTP/1.0\n\n")
// Require successful HTTP response
// before switching to RPC protocol.
resp, err := http.ReadResponse(bufio.NewReader(conn), &http.Request{Method: "CONNECT"})
if err == nil && resp.Status == connected {
return NewClient(conn), nil
}
if err == nil {
err = errors.New("unexpected HTTP response: " + resp.Status)
}
conn.Close()
return nil, &net.OpError{
Op: "dial-http",
Net: network + " " + address,
Addr: nil,
Err: err,
}
}
首先發(fā)送CONNECT請(qǐng)求,如果連接成功則通NewClient(conn)創(chuàng)建client。
Dial通過TCP與服務(wù)器建立連接。
func Dial(network, address string) (*Client, error) {
conn, err := net.Dial(network, address)
if err != nil {
return nil, err
}
return NewClient(conn), nil
}
NewClient創(chuàng)建一個(gè)缺省codec為glob序列化庫的客戶端。
func NewClient(conn io.ReadWriteCloser) *Client {
encBuf := bufio.NewWriter(conn)
client := &gobClientCodec{conn, gob.NewDecoder(conn), gob.NewEncoder(encBuf), encBuf}
return NewClientWithCodec(client)
}
NewClientWithCodec創(chuàng)建一個(gè)codec序列化庫的客戶端。
func NewClientWithCodec(codec ClientCodec) *Client {
client := &Client{
codec: codec,
pending: make(map[uint64]*Call),
}
go client.input()
return client
}
客戶端的調(diào)用RPC服務(wù)方法有兩個(gè)方法:?Go?和?Call。?Go方法是異步的,返回一個(gè)Call指針對(duì)象, 它的Done是一個(gè)channel,如果服務(wù)返回,Done就可以得到返回的對(duì)象(實(shí)際是Call對(duì)象,包含Reply和error信息)。?Go是同步的方式調(diào)用,它實(shí)際是調(diào)用Call實(shí)現(xiàn)的
func (client *Client) Call(serviceMethod string, args interface{}, reply interface{}) error {
call := <-client.Go(serviceMethod, args, reply, make(chan *Call, 1)).Done
return call.Error
}
rpc框架默認(rèn)使用gob序列化庫,為了追求更好的效率或者追求更通用的序列化格式,可以采用其它序列化方式,如protobuf,,json,,xml等。
gob序列化庫要求注冊(cè)接口類型的具體實(shí)現(xiàn)類型。
func (server *Server) Register(rcvr interface{}) error
func (server *Server) RegisterName(name string, rcvr interface{}) error
Server.go:
package main
import (
"log"
"net/http"
"net/rpc"
)
type Args struct {
width int
Height int
}
type Rect struct{}
func (r *Rect) GetArea(p Args, ret *int) error {
*ret = p.Width * p.Height
return nil
}
func (r *Rect) GetPerimeter(p Args, ret *int) error {
*ret = (p.Width + p.Height) * 2
return nil
}
func main() {
rect := new(Rect)
//注冊(cè)一個(gè)rect服務(wù)
rpc.Register(rect)
//綁定服務(wù)到HTTP協(xié)議
rpc.HandleHTTP()
err := http.ListenAndServe(":8081", nil)
if err != nil {
log.Fatal(err)
}
}
Client.go:
package main
import (
"fmt"
"log"
"net/rpc"
)
type Args struct {
width int
Height int
}
func main() {
//連接遠(yuǎn)程RPC服務(wù)
rpc, err := rpc.DialHTTP("tcp", "127.0.0.1:8081")
if err != nil {
log.Fatal(err)
}
ret := 0
//調(diào)用服務(wù)方法
err = rpc.Call("Rect.GetArea", Args{50, 100}, &ret)
if err != nil {
log.Fatal(err)
}
fmt.Println(ret)
// 調(diào)用服務(wù)方法
err = rpc.Call("Rect.GetPerimeter", Args{50, 100}, &ret)
if err != nil {
log.Fatal(err)
}
fmt.Println(ret)
}
// output:
// 5000
// 300
Server.go:
package main
import (
"log"
"net"
"net/rpc"
)
type Args struct {
width int
Height int
}
type Rect struct{}
func (r *Rect) GetArea(p Args, ret *int) error {
*ret = p.Width * p.Height
return nil
}
func (r *Rect) GetPerimeter(p Args, ret *int) error {
*ret = (p.Width + p.Height) * 2
return nil
}
func errorHandler(err error) {
if err != nil {
log.Fatal(err)
}
}
func main() {
rect := new(Rect)
//注冊(cè)RPC服務(wù)
rpc.Register(rect)
tcpADDR, err := net.ResolveTCPAddr("tcp", "127.0.0.1:8081")
errorHandler(err)
//監(jiān)聽端口
tcpListen, err := net.ListenTCP("tcp", tcpADDR)
errorHandler(err)
// 處理RPC連接請(qǐng)求
for {
conn, err := tcpListen.Accept()
if err != nil {
continue
}
// goroutine處理RPC連接請(qǐng)求
go rpc.ServeConn(conn)
}
}
Client.go:
package main
import (
"fmt"
"log"
"net/rpc"
)
type Args struct {
width int
Height int
}
func main() {
//連接遠(yuǎn)程RPC服務(wù)
rpc, err := rpc.Dial("tcp", "127.0.0.1:8081")
if err != nil {
log.Fatal(err)
}
ret := 0
//調(diào)用服務(wù)方法
err = rpc.Call("Rect.GetArea", Args{50, 100}, &ret)
if err != nil {
log.Fatal(err)
}
fmt.Println(ret)
err = rpc.Call("Rect.GetPerimeter", Args{50, 100}, &ret)
if err != nil {
log.Fatal(err)
}
fmt.Println(ret)
}
// output:
// 5000
// 300
JSON RPC方式使用json進(jìn)行數(shù)據(jù)編解碼,而不是gob編碼。
Server.go:
package main
import (
"log"
"net"
"net/rpc"
"net/rpc/jsonrpc"
)
type Args struct {
width int
Height int
}
type Rect struct{}
func (r *Rect) GetArea(p Args, ret *int) error {
*ret = p.Width * p.Height
return nil
}
func (r *Rect) GetPerimeter(p Args, ret *int) error {
*ret = (p.Width + p.Height) * 2
return nil
}
func errorHandler(err error) {
if err != nil {
log.Fatal(err)
}
}
func main() {
rect := new(Rect)
//注冊(cè)RPC服務(wù)
rpc.Register(rect)
tcpAddr, err := net.ResolveTCPAddr("tcp", "127.0.0.1:8081")
errorHandler(err)
//監(jiān)聽端口
tcpListen, err := net.ListenTCP("tcp", tcpAddr)
errorHandler(err)
for {
conn, err := tcpListen.Accept()
if err != nil {
continue
}
//處理RPC連接請(qǐng)求
go jsonrpc.ServeConn(conn)
}
}
Client.go:
package main
import (
"fmt"
"log"
"net/rpc/jsonrpc"
)
type Args struct {
width int
Height int
}
func main() {
//連接遠(yuǎn)程RPC服務(wù)
rpc, err := jsonrpc.Dial("tcp", "127.0.0.1:8081")
if err != nil {
log.Fatal(err)
}
ret := 0
//調(diào)用服務(wù)方法
err = rpc.Call("Rect.GetArea", Args{50, 100}, &ret)
if err != nil {
log.Fatal(err)
}
fmt.Println(ret)
err = rpc.Call("Rect.GetPerimeter", Args{50, 100}, &ret)
if err != nil {
log.Fatal(err)
}
fmt.Println(ret)
}
// output:
// 5000
// 300
另外有需要云服務(wù)器可以了解下創(chuàng)新互聯(lián)scvps.cn,海內(nèi)外云服務(wù)器15元起步,三天無理由+7*72小時(shí)售后在線,公司持有idc許可證,提供“云服務(wù)器、裸金屬服務(wù)器、高防服務(wù)器、香港服務(wù)器、美國服務(wù)器、虛擬主機(jī)、免備案服務(wù)器”等云主機(jī)租用服務(wù)以及企業(yè)上云的綜合解決方案,具有“安全穩(wěn)定、簡單易用、服務(wù)可用性高、性價(jià)比高”等特點(diǎn)與優(yōu)勢,專為企業(yè)上云打造定制,能夠滿足用戶豐富、多元化的應(yīng)用場景需求。
本文題目:Go語言開發(fā)(十六)、Go語言常用標(biāo)準(zhǔn)庫六-創(chuàng)新互聯(lián)
網(wǎng)站地址:http://jinyejixie.com/article28/isijp.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供企業(yè)網(wǎng)站制作、移動(dòng)網(wǎng)站建設(shè)、網(wǎng)站排名、面包屑導(dǎo)航、網(wǎng)站設(shè)計(jì)、網(wǎng)站維護(hù)
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場,如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來源: 創(chuàng)新互聯(lián)
猜你還喜歡下面的內(nèi)容