code upload
This commit is contained in:
commit
8291b8b6a8
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
go.sum
|
||||
traceroute_dynamicIngest.txt
|
||||
traceroute_region.txt
|
||||
vrcdn-nettest.exe
|
15
go.mod
Normal file
15
go.mod
Normal file
@ -0,0 +1,15 @@
|
||||
module sticksdev/vrcdn-nettest
|
||||
|
||||
go 1.21.6
|
||||
|
||||
require (
|
||||
github.com/buger/goterm v1.0.4 // indirect
|
||||
github.com/fatih/color v1.16.0 // indirect
|
||||
github.com/google/uuid v1.5.0 // indirect
|
||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/prometheus-community/pro-bing v0.3.0 // indirect
|
||||
golang.org/x/net v0.20.0 // indirect
|
||||
golang.org/x/sync v0.6.0 // indirect
|
||||
golang.org/x/sys v0.16.0 // indirect
|
||||
)
|
513
main.go
Normal file
513
main.go
Normal file
@ -0,0 +1,513 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"os/signal"
|
||||
"runtime"
|
||||
"time"
|
||||
|
||||
probing "github.com/prometheus-community/pro-bing"
|
||||
)
|
||||
|
||||
// Setup Color Palette
|
||||
const (
|
||||
Black = "\033[1;30m"
|
||||
Red = "\033[1;31m"
|
||||
Green = "\033[1;32m"
|
||||
Yellow = "\033[1;33m"
|
||||
Blue = "\033[1;34m"
|
||||
Magenta = "\033[1;35m"
|
||||
Cyan = "\033[1;36m"
|
||||
White = "\033[1;37m"
|
||||
Reset = "\033[0m"
|
||||
)
|
||||
|
||||
// Regions
|
||||
var regions = []string{
|
||||
"Europe",
|
||||
"Americas",
|
||||
"Asia",
|
||||
"Australia",
|
||||
}
|
||||
|
||||
// URLs for each region. Key is the region name, value is a array of objects with the following keys:
|
||||
// - url: URL to test
|
||||
// - url_friendlyname: Friendly name for the URL. Used in the output.
|
||||
|
||||
var urls = map[string][]map[string]string{
|
||||
"Europe": {
|
||||
{
|
||||
"url": "uk.ingest.vrcdn.live",
|
||||
"url_friendlyname": "UK Ingest (England)",
|
||||
},
|
||||
{
|
||||
"url": "de.ingest.vrcdn.live",
|
||||
"url_friendlyname": "DE Ingest (Germany)",
|
||||
},
|
||||
},
|
||||
"Americas": {
|
||||
{
|
||||
"url": "use.ingest.vrcdn.live",
|
||||
"url_friendlyname": "USE Ingest (United States East)",
|
||||
},
|
||||
{
|
||||
"url": "usc.ingest.vrcdn.live",
|
||||
"url_friendlyname": "USC Ingest (United States Central)",
|
||||
},
|
||||
{
|
||||
"url": "usw.ingest.vrcdn.live",
|
||||
"url_friendlyname": "USW Ingest (United States West)",
|
||||
},
|
||||
},
|
||||
"Asia": {
|
||||
{
|
||||
"url": "jpe.ingest.vrcdn.live",
|
||||
"url_friendlyname": "JPE Ingest (Japan East)",
|
||||
},
|
||||
{
|
||||
"url": "jpw.ingest.vrcdn.live",
|
||||
"url_friendlyname": "JPW Ingest (Japan West)",
|
||||
},
|
||||
},
|
||||
"Australia": {
|
||||
{
|
||||
"url": "au.ingest.vrcdn.live",
|
||||
"url_friendlyname": "AUS Ingest (Sydney)",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
// Define our structs
|
||||
type dynamicIngestResult struct {
|
||||
packetsTransmitted int
|
||||
packetsReceived int
|
||||
packetLoss float64
|
||||
minPing float64
|
||||
maxPing float64
|
||||
avgPing float64
|
||||
}
|
||||
|
||||
type regionResult struct {
|
||||
region string
|
||||
areaName string
|
||||
packetsTransmitted int
|
||||
packetsReceived int
|
||||
packetLoss float64
|
||||
minPing float64
|
||||
maxPing float64
|
||||
avgPing float64
|
||||
}
|
||||
|
||||
func testRegionPing(region string) []regionResult {
|
||||
// Get URLs for the region.
|
||||
regionUrls := urls[region]
|
||||
|
||||
// Make the results array for the regions, using the regionRequest struct.
|
||||
var regionResults []regionResult = make([]regionResult, len(regionUrls))
|
||||
|
||||
// Print region name.
|
||||
fmt.Printf("%s[ping_test] Testing region %s...%s\n", Yellow, region, Reset)
|
||||
|
||||
// Test each URL.
|
||||
for _, url := range regionUrls {
|
||||
fmt.Printf("[ping_test] Testing endpoint %s in region %s with sample rate 10\n", url["url_friendlyname"], region)
|
||||
|
||||
pinger, err := probing.NewPinger(url["url"])
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// Listen for Ctrl-C.
|
||||
c := make(chan os.Signal, 1)
|
||||
signal.Notify(c, os.Interrupt)
|
||||
go func() {
|
||||
for range c {
|
||||
pinger.Stop()
|
||||
fmt.Printf("\n%s[ping_test] Stopped.%s\n", Red, Reset)
|
||||
os.Exit(0)
|
||||
}
|
||||
}()
|
||||
|
||||
pinger.Count = 10
|
||||
pinger.Interval = 100 * time.Millisecond
|
||||
pinger.Timeout = 1 * time.Second
|
||||
|
||||
pinger.OnSend = func(pkt *probing.Packet) {
|
||||
// Increment the packetsTransmitted counter.
|
||||
regionResults[len(regionResults)-1].packetsTransmitted++
|
||||
}
|
||||
|
||||
pinger.OnRecv = func(pkt *probing.Packet) {
|
||||
// Increment the packetsReceived counter.
|
||||
regionResults[len(regionResults)-1].packetsReceived++
|
||||
}
|
||||
|
||||
pinger.OnFinish = func(stats *probing.Statistics) {
|
||||
// Add our results to the regionResults array.
|
||||
regionResults = append(regionResults, regionResult{
|
||||
region: region,
|
||||
areaName: url["url_friendlyname"],
|
||||
packetsTransmitted: regionResults[len(regionResults)-1].packetsTransmitted,
|
||||
packetsReceived: regionResults[len(regionResults)-1].packetsReceived,
|
||||
packetLoss: stats.PacketLoss,
|
||||
minPing: stats.MinRtt.Seconds() * 1000,
|
||||
maxPing: stats.MaxRtt.Seconds() * 1000,
|
||||
avgPing: stats.AvgRtt.Seconds() * 1000,
|
||||
})
|
||||
}
|
||||
|
||||
// If we are in windows, we need to call pinger.SetPrivileged(true) to use ICMP.
|
||||
if runtime.GOOS == "windows" {
|
||||
pinger.SetPrivileged(true)
|
||||
}
|
||||
|
||||
err = pinger.Run()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
return regionResults
|
||||
}
|
||||
|
||||
func testDynamicIngestPing() dynamicIngestResult {
|
||||
// Make a new pinger for ingest.vrcdn.live.
|
||||
pinger, err := probing.NewPinger("ingest.vrcdn.live")
|
||||
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// Listen for Ctrl-C.
|
||||
c := make(chan os.Signal, 1)
|
||||
signal.Notify(c, os.Interrupt)
|
||||
go func() {
|
||||
for range c {
|
||||
pinger.Stop()
|
||||
fmt.Printf("\n%s[ping_test:dynamicIngestTest] Stopped.%s\n", Red, Reset)
|
||||
os.Exit(0)
|
||||
}
|
||||
}()
|
||||
|
||||
pinger.Count = 10
|
||||
pinger.Interval = 100 * time.Millisecond
|
||||
pinger.Timeout = 1 * time.Second
|
||||
|
||||
// Create a empty dynamicIngestResult struct.
|
||||
var dynamicIngestResultData dynamicIngestResult
|
||||
|
||||
pinger.OnSend = func(pkt *probing.Packet) {
|
||||
// Increment the packetsTransmitted counter.
|
||||
dynamicIngestResultData.packetsTransmitted++
|
||||
}
|
||||
|
||||
pinger.OnRecv = func(pkt *probing.Packet) {
|
||||
// Increment the packetsReceived counter.
|
||||
dynamicIngestResultData.packetsReceived++
|
||||
}
|
||||
|
||||
pinger.OnFinish = func(stats *probing.Statistics) {
|
||||
// Print results.
|
||||
fmt.Printf("%s[ping_test:dynamicIngestTest] %d/%d packets received (%.2f%% loss), min/avg/max = %.2f/%.2f/%.2f ms.%s\n", Cyan, stats.PacketsRecv, stats.PacketsSent, stats.PacketLoss, stats.MinRtt.Seconds()*1000, stats.AvgRtt.Seconds()*1000, stats.MaxRtt.Seconds()*1000, Reset)
|
||||
|
||||
// Add our results to the regionResults array.
|
||||
dynamicIngestResultData = dynamicIngestResult{
|
||||
packetsTransmitted: dynamicIngestResultData.packetsTransmitted,
|
||||
packetsReceived: dynamicIngestResultData.packetsReceived,
|
||||
packetLoss: stats.PacketLoss,
|
||||
minPing: stats.MinRtt.Seconds() * 1000,
|
||||
maxPing: stats.MaxRtt.Seconds() * 1000,
|
||||
avgPing: stats.AvgRtt.Seconds() * 1000,
|
||||
}
|
||||
}
|
||||
|
||||
// If we are in windows, we need to call pinger.SetPrivileged(true) to use ICMP.
|
||||
if runtime.GOOS == "windows" {
|
||||
pinger.SetPrivileged(true)
|
||||
}
|
||||
|
||||
err = pinger.Run()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return dynamicIngestResultData
|
||||
}
|
||||
|
||||
func Traceroute(mode string, region string) []string {
|
||||
// Figure out the binary we need to use based on the OS.
|
||||
var binary string
|
||||
if runtime.GOOS == "windows" {
|
||||
binary = "tracert"
|
||||
} else {
|
||||
binary = "traceroute"
|
||||
}
|
||||
|
||||
// Ensure that the binary exists.
|
||||
if _, err := exec.LookPath(binary); err != nil {
|
||||
fmt.Printf("%s[!] Test Failed: %s %s is not installed. Please install it and try again.\n", Red, Reset, binary)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
switch mode {
|
||||
case "region":
|
||||
// Get URLs for the region.
|
||||
regionUrls := urls[region]
|
||||
|
||||
// Make the results array for the regions, using the regionRequest struct.
|
||||
var regionResults []string = make([]string, len(regionUrls))
|
||||
|
||||
// Print region name.
|
||||
fmt.Printf("%s[traceroute_test] Testing region %s...%s\n", Yellow, region, Reset)
|
||||
|
||||
// Test each URL.
|
||||
for _, url := range regionUrls {
|
||||
fmt.Printf("[traceroute_test] Testing endpoint %s in region %s\n", url["url_friendlyname"], region)
|
||||
|
||||
// Run the traceroute command.
|
||||
out, err := exec.Command(binary, url["url"]).Output()
|
||||
|
||||
if err != nil {
|
||||
fmt.Printf("%s[!] Test Failed: %s %s failed to run. Please install it and try again.\n", Red, Reset, binary)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Add our results to the regionResults array.
|
||||
regionResults = append(regionResults, string(out))
|
||||
}
|
||||
|
||||
return regionResults
|
||||
case "dynamicIngest":
|
||||
// Run the traceroute command.
|
||||
out, err := exec.Command(binary, "ingest.vrcdn.live").Output()
|
||||
|
||||
if err != nil {
|
||||
fmt.Printf("%s[!] Test Failed: %s %s failed to run. Please install it and try again.\n", Red, Reset, binary)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
return []string{string(out)}
|
||||
}
|
||||
|
||||
return []string{}
|
||||
}
|
||||
|
||||
func printResults(regionResults []regionResult, dynamicIngestResultData dynamicIngestResult) {
|
||||
// Find the best area in our selected region.
|
||||
var bestArea regionResult = regionResult{
|
||||
avgPing: 9999999999,
|
||||
}
|
||||
|
||||
for _, regionResult := range regionResults {
|
||||
if regionResult.areaName == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
// Check if the regionResult is better than the best area.
|
||||
if regionResult.avgPing < bestArea.avgPing {
|
||||
bestArea = regionResult
|
||||
} else if regionResult.avgPing == bestArea.avgPing {
|
||||
// If the ping is the same, check if the packet loss is better.
|
||||
if regionResult.packetLoss < bestArea.packetLoss {
|
||||
bestArea = regionResult
|
||||
}
|
||||
} else {
|
||||
// If the ping is worse, skip it.
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
// Check if dynamic is better than the best area.
|
||||
if dynamicIngestResultData.avgPing < bestArea.avgPing {
|
||||
bestArea = regionResult{ // Reconstruct the bestArea struct with the dynamicIngestResultData.
|
||||
region: "dynamicIngest",
|
||||
areaName: "Dynamic Ingest",
|
||||
packetsTransmitted: dynamicIngestResultData.packetsTransmitted,
|
||||
packetsReceived: dynamicIngestResultData.packetsReceived,
|
||||
packetLoss: dynamicIngestResultData.packetLoss,
|
||||
minPing: dynamicIngestResultData.minPing,
|
||||
maxPing: dynamicIngestResultData.maxPing,
|
||||
avgPing: dynamicIngestResultData.avgPing,
|
||||
}
|
||||
}
|
||||
|
||||
// Print the results.
|
||||
// ping less then 100ms = green
|
||||
// ping between 100ms and 200ms = yellow
|
||||
// ping more then 200ms = red
|
||||
|
||||
bestAreaPingColor := Green
|
||||
if bestArea.avgPing > 100 && bestArea.avgPing < 200 {
|
||||
bestAreaPingColor = Yellow
|
||||
} else if bestArea.avgPing > 200 {
|
||||
bestAreaPingColor = Red
|
||||
}
|
||||
|
||||
fmt.Printf("Your best area in your region is %s%s%s with a average ping of %.2fms.%s\n", Green, bestArea.areaName, bestAreaPingColor, bestArea.avgPing, Reset)
|
||||
|
||||
// Print the results for each area in our region.
|
||||
for _, regionResult := range regionResults {
|
||||
// ping less then 100ms = green
|
||||
// ping between 100ms and 200ms = yellow
|
||||
// ping more then 200ms = red
|
||||
regionResultPingColor := Green
|
||||
if regionResult.avgPing > 100 && regionResult.avgPing < 200 {
|
||||
regionResultPingColor = Yellow
|
||||
} else if regionResult.avgPing > 200 {
|
||||
regionResultPingColor = Red
|
||||
}
|
||||
|
||||
// If no area name, continue.
|
||||
if regionResult.areaName == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
fmt.Printf("%s%s%s: %s%d/%d%s packets received (%s%.2f%%%s loss), min/avg/max = %s%.2f%s/%s%.2f%s/%s%.2f%s ms.%s\n", Cyan, regionResult.areaName, Reset, Cyan, regionResult.packetsReceived, regionResult.packetsTransmitted, Reset, Cyan, regionResult.packetLoss, Reset, Cyan, regionResult.minPing, Reset, regionResultPingColor, regionResult.avgPing, Reset, Cyan, regionResult.maxPing, Reset, Reset)
|
||||
}
|
||||
|
||||
// Print the results for dynamic ingest.
|
||||
// ping less then 100ms = green
|
||||
// ping between 100ms and 200ms = yellow
|
||||
// ping more then 200ms = red
|
||||
dynamicIngestResultPingColor := Green
|
||||
if dynamicIngestResultData.avgPing > 100 && dynamicIngestResultData.avgPing < 200 {
|
||||
dynamicIngestResultPingColor = Yellow
|
||||
} else if dynamicIngestResultData.avgPing > 200 {
|
||||
dynamicIngestResultPingColor = Red
|
||||
}
|
||||
|
||||
fmt.Printf("%sDynamic Ingest%s: %s%d/%d%s packets received (%s%.2f%%%s loss), min/avg/max = %s%.2f%s/%s%.2f%s/%s%.2f%s ms.%s\n", Cyan, Reset, Cyan, dynamicIngestResultData.packetsReceived, dynamicIngestResultData.packetsTransmitted, Reset, Cyan, dynamicIngestResultData.packetLoss, Reset, Cyan, dynamicIngestResultData.minPing, Reset, dynamicIngestResultPingColor, dynamicIngestResultData.avgPing, Reset, Cyan, dynamicIngestResultData.maxPing, Reset, Reset)
|
||||
|
||||
// Print the traceroute results.
|
||||
fmt.Printf("\nTraceroute results have been saved to traceroute_region.txt and traceroute_dynamicIngest.txt\n")
|
||||
|
||||
// Print the disclaimer.
|
||||
fmt.Printf("\nDisclaimer: This tool should not be used as a 100%% accurate way to determine your best region or a possible network issue. Please work with official VRCDN support to determine the cause of any issues you may be having.\n\n")
|
||||
|
||||
// Print warning about traceroute.
|
||||
fmt.Printf("%s%s%s\n", Yellow, "WARNING: Traceroute results contain SENSITIVE data. Please share with care!", Reset)
|
||||
}
|
||||
|
||||
func main() {
|
||||
fmt.Printf("VRCDN Network Test v1.0, built with %s\n", runtime.Version())
|
||||
fmt.Print("Created by sticksdev. This tool is not affiliated with VRCDN and is community-made. See LICENSE for more information.\n\n")
|
||||
|
||||
fmt.Print("This tool will test your connection to the VRCDN network. Please select your closest region.\n")
|
||||
|
||||
// Print regions.
|
||||
for i, region := range regions {
|
||||
fmt.Printf("%d. %s\n", i+1, region)
|
||||
}
|
||||
|
||||
// Get user input.
|
||||
var region int
|
||||
var errorInput error
|
||||
|
||||
// Keep asking for input until a valid region is selected.
|
||||
for {
|
||||
fmt.Print("Please enter the number of your region: ")
|
||||
fmt.Scan(®ion, &errorInput)
|
||||
|
||||
// If the input is valid, break out of the loop.
|
||||
if region > 0 && region <= len(regions) {
|
||||
break
|
||||
} else {
|
||||
fmt.Print("\nInvalid region selected. Please try again.")
|
||||
|
||||
// Sleep for .5 seconds
|
||||
time.Sleep(500 * time.Millisecond)
|
||||
}
|
||||
|
||||
if errorInput != nil {
|
||||
fmt.Print(Red + "[!]" + Reset + " a error occurred while reading your input. Please try again.\n")
|
||||
fmt.Print(errorInput.Error() + "\n")
|
||||
|
||||
// Sleep for .5 seconds
|
||||
time.Sleep(500 * time.Millisecond)
|
||||
}
|
||||
}
|
||||
|
||||
// Test the region.
|
||||
var regionResults []regionResult = testRegionPing(regions[region-1])
|
||||
|
||||
if len(regionResults) == 0 {
|
||||
fmt.Printf("%s[!]%s No results were returned from the previous test. Please try again.\n", Red, Reset)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
fmt.Printf("[ping_test] Running dynamic ingest test... (ingest.vrcdn.live)\n")
|
||||
var dynamicIngestResultData dynamicIngestResult = testDynamicIngestPing()
|
||||
|
||||
if dynamicIngestResultData == (dynamicIngestResult{}) {
|
||||
fmt.Printf("%s[!]%s No results were returned from the previous test. Please try again.\n", Red, Reset)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
var traceRegionResults []string = Traceroute("region", regions[region-1])
|
||||
|
||||
if len(traceRegionResults) == 0 {
|
||||
fmt.Printf("%s[!]%s No results were returned from the previous test. Please try again.\n", Red, Reset)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
fmt.Printf("[traceroute_test] Running dynamic ingest test... (ingest.vrcdn.live)\n")
|
||||
var traceDynamicIngestResults []string = Traceroute("dynamicIngest", "")
|
||||
|
||||
if len(traceDynamicIngestResults) == 0 {
|
||||
fmt.Printf("%s[!]%s No results were returned from the previous test. Please try again.\n", Red, Reset)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
fmt.Printf("%s[...]%s Saving traceroute results to traceroute_region.txt and traceroute_dynamicIngest.txt\n", Yellow, Reset)
|
||||
|
||||
// Save the traceroute results to a file.
|
||||
var traceRegionFile, traceDynamicIngestFile *os.File
|
||||
|
||||
// If we have old results, delete them.
|
||||
if _, err := os.Stat("traceroute_region.txt"); err == nil {
|
||||
os.Remove("traceroute_region.txt")
|
||||
}
|
||||
|
||||
if _, err := os.Stat("traceroute_dynamicIngest.txt"); err == nil {
|
||||
os.Remove("traceroute_dynamicIngest.txt")
|
||||
}
|
||||
|
||||
// Define error.
|
||||
var err error
|
||||
|
||||
traceRegionFile, err = os.OpenFile("traceroute_region.txt", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
|
||||
if err != nil {
|
||||
fmt.Printf("%s[!]%s Failed to open traceroute_region.txt. Please try again.\n", Red, Reset)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
traceDynamicIngestFile, err = os.OpenFile("traceroute_dynamicIngest.txt", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
|
||||
if err != nil {
|
||||
fmt.Printf("%s[!]%s Failed to open traceroute_dynamicIngest.txt. Please try again.\n", Red, Reset)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Write the results to the files.
|
||||
for _, traceRegionResult := range traceRegionResults {
|
||||
traceRegionFile.WriteString(traceRegionResult)
|
||||
}
|
||||
|
||||
for _, traceDynamicIngestResult := range traceDynamicIngestResults {
|
||||
traceDynamicIngestFile.WriteString(traceDynamicIngestResult)
|
||||
}
|
||||
|
||||
// Close the files.
|
||||
traceRegionFile.Close()
|
||||
traceDynamicIngestFile.Close()
|
||||
|
||||
fmt.Printf("%s[i]%s Save complete. Showing results!\n", Green, Reset)
|
||||
printResults(regionResults, dynamicIngestResultData)
|
||||
|
||||
// Sleep for 5 seconds.
|
||||
time.Sleep(5 * time.Second)
|
||||
|
||||
// Exit.
|
||||
os.Exit(0)
|
||||
}
|
31
readme.md
Normal file
31
readme.md
Normal file
@ -0,0 +1,31 @@
|
||||
# VRCDN Network Test
|
||||
|
||||
This is a simple tool to help you test your connection to the VRCDNs network. This can be useful to determine if you are having issues connecting to the VRCDN network.
|
||||
|
||||
## How does it work?
|
||||
|
||||
This tool will run two tests to test your connectivity to the VRCDN network. The first is a ping test, which will ping
|
||||
each of the VRCDN ingest nodes for your area and report the results. The second is traceroute, which will show you the
|
||||
path your connection takes to reach the VRCDN network.
|
||||
|
||||
Trace route is a useful tool to determine if you are having issues connecting to the VRCDN network. If you see a lot of
|
||||
packet loss or high latency in the trace route, this could indicate an issue with your connection to the VRCDN network.
|
||||
|
||||
## How do I use it?
|
||||
|
||||
You can download the latest release from the [releases page]() - or you can build it yourself.
|
||||
|
||||
Once you have the binary, you can simply double click it to run it. Results will be shown for 5 seconds, and then the
|
||||
program will exit. We recommend opening a command prompt and running the program from there so you can see the results
|
||||
after the program exits, or use one of our pre-built scripts to run the program for you.
|
||||
|
||||
A sample output from the program is shown below:
|
||||
|
||||

|
||||
|
||||
## Compiling from source
|
||||
|
||||
This project uses [Go](https://golang.org/) to compile. You can download Go from [here](https://golang.org/dl/). Once
|
||||
you have Go installed, you can compile the project by running `go build` in the root directory of the project. This will
|
||||
create a binary called `vrcdn-nettest` in the root directory of the project that is ready to run. Alternatively, you can
|
||||
run `go run .` to run the program without compiling it first.
|
Loading…
x
Reference in New Issue
Block a user