Prej katër muajsh kam punuar në një projekt të quajtur “Zhvillimi i mjeteve të mbrojtjes dhe menaxhimit të të dhënave në sektorët qeveritarë dhe industrialë bazuar në blockchain”.
Tani do të doja t'ju tregoja se si e fillova këtë projekt, dhe tani do të përshkruaj kodin e programit në detaje.
Ky është artikulli i parë në një seri artikujsh. Këtu unë përshkruaj serverin dhe protokollin. Në fakt, lexuesi mund të shkruajë edhe versionet e tij të këtyre elementeve të blockchain.
Vitin e kaluar, në hackathon Digital Breakthrough, ata dolën me një ide për të bërë një sistem të dobishëm për industrinë dhe ekonominë dixhitale duke përdorur teknologjinë e librit të shpërndarë; gjithashtu u dha një grant për zhvillimin nga Fondi i Ndihmës për Inovacionin (duhet të shkruaj një të veçantë artikull për grantin, për ata që sapo kanë filluar startup-et ), dhe tani në rregull.
Zhvillimi bëhet në gjuhën Go, dhe baza e të dhënave në të cilën ruhen blloqet është LevelDB.
Pjesët kryesore janë protokolli, serveri (i cili drejton TCP dhe WebSocket - i pari për sinkronizimin e blockchain, i dyti për lidhjen e klientëve, dërgimin e transaksioneve dhe komandave nga JavaScript, për shembull.
Siç u përmend, ky blockchain nevojitet kryesisht për të automatizuar dhe mbrojtur shkëmbimin e produkteve midis furnitorëve dhe klientëve, ose të dyja në një person. Këta njerëz nuk nxitojnë t'i besojnë njëri-tjetrit. Por detyra nuk është vetëm krijimi i një "libër kontrolli" me një kalkulator të integruar, por një sistem që automatizon shumicën e detyrave rutinë që lindin kur punoni me ciklin e jetës së produktit. Bajtkodi që është përgjegjës për këtë çështje, siç është zakon me blockchains, ruhet në hyrjet dhe daljet e transaksioneve (vetë transaksionet ruhen në blloqe, blloqet në LevelDB janë të koduar paraprakisht në formatin GOB). Së pari, le të flasim për protokollin dhe serverin (aka node).
Protokolli nuk është i komplikuar, e gjithë qëllimi i tij është të kalojë në mënyrën e ngarkimit të disa të dhënave, zakonisht një bllok ose transaksion, në përgjigje të një linje komandimi të veçantë, dhe gjithashtu nevojitet për shkëmbimin e inventarit, në mënyrë që nyja të dijë se kush është. është i lidhur dhe si duhet të bëjnë biznesin (nyjet e lidhura për seancën e sinkronizimit quhen gjithashtu "fqinj" sepse IP-ja e tyre njihet dhe të dhënat e gjendjes së tyre ruhen në memorie).
Dosjet (direktoritë siç i quan Linux) në kuptimin e programuesve Go quhen paketa, kështu që në fillim të çdo skedari me kodin Go nga kjo direktori shkruajnë folder_name_where_this_file. Përndryshe, nuk do të jeni në gjendje ta ushqeni paketën te përpiluesi. Epo, kjo nuk është sekret për ata që e njohin këtë gjuhë. Këto janë paketat:
- Komunikimi në rrjet (server, klient, protokoll)
- Strukturat e të dhënave të ruajtura dhe të transmetuara (blloku, transaksioni)
- Baza e të dhënave (blockchain)
- Konsensusi
- Makina virtuale e grumbulluar (xvm)
- Ndihmës (kripto, lloje) kjo është e gjitha për momentin.
Ky është një version edukativ, i mungon ndërveprimi ndër-procesor dhe disa komponentë eksperimentalë, por struktura korrespondon me atë në të cilën po zhvillohet zhvillimi. Nëse keni diçka për të sugjeruar në komente, do të jem i lumtur ta marr parasysh në zhvillimin e mëtejshëm. Dhe tani për një shpjegim të serverit dhe protokoll.
Le të shohim së pari serverin.
Nënprogrami i serverit vepron si një server i të dhënave që funksionon në krye të protokollit TCP duke përdorur strukturat e të dhënave nga paketa e protokollit.
Rutina përdor paketat e mëposhtme: server, protokoll, Llojet. Në vetë paketimin tcp_server.go përmban strukturën e të dhënave Shërbej.
type Serve struct {
Port string
BufSize int
ST *types.Settings
}
Mund të pranojë parametrat e mëposhtëm:
- Porta e rrjetit përmes së cilës do të shkëmbehen të dhënat
- Skedari i konfigurimit të serverit në formatin JSON
- Flamuri për ekzekutimin në modalitetin e korrigjimit (blockchain privat)
Progresi:
- Lexon konfigurimin nga skedari JSON
- Flamuri i modalitetit të korrigjimit kontrollohet: nëse është vendosur, planifikuesi i sinkronizimit të rrjetit nuk niset dhe zinxhiri i bllokimit nuk është i ngarkuar
- Inicializimi i strukturës së të dhënave të konfigurimit dhe fillimi i serverit
server
- Kryen nisjen e serverit TCP dhe ndërveprimin e rrjetit në përputhje me protokollin.
- Ajo ka një strukturë të dhënash Serve që përbëhet nga një numër porti, një madhësi buferi dhe një tregues për strukturën llojet.Cilësimet
- Metoda Run fillon ndërveprimin e rrjetit (dëgjimi i lidhjeve hyrëse në një port të caktuar, kur merret një lidhje e re, përpunimi i saj transferohet në metodën e dorezës private në një thread të ri)
- В trajtuar të dhënat nga lidhja lexohen në një buffer, konvertohen në një paraqitje të vargut dhe kalohen në protokolli.Zgjedhja
- protokolli.Zgjedhja kthehet rezultat ose shkakton një gabim. rezultat më pas transferohet në protokolli.Interpretonie cila kthehet intrpr - objekt i llojit InterpreteData, ose shkakton një gabim në përpunimin e rezultatit të përzgjedhjes
- Pastaj kaloni ekzekutohet intrpr.Komanda[0] i cili kontrollon një nga: rezultat, inv, gabim dhe ka një seksion parazgjedhur
- Në seksionin rezultat kaloni gjendet sipas vlerës intrpr.Komanda[1] i cili kontrollon vlerat gjatësia e tamponit и version (në çdo rast thirret funksioni përkatës)
Funksionet GetVersion и Gjatësia e tamponit janë në dosje srvlib.go paketën e serverit
GetVersion(conn net.Conn, version string)
ai thjesht printon në tastierë dhe dërgon versionin e kaluar në parametrin te klienti:
conn.Write([]byte("result:" + version))
.
Funksion
BufferLength(conn net.Conn, intrpr *protocol.InterpreteData)
ngarkon një bllok, transaksion ose të dhëna të tjera specifike si më poshtë:
- Printon në tastierë llojin e të dhënave të specifikuara në protokoll që duhet të pranohen:
fmt.Println("DataType:", intrpr.Commands[2])
- Lexon vlerën intrpr.Trupi në një ndryshore numerike buf_len
- Krijon një tampon i ri madhësia e specifikuar:
make([]byte, buf_len)
- Dërgon një përgjigje në rregull:
conn.Write([]byte("result:ok"))
- Plotëson plotësisht buferin nga transmetimi i lexuar:
io.ReadFull(conn, newbuf)
.
- Printon përmbajtjen e tamponit në tastierë
fmt.Println(string(newbuf))
dhe numri i bajteve të lexuara
fmt.Println("Bytes length:", n)
- Dërgon një përgjigje në rregull:
conn.Write([]byte("result:ok"))
Metodat nga paketa e serverit janë konfiguruar për të përpunuar të dhënat e marra duke përdorur funksionet nga paketa protokoll.
Protokoll
Një protokoll shërben si një mjet që përfaqëson të dhënat në shkëmbimin e rrjetit.
Zgjedhja (string str) (string, gabim) kryen përpunimin parësor të të dhënave të marra nga serveri, merr një paraqitje të vargut të të dhënave si hyrje dhe kthen një varg të përgatitur për Përkthyes:
- Vargu i hyrjes ndahet në kokë dhe trup duke përdorur ReqParseN2(rr)
- koka ndahet në elementë dhe vendoset në një pjesë të komandave duke përdorur ReqParseHead(head)
- В ndërprerës (komandat[0]) zgjidhni komandën e marrë (cmd, çelësi, adresa ose seksioni është shkaktuar parazgjedhur)
- 2 komanda kontrollohen në cmd switch(komandat[1]) - gjatësia и getversion.
- gjatësi kontrollon llojin e të dhënave komandat[2] dhe e ruan atë brenda tipi
- Kontrollon atë trup përmban një vlerë vargu
len(body) < 1
- Kthen vargun e përgjigjes:
"result:bufferlength:" + datatype + "/" + body
- getversion kthen një varg
return "result:version/auto"
Përkthyes
Përmban strukturën InterpreteData dhe kryen përpunim dytësor të të dhënave të kthyera nga Zgjedhje vargjet dhe formimi i objekteve InterpreteData.
type InterpreteData struct {
Head string
Commands []string
Body string
IsErr bool
ErrCode int
ErrMessage string
}
Funksion
Interprete(str string) (*InterpreteData, error)
pranon një varg rezultat dhe krijon dhe kthen një referencë për objektin InterpreteData.
Progresi:
- Аналогично Zgjedhje koka dhe trupi nxirren duke përdorur ReqParseN2(rr)
- koka është e ndarë në elementë duke përdorur ReqParseHead(koka)
- Objekti është inicializuar InterpreteData dhe një tregues për të kthehet:
res := &InterpreteData{
Head: head,
Commands: commands,
Body: body,
}
return res, nil
Ky objekt përdoret në server.go kryesore e paketës.
klient
Paketa e klientit përmban funksionet TCPConnect и TCPRsponseData.
Funksion
TCPConnect(s *types.Settings, data []byte, payload []byte)
funksionon si më poshtë:
- Bëhet një lidhje me lidhjen e specifikuar në objektin e cilësimeve të kaluara
net.Dial("tcp", s.Host + ":" + s.Port)
- Të dhënat e kaluara në parametrin e të dhënave transmetohen:
conn.Write(data)
- Përgjigja lexohet
resp, n, _ := TCPResponseData(conn, s.BufSize)
dhe të printuara në tastierë
fmt.Println(string(resp[:n]))
- Nëse transferohet payload pastaj e kalon atë
conn.Write(payload)
dhe gjithashtu lexon përgjigjen e serverit, duke e printuar atë në tastierë
Funksion
TCPResponseData(conn net.Conn, bufsiz int) ([]byte, int, error)
krijon një buffer të madhësisë së specifikuar, lexon përgjigjen e serverit atje dhe kthen këtë buffer dhe numrin e bajteve të lexuara, si dhe një objekt gabimi.
Nënprogrami i klientit
Shërben për të dërguar komanda në serverët e nyjeve, si dhe për të marrë statistika dhe testime të shkurtra.
Mund të pranojë parametrat e mëposhtëm: skedari i konfigurimit në formatin JSON, të dhënat që do të dërgohen në server si një varg, shtegu drejt skedarit që do të dërgohet në ngarkesë, flamuri i emulimit të programuesit të nyjeve, lloji i të dhënave të transferuara si vlerë numerike.
- Marrja e konfigurimit
st := types.ParseConfig(*config)
- Nëse kalohet flamuri emu, ai fillon sheduler
- Nëse jepet flamuri f që tregon rrugën drejt skedarit, atëherë ne i ngarkojmë të dhënat e tij fdb dhe përmbajtja dërgohet në server
client.TCPConnect(st, []byte(CMD_BUFFER_LENGTH + ":" + strconv.Itoa(*t) + "/" + strconv.Itoa(fdblen)), fdb)
- Nëse skedari nuk është i specifikuar, atëherë të dhënat nga flamuri thjesht dërgohen -d:
client.TCPConnect(st, []byte(*data), nil)
E gjithë kjo është një paraqitje e thjeshtuar që tregon strukturën e protokollit. Gjatë zhvillimit, strukturës së tij i shtohet funksionaliteti i nevojshëm.
Në pjesën e dytë do të flas për strukturat e të dhënave për blloqet dhe transaksionet, në 3 për serverin WebSocket për lidhje nga JavaScript, në 4 do të shikoj planifikuesin e sinkronizimit, më pas një makineri stack që përpunon bytekodin nga hyrjet dhe daljet, kriptografinë dhe pishina për rezultate.
Burimi: www.habr.com