#Write json array to file
145 messages · Page 1 of 1 (latest)
How are you storing this in the code?
If it's just a slice of structs, just pass the whole slice to json.Marshal
If it's not a slice of structs, make it into a slice of structs :)
this is my code: ```go
type Response struct {
IP string json:"ip"
Timestamp string json:"timestamp"
Status string json:"status"
Last string json:"lastping"
}
func main() {
start := time.Now().UnixMilli()
fmt.Println("\n [ Start ] ")
content, err := ioutil.ReadFile("./servers.json")
if err != nil {
log.Fatal("Error when opening file: ", err)
}
var servers []struct {
IP string `json:"ip"`
Port int `json:"port"`
}
err = json.Unmarshal(content, &servers)
if err != nil {
log.Fatal("Error during Unmarshal(): ", err)
}
var pingout // idk what to put here
for i, data := range servers {
res, err := PerformPing(data.IP, data.Port)
if err != nil { fmt.Printf("[%d] (Failed) %s\n", i, err) }
jsonString, _ := json.Marshal(res)
pingout = append(pingout, jsonString)
}
final, _ := json.Marshal(pingout)
ioutil.WriteFile("ping_out.json", final, os.ModePerm)
fmt.Println("\n [ End ] ")
fmt.Printf("Took: %dms\n", time.Now().UnixMilli() - start)
}
var pingout []Response
oh fromi PerformPing i get the Response struct back
Cannot use 'jsonString' (type []byte) as the type Response
var pingout []Response
for {
pingout = append(pingout, res)
}
final, _ := json.Marshal(pingout)
i get a pointer from PerformPing
panic: runtime error: invalid memory address or nil pointer dereference [signal 0xc0000005 code=0x0 addr=0x0 pc=0x46e8ed]
nvm im stupid
the ping failed so ofc im getting nil
but im not skiping the rest of the loop
continue is your friend
i just forgot the continue after it fails
if err != nil { fmt.Printf("[%d -> %s] (Failed) %s\n", i, data.IP, err); continue }
can i somehow make json.Marshal format the json data it gives me?
like this
[
{
"some": "thing",
"another": "thing"
}
]
instead of this
[{"some":"thing","another":"thing"}]
MarshalIndent
oh ok
i have another question
the pinging takes a bit of time
can i somehow run multiple pings at the same time?
because like this, 11 pings take 30secs
and thats quite a long time
Yeah using goroutines and a waitgroup
https://go.dev/tour/concurrency/1
I recommend doing the go tour about this, lots of good examples
sounds interesting
also this: https://gobyexample.com/waitgroups
thank you for your help
i got a small problem
(i think)
nvm
forgot to add group.Wait()
i may have a problem
panic: runtime error: slice bounds out of range [-3:]
That's a panic, runtime error. yep, definitely looks like an error.
I bet some code caused this
it seems to be some library im using
goroutine 16 [running]:
github.com/dreamscached/minequery/v2.(*Pinger).ping17ReadStatusResponsePacketPayload(0xc0000564a0?, {0x22c6c08c278, 0xc007740268})
C:/Users/Sandbox/go/pkg/mod/github.com/dreamscached/minequery/[email protected]/ping_17.go:1841 +0x525
github.com/dreamscached/minequery/v2.(*Pinger).Ping17(0xc0000564a0, {0xc000361d72, 0xd}, 0x0?)
C:/Users/Sandbox/go/pkg/mod/github.com/dreamscached/minequery/[email protected]/ping_17.go:1757 +0x27b
github.com/dreamscached/minequery/v2.Ping17(...)
C:/Users/Sandbox/go/pkg/mod/github.com/dreamscached/minequery/[email protected]/ping_17.go:1731
main.main.func1({{0xc000361d72, 0xd}, {0xc000361d80, 0xa}, {0xc00036c100, 0x1, 0x4}}, 0x0?)
F:/programming/2022/go/server_pinger/main.go:53 +0xe6
created by main.main
F:/programming/2022/go/server_pinger/main.go:50 +0x608
exit status 2
Look at the line numbers to know what line caused
in my main.go file or in the library files?
Well first in your file
because the lines in my main.go file is in my PerformPing function
pingres, err := minequery.Ping17(data.IP, data.Port)
Looks like it's in the library, maybe it's not meant to handle concurency
Maybe you need a sperate pinger for each site, idk
Can't remote-debug it
the library doesnt use panic and my code doesnt eighter
It looks like something is trying to access a slice index that doesn't exist
Which causes a panic
this
the last line the error gives me is this: ```
pb.Write(lb[ln-lr.Len() : ln])
thats the line in the library
You can open an issue there
can i somehow "skip" the panic with something like a try {} except () {}?
There's recover not best practice
If this didn't happen when you were doing the requests 1 by 1, maybe the problem is with concurrency, did you try making a separate pinger for each site?
ye but i stopped after 10
if i run with the "threads" (idk what to call it) it works on smaller scale like 10 requests
but when i go trough my entire list it fails
fails?
with the panic from before
I dunno, can open issue at the library
Or try researching recover and hack something together
can i count how many times i recover from a panic?
You could do that (although it would be a bit hacky) by wrapping the function that panics in an anonymous function that defers a function that recovers and then, if the recover does not return nil, increments a counter using a closure
nevermind
its just a bad idea
i think using coroutines wasnt the best idea
you could most likely implement this, but you shuld find another solution if possible
because i get a lot of i/o timeouts, Couldnt establish connections and could not read response packets
Couldnt you just have a simple wrapper for what you are doing that converts panics to regular errors and then just write the code "normally"
uh can i print something and then remove that print in the next print call, like print("\rsomething", end="\r") in python?
im using a wrapper to ping servers
and i have my own function that just pings a server removes unwanted attributes from the returned data (or moves the wanted data into another struct) and returns that
then i put that into a slice and output that as json
so you pass the list of addressees to the library and then it panics?
thats not how that works
there is intrinsic panic
the library also may have imports
that can have panics
the line that panics is this one: https://github.com/dreamscached/MineQuery/blob/v2/ping_17.go#L1841
can you give a full stacktrace
// Read entire packet to a buffer
pb := bytes.NewBuffer(make([]byte, 0, pl))
pb.Write(lb[ln-lr.Len() : ln]) // < panic here
if _, err = io.CopyN(pb, reader, int64(pl)-int64(lr.Len())); err != nil {
return nil, err
one sec
panic: runtime error: slice bounds out of range [-1:]
goroutine 454 [running]:
github.com/dreamscached/minequery/v2.(*Pinger).ping17ReadStatusResponsePacketPayload(0xc0000564a0?, {0x2785faece38, 0xc0076cc518})
C:/Users/Sandbox/go/pkg/mod/github.com/dreamscached/minequery/[email protected]/ping_17.go:1841 +0x525
github.com/dreamscached/minequery/v2.(*Pinger).Ping17(0xc0000564a0, {0xc00045e1e0, 0xf}, 0x0?)
C:/Users/Sandbox/go/pkg/mod/github.com/dreamscached/minequery/[email protected]/ping_17.go:1757 +0x27b
github.com/dreamscached/minequery/v2.Ping17(...)
C:/Users/Sandbox/go/pkg/mod/github.com/dreamscached/minequery/[email protected]/ping_17.go:1731
main.main.func1({{0xc00045e1e0, 0xf}, {0xc00045e1f0, 0xa}, {0xc00045d100, 0x1, 0x4}}, 0x0?)
F:/programming/2022/go/server_pinger/main.go:53 +0xe6
created by main.main
F:/programming/2022/go/server_pinger/main.go:50 +0x608
exit status 2
i am pretty confident this is simply a bug in the library
this looks like a lack of fault tolerance
Apprently in this case 'ln' is zero while 'lr' is 1, which seems like a case the author simlpy missed
leading to this weird negative index slicing operation
This is extremely weird though, since the buffer at the beginning 'lb' is a constant size 5 buffer, therefore the implementation of the concrete io.Reader used here must be wrong. Since a read from an io.Reader that did not return an error should return the correct number of elements read, the number of elements read is clearly zero here though, which should not be possible
Are you familiar with dlv by any chance?
no, im new to go
Can you explain how you are using goroutines?
can you give me an example of what goroutines are (i suck at remembering names of things)
they allow you to aynchronously run some functions using the go keyword
oh so go func right?
can you maybe post your current source?
yeah that would be an anonymous function running in a goroutine
if yes, ```go
go func(data Data, i int) {
// my getting daat code
}(data, i)
this: ```go
go func(data Data, i int) {
defer wg.Done()
res, err := PerformPing(data.IP, data.Ports[0].Port)
if err != nil { fmt.Printf("[%d -> %s] (Failed) %s\n", i, data.IP, err); return }
pingout = append(pingout, *res)
}(data, i)
It would be easier if you posted your source
k: ```go
package main
type Data struct {
IP string json:"ip"
Timestamp string json:"timestamp"
Ports []struct {
Port int json:"port"
Proto string json:"proto"
Status string json:"status"
Reason string json:"reason"
TTL int json:"ttl"
} json:"ports"
}
type Result struct {
IP string
VersionName string
ProtocolVersion int
OnlinePlayers int
MaxPlayers int
Description Chat17 // type Chat17 interface{} from minequery
Icon image.Image
}
func PerformPing(IP string, Port int) Result {
pingres := minequery.Ping17(IP, Port)
res := Reuslt {
IP,
pingres.VersionName,
pingres.ProtocolVersion,
pingres.OnlinePlayers,
pingres.MaxPlayers,
pingres.Description,
pingres.Icon,
}
return res
}
func main() {
start := time.Now().UnixMilli()
fmt.Println("\n [ Start ] ")
content, err := ioutil.ReadFile("./server_dump.json")
if err != nil {
log.Fatal("Error when opening file: ", err)
}
var payload []Data
err = json.Unmarshal(content, &payload)
if err != nil {
log.Fatal("Error during Unmarshal(): ", err)
}
var wg sync.WaitGroup
var pingout []Result
for i, data := range payload {
wg.Add(1)
go func(data Data, i int) {
defer wg.Done()
res, err := PerformPing(data.IP, data.Ports[0].Port)
if err != nil { fmt.Printf("[%d -> %s] (Failed) %s\n", i, data.IP, err); return }
pingout = append(pingout, *res)
}(data, i)
}
wg.Wait()
final, _ := json.MarshalIndent(pingout, "", " ")
err = ioutil.WriteFile("ping_out.json", final, os.ModePerm)
if err != nil { fmt.Printf("(Error -> ioutil.WriteFile) %s\n", err) }
fmt.Println("\n [ End ] ")
fmt.Printf("Took: %dms\n", time.Now().UnixMilli() - start)
}
(removed the imports because the messave would've been too long)
(also the code is really bad)
alright, could you start by just removing the goroutines
it takes 30secs for 5pings if i remove them (thats why i had them)
but without them there is no panic
alright then the library is not thread safe
i assume that io.Reader they were using has a threading bug
i got some (probably really really bad) thing from your first suggestion, that kinda works
var panics int
func wrapAndCountPanics(f func()) {
defer func() {
if r := recover(); r != nil {
panics++
}
}()
f()
}
// ...
// for
go wrapAndCountPanics(func() {
defer wg.Done()
res, err := minequery.Ping17(data.IP, data.Ports[0].Port)
if err != nil { fmt.Printf("[%d -> %s] (Failed) %s\n", i, data.IP, err); return }
pingout = append(pingout, *res)
})
if panics > 5 {
panic("More than 5 panics\nexiting") // never gets called
}
i would strongly recommend not using this
there are never more than 5 panics
i think its probably just the one
you should look for a different library that is thread safe
if i print the panics variable at the end, it gives me 1
so i think its just some weird server response
i dont think so, this will most likely give you wrong outputs
why?
the reader panics are most likely not because of a response but because of threading issues
but if it only happens once i can just ignore it, right?
because im checking ca 2.5k servers
i am sorry but i dont have the time to explain multithreading right now
ok