เป็นเวลาสี่เดือนแล้วที่ฉันทำงานในโครงการที่เรียกว่า "การพัฒนาเครื่องมือปกป้องข้อมูลและการจัดการในภาครัฐและภาคอุตสาหกรรมโดยใช้บล็อกเชน"
ตอนนี้ฉันอยากจะบอกคุณว่าฉันเริ่มต้นโครงการนี้ได้อย่างไร และตอนนี้ฉันจะอธิบายโค้ดโปรแกรมโดยละเอียด
นี่เป็นบทความแรกในชุดบทความ ที่นี่ฉันอธิบายเซิร์ฟเวอร์และโปรโตคอล ในความเป็นจริง ผู้อ่านสามารถเขียนองค์ประกอบบล็อกเชนในเวอร์ชันของเขาเองได้
เมื่อปีที่แล้วในงาน Digital Breakthrough hackathon พวกเขาเกิดแนวคิดที่จะสร้างระบบที่เป็นประโยชน์สำหรับอุตสาหกรรมและเศรษฐกิจดิจิทัลโดยใช้เทคโนโลยีบัญชีแยกประเภทแบบกระจาย นอกจากนี้ ยังมีการออกทุนสนับสนุนเพื่อการพัฒนาโดย Innovation Assistance Fund (ฉันควรเขียนรายงานแยกต่างหาก บทความเกี่ยวกับเงินช่วยเหลือสำหรับผู้ที่เพิ่งเริ่มต้นสตาร์ทอัพ ) และตอนนี้ตามลำดับ
การพัฒนาเกิดขึ้นในภาษา Go และฐานข้อมูลที่เก็บบล็อกคือ LevelDB
ส่วนหลักคือโปรโตคอล เซิร์ฟเวอร์ (ซึ่งรัน TCP และ WebSocket - ส่วนแรกสำหรับการซิงโครไนซ์บล็อคเชน ส่วนที่สองสำหรับการเชื่อมต่อไคลเอนต์ การส่งธุรกรรมและคำสั่งจาก JavaScript เป็นต้น
ดังที่ได้กล่าวไปแล้ว บล็อกเชนนี้มีความจำเป็นเป็นหลักเพื่อทำให้เป็นอัตโนมัติและปกป้องการแลกเปลี่ยนผลิตภัณฑ์ระหว่างซัพพลายเออร์และลูกค้า หรือทั้งสองอย่างในบุคคลเดียว คนเหล่านี้ไม่รีบร้อนที่จะไว้วางใจซึ่งกันและกัน แต่งานไม่เพียงแต่สร้าง "สมุดเช็ค" ด้วยเครื่องคิดเลขในตัวเท่านั้น แต่ยังเป็นระบบที่ทำให้งานประจำส่วนใหญ่ที่เกิดขึ้นเมื่อทำงานกับวงจรชีวิตผลิตภัณฑ์เป็นไปโดยอัตโนมัติ รหัสไบต์ที่รับผิดชอบเรื่องนี้ตามธรรมเนียมของบล็อกเชน จะถูกจัดเก็บไว้ในอินพุตและเอาต์พุตของธุรกรรม (ธุรกรรมจะถูกจัดเก็บไว้ในบล็อก บล็อกใน LevelDB จะถูกเข้ารหัสล่วงหน้าในรูปแบบ GOB) ก่อนอื่น เรามาพูดถึงโปรโตคอลและเซิร์ฟเวอร์ (หรือที่เรียกว่าโหนด) กันก่อน
โปรโตคอลไม่ซับซ้อน ประเด็นทั้งหมดคือการเปลี่ยนไปใช้โหมดการโหลดข้อมูลบางอย่าง ซึ่งมักจะเป็นบล็อกหรือธุรกรรม เพื่อตอบสนองต่อบรรทัดคำสั่งพิเศษ และยังจำเป็นสำหรับการแลกเปลี่ยนสินค้าคงคลัง เพื่อให้โหนดรู้ว่าใครเป็นใคร เชื่อมต่อและดำเนินธุรกิจอย่างไร (โหนดที่เชื่อมต่อสำหรับเซสชันการซิงโครไนซ์เรียกอีกอย่างว่า "เพื่อนบ้าน" เนื่องจากทราบ IP และข้อมูลสถานะถูกจัดเก็บไว้ในหน่วยความจำ)
โฟลเดอร์ (ไดเร็กทอรีที่ Linux เรียก) ตามความเข้าใจของโปรแกรมเมอร์ Go เรียกว่าแพ็คเกจ ดังนั้นที่จุดเริ่มต้นของแต่ละไฟล์ที่มีโค้ด Go จากไดเร็กทอรีนี้ พวกเขาเขียนแพ็คเกจ folder_name_where_this_file อยู่ มิฉะนั้น คุณจะไม่สามารถฟีดแพ็คเกจไปยังคอมไพลเลอร์ได้ นี่ไม่ใช่ความลับสำหรับผู้ที่รู้ภาษานี้ เหล่านี้คือแพ็คเกจ:
- การสื่อสารเครือข่าย (เซิร์ฟเวอร์ ไคลเอนต์ โปรโตคอล)
- โครงสร้างของข้อมูลที่จัดเก็บและส่ง (บล็อก ธุรกรรม)
- ฐานข้อมูล (บล็อกเชน)
- ฉันทามติ
- เครื่องเสมือนแบบเรียงซ้อน (xvm)
- อุปกรณ์เสริม (crypto, ประเภท) นั่นคือทั้งหมดสำหรับตอนนี้
นี่เป็นเวอร์ชันเพื่อการศึกษา ขาดปฏิสัมพันธ์ระหว่างกระบวนการและองค์ประกอบการทดลองหลายอย่าง แต่โครงสร้างนั้นสอดคล้องกับโครงสร้างที่กำลังดำเนินการพัฒนา หากคุณมีสิ่งใดที่จะแนะนำในความคิดเห็นฉันยินดีที่จะนำไปพิจารณาในการพัฒนาต่อไป และตอนนี้สำหรับคำอธิบายของเซิร์ฟเวอร์และ โปรโตคอล.
มาดูที่เซิฟเวอร์กันก่อน
รูทีนย่อยของเซิร์ฟเวอร์ทำหน้าที่เป็นเซิร์ฟเวอร์ข้อมูลที่ทำงานบนโปรโตคอล TCP โดยใช้โครงสร้างข้อมูลจากแพ็คเกจโปรโตคอล
รูทีนใช้แพ็คเกจต่อไปนี้: เซิร์ฟเวอร์, โปรโตคอล, ชนิด. ในแพ็คเกจนั้นเอง tcp_server.go มีโครงสร้างข้อมูล บริการ.
type Serve struct {
Port string
BufSize int
ST *types.Settings
}
สามารถยอมรับพารามิเตอร์ต่อไปนี้:
- พอร์ตเครือข่ายที่จะแลกเปลี่ยนข้อมูล
- ไฟล์การกำหนดค่าเซิร์ฟเวอร์ในรูปแบบ JSON
- ตั้งค่าสถานะสำหรับการทำงานในโหมดแก้ไขข้อบกพร่อง (บล็อคเชนส่วนตัว)
ความคืบหน้า:
- อ่านการกำหนดค่าจากไฟล์ JSON
- มีการตรวจสอบการตั้งค่าสถานะโหมดดีบัก: หากมีการตั้งค่าไว้ ตัวกำหนดเวลาการซิงโครไนซ์เครือข่ายจะไม่เปิดตัวและบล็อกเชนจะไม่ถูกโหลด
- การเริ่มต้นโครงสร้างข้อมูลการกำหนดค่าและการเริ่มต้นเซิร์ฟเวอร์
เซิร์ฟเวอร์
- ดำเนินการเปิดตัวเซิร์ฟเวอร์ TCP และการโต้ตอบเครือข่ายตามโปรโตคอล
- มีโครงสร้างข้อมูลเสิร์ฟซึ่งประกอบด้วยหมายเลขพอร์ต ขนาดบัฟเฟอร์ และตัวชี้ไปยังโครงสร้าง ประเภทการตั้งค่า
- วิธีการเรียกใช้เริ่มต้นการโต้ตอบของเครือข่าย (การฟังการเชื่อมต่อขาเข้าบนพอร์ตที่กำหนด เมื่อได้รับการเชื่อมต่อใหม่ การประมวลผลจะถูกถ่ายโอนไปยังวิธีจัดการส่วนตัวในเธรดใหม่)
- В จัดการ ข้อมูลจากการเชื่อมต่อจะถูกอ่านลงในบัฟเฟอร์ แปลงเป็นตัวแทนสตริงและส่งผ่านไปยัง โปรโตคอลทางเลือก
- โปรโตคอลทางเลือก ผลตอบแทน ผล หรือทำให้เกิดข้อผิดพลาด ผล แล้วโอนไปที่ โปรโตคอลตีความซึ่งกลับมา ภายใน - วัตถุประเภท ตีความข้อมูลหรือทำให้เกิดข้อผิดพลาดในการประมวลผลผลการคัดเลือก
- จากนั้นสวิตช์จะถูกดำเนินการ intrpr.คำสั่ง[0] ซึ่งจะตรวจสอบหนึ่งใน: ผลลัพธ์ คำเชิญ ข้อผิดพลาด และมีส่วน ผิดนัด
- ในส่วน ผล สวิตช์ถูกพบตามค่า intrpr.คำสั่ง[1] ซึ่งจะตรวจสอบค่าต่างๆ ความยาวบัฟเฟอร์ и รุ่น (ในแต่ละกรณีจะมีการเรียกใช้ฟังก์ชันที่เกี่ยวข้อง)
ฟังก์ชั่น รับเวอร์ชัน и บัฟเฟอร์Length อยู่ในไฟล์ srvlib.go แพ็คเกจเซิร์ฟเวอร์
GetVersion(conn net.Conn, version string)
เพียงพิมพ์ไปที่คอนโซลและส่งเวอร์ชันที่ส่งผ่านพารามิเตอร์ไปยังไคลเอนต์:
conn.Write([]byte("result:" + version))
.
ฟังก์ชัน
BufferLength(conn net.Conn, intrpr *protocol.InterpreteData)
โหลดบล็อก ธุรกรรม หรือข้อมูลเฉพาะอื่น ๆ ดังต่อไปนี้:
- พิมพ์ประเภทข้อมูลที่ระบุในโปรโตคอลที่ต้องยอมรับไปยังคอนโซล:
fmt.Println("DataType:", intrpr.Commands[2])
- อ่านค่า intrpr.ร่างกาย ให้เป็นตัวแปรตัวเลข buf_len
- สร้างบัฟเฟอร์ นิวบุฟ ขนาดที่ระบุ:
make([]byte, buf_len)
- ส่งการตอบกลับตกลง:
conn.Write([]byte("result:ok"))
- เติมบัฟเฟอร์จากสตรีมการอ่านโดยสมบูรณ์:
io.ReadFull(conn, newbuf)
.
- พิมพ์เนื้อหาของบัฟเฟอร์ไปยังคอนโซล
fmt.Println(string(newbuf))
และจำนวนไบต์ที่อ่าน
fmt.Println("Bytes length:", n)
- ส่งการตอบกลับตกลง:
conn.Write([]byte("result:ok"))
วิธีการจากแพ็คเกจเซิร์ฟเวอร์ได้รับการกำหนดค่าให้ประมวลผลข้อมูลที่ได้รับโดยใช้ฟังก์ชันจากแพ็คเกจ โปรโตคอล.
โปรโตคอล
โปรโตคอลทำหน้าที่เป็นวิธีการที่แสดงถึงข้อมูลในการแลกเปลี่ยนเครือข่าย
ตัวเลือก (สตริง str) (สตริง ข้อผิดพลาด) ดำเนินการประมวลผลข้อมูลเบื้องต้นที่เซิร์ฟเวอร์ได้รับ รับการแสดงสตริงของข้อมูลเป็นอินพุต และส่งคืนสตริงที่เตรียมไว้ ล่าม:
- สตริงอินพุตจะถูกแบ่งออกเป็นส่วนหัวและลำตัวโดยใช้ ReqParseN2(str)
- head ถูกแบ่งออกเป็นองค์ประกอบและวางไว้ในส่วนคำสั่งโดยใช้ ReqParseHead(head)
- В สวิตช์ (คำสั่ง [0]) เลือกคำสั่งที่ได้รับ (cmd, คีย์, ที่อยู่ หรือส่วนถูกทริกเกอร์ ผิดนัด)
- ตรวจสอบ 2 คำสั่งใน cmd สวิตช์ (คำสั่ง [1]) - ความยาว и การกลับใจใหม่.
- ความยาว ตรวจสอบประเภทข้อมูลใน คำสั่ง[2] และบันทึกไว้ใน ประเภทข้อมูล
- ตรวจสอบสิ่งนั้น ร่างกาย มีค่าสตริง
len(body) < 1
- ส่งกลับสตริงการตอบสนอง:
"result:bufferlength:" + datatype + "/" + body
- การกลับใจใหม่ ส่งคืนสตริง
return "result:version/auto"
ล่าม
ประกอบด้วยโครงสร้าง InterpreteData และดำเนินการประมวลผลรองของข้อมูลที่ส่งคืน ทางเลือก สตริงและการก่อตัวของวัตถุ ตีความข้อมูล.
type InterpreteData struct {
Head string
Commands []string
Body string
IsErr bool
ErrCode int
ErrMessage string
}
ฟังก์ชัน
Interprete(str string) (*InterpreteData, error)
ยอมรับสตริง ผล และสร้างและส่งกลับการอ้างอิงไปยังวัตถุ ตีความข้อมูล.
ความคืบหน้า:
- เหมือนกับ ทางเลือก ศีรษะและลำตัวถูกสกัดโดยใช้ ReqParseN2(str)
- หัวแบ่งออกเป็นองค์ประกอบโดยใช้ ReqParseHead(หัว)
- วัตถุถูกเตรียมใช้งาน ตีความข้อมูล และตัวชี้จะถูกส่งกลับ:
res := &InterpreteData{
Head: head,
Commands: commands,
Body: body,
}
return res, nil
วัตถุนี้ถูกนำมาใช้ใน เซิร์ฟเวอร์.ไป แพ็คเกจหลัก
ไคลเอนต์
แพ็คเกจไคลเอนต์ประกอบด้วยฟังก์ชันต่างๆ TCPConnect и TCPResponseData.
ฟังก์ชัน
TCPConnect(s *types.Settings, data []byte, payload []byte)
ทำงานดังนี้:
- มีการเชื่อมต่อกับการเชื่อมต่อที่ระบุในออบเจ็กต์การตั้งค่าที่ส่งผ่าน
net.Dial("tcp", s.Host + ":" + s.Port)
- ข้อมูลที่ส่งผ่านพารามิเตอร์ข้อมูลจะถูกส่ง:
conn.Write(data)
- คำตอบคืออ่าน
resp, n, _ := TCPResponseData(conn, s.BufSize)
และพิมพ์บนคอนโซล
fmt.Println(string(resp[:n]))
- ถ้าโอนแล้ว น้ำหนักบรรทุก แล้วส่งต่อ
conn.Write(payload)
และยังอ่านการตอบสนองของเซิร์ฟเวอร์โดยพิมพ์ไปยังคอนโซล
ฟังก์ชัน
TCPResponseData(conn net.Conn, bufsiz int) ([]byte, int, error)
สร้างบัฟเฟอร์ตามขนาดที่ระบุ อ่านการตอบสนองของเซิร์ฟเวอร์ที่นั่น และส่งคืนบัฟเฟอร์นี้และจำนวนไบต์ที่อ่าน รวมถึงอ็อบเจ็กต์ข้อผิดพลาด
รูทีนย่อยของลูกค้า
ทำหน้าที่ส่งคำสั่งไปยังเซิร์ฟเวอร์โหนด รวมถึงรับสถิติและการทดสอบโดยย่อ
สามารถยอมรับพารามิเตอร์ต่อไปนี้: ไฟล์การกำหนดค่าในรูปแบบ JSON, ข้อมูลที่จะส่งไปยังเซิร์ฟเวอร์ในรูปแบบสตริง, เส้นทางไปยังไฟล์ที่จะส่งไปยังเพย์โหลด, การตั้งค่าสถานะการจำลองตัวกำหนดเวลาโหนด, ประเภทของข้อมูลที่ถ่ายโอนเป็นค่าตัวเลข
- รับการกำหนดค่า
st := types.ParseConfig(*config)
- หากผ่านธงนกอีมู มันจะเริ่มต้น ผู้จัดกำหนดการ
- หากมีการระบุแฟล็ก f ที่ระบุเส้นทางไปยังไฟล์ เราจะโหลดข้อมูลเข้าไป FDB และเนื้อหาจะถูกส่งไปยังเซิร์ฟเวอร์
client.TCPConnect(st, []byte(CMD_BUFFER_LENGTH + ":" + strconv.Itoa(*t) + "/" + strconv.Itoa(fdblen)), fdb)
- หากไม่ได้ระบุไฟล์ ข้อมูลจากแฟล็กจะถูกส่งไป -d:
client.TCPConnect(st, []byte(*data), nil)
ทั้งหมดนี้เป็นเพียงการนำเสนอที่เรียบง่ายซึ่งแสดงโครงสร้างของโปรโตคอล ในระหว่างการพัฒนา ฟังก์ชั่นที่จำเป็นจะถูกเพิ่มเข้าไปในโครงสร้าง
ในส่วนที่สอง ฉันจะพูดถึงโครงสร้างข้อมูลสำหรับบล็อกและธุรกรรม ในบทที่ 3 เกี่ยวกับเซิร์ฟเวอร์ WebSocket สำหรับการเชื่อมต่อจาก JavaScript ในบทที่ 4 ฉันจะดูที่ตัวกำหนดเวลาการซิงโครไนซ์ จากนั้นเครื่องสแต็กที่ประมวลผลโค้ดไบต์จากอินพุตและเอาต์พุต การเข้ารหัสและ พูลสำหรับเอาต์พุต
ที่มา: will.com