initial working version

main
Ben Grewell 4 years ago
parent e8dc386ba0
commit 10cf19535e

34
.gitignore vendored

@ -1,18 +1,18 @@
# Binaries for programs and plugins
*.exe
*.exe~
*.dll
*.so
*.dylib
# Test binary, built with `go test -c`
*.test
# Output of the go coverage tool, specifically when used with LiteIDE
*.out
# Dependency directories (remove the comment below to include it)
# vendor/
# Ignore the embedded directory
# Binaries for programs and plugins
*.exe
*.exe~
*.dll
*.so
*.dylib
# Test binary, built with `go test -c`
*.test
# Output of the go coverage tool, specifically when used with LiteIDE
*.out
# Dependency directories (remove the comment below to include it)
# vendor/
# Ignore the embedded directory
embedded/

@ -1,107 +1,131 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ChangeListManager">
<list default="true" id="fc2840de-29dc-4fca-8e0e-a283562f60ca" name="Default Changelist" comment="">
<change afterPath="$PROJECT_DIR$/client.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/execute.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/protocol.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/server.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/shared.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/README.md" beforeDir="false" afterPath="$PROJECT_DIR$/README.md" afterDir="false" />
<change beforePath="$PROJECT_DIR$/bindata.go" beforeDir="false" afterPath="$PROJECT_DIR$/bindata.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/cmd/main.go" beforeDir="false" afterPath="$PROJECT_DIR$/cmd/main.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/go.mod" beforeDir="false" afterPath="$PROJECT_DIR$/go.mod" afterDir="false" />
<change beforePath="$PROJECT_DIR$/iperf.go" beforeDir="false" afterPath="$PROJECT_DIR$/iperf.go" afterDir="false" />
</list>
<option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" />
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
<option name="LAST_RESOLUTION" value="IGNORE" />
</component>
<component name="FileTemplateManagerImpl">
<option name="RECENT_TEMPLATES">
<list>
<option value="Go Application" />
<option value="Go File" />
</list>
</option>
</component>
<component name="GOROOT" path="C:\Go" />
<component name="Git.Settings">
<option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" />
</component>
<component name="GoLibraries">
<option name="indexEntireGoPath" value="false" />
</component>
<component name="ProjectId" id="1lhjlU9mkZIWjchkp4HkX1szrI1" />
<component name="ProjectViewState">
<option name="hideEmptyMiddlePackages" value="true" />
<option name="showLibraryContents" value="true" />
</component>
<component name="PropertiesComponent">
<property name="DefaultGoTemplateProperty" value="Go File" />
<property name="RunOnceActivity.OpenProjectViewOnStart" value="true" />
<property name="WebServerToolWindowFactoryState" value="false" />
<property name="go.import.settings.migrated" value="true" />
<property name="go.sdk.automatically.set" value="true" />
<property name="go.tried.to.enable.integration.vgo.integrator" value="true" />
<property name="last_opened_file_path" value="$PROJECT_DIR$/embedded" />
<property name="nodejs_interpreter_path.stuck_in_default_project" value="undefined stuck path" />
<property name="nodejs_npm_path_reset_for_default_project" value="true" />
</component>
<component name="RecentsManager">
<key name="CopyFile.RECENT_KEYS">
<recent name="C:\Users\BGrewell\repos\go-iperf\embedded" />
</key>
</component>
<component name="RunManager">
<configuration name="go build github.com/BGrewell/go-iperf/cmd" type="GoApplicationRunConfiguration" factoryName="Go Application" temporary="true" nameIsGenerated="true">
<module name="go-iperf" />
<working_directory value="$PROJECT_DIR$" />
<kind value="PACKAGE" />
<filePath value="$PROJECT_DIR$/cmd/main.go" />
<package value="github.com/BGrewell/go-iperf/cmd" />
<directory value="$PROJECT_DIR$" />
<method v="2" />
</configuration>
<recent_temporary>
<list>
<item itemvalue="Go Build.go build github.com/BGrewell/go-iperf/cmd" />
</list>
</recent_temporary>
</component>
<component name="SpellCheckerSettings" RuntimeDictionaries="0" Folders="0" CustomDictionaries="0" DefaultDictionary="application-level" UseSingleDictionary="true" transferred="true" />
<component name="TypeScriptGeneratedFilesManager">
<option name="version" value="3" />
</component>
<component name="VgoProject">
<integration-enabled>true</integration-enabled>
</component>
<component name="WindowStateProjectService">
<state x="652" y="533" key="#Go_Modules" timestamp="1608060904277">
<screen x="0" y="0" width="3440" height="1400" />
</state>
<state x="652" y="533" key="#Go_Modules/0.0.3440.1400@0.0.3440.1400" timestamp="1608060904277" />
<state width="1676" height="403" key="GridCell.Tab.0.bottom" timestamp="1608066744405">
<screen x="0" y="0" width="3440" height="1400" />
</state>
<state width="1676" height="403" key="GridCell.Tab.0.bottom/0.0.3440.1400@0.0.3440.1400" timestamp="1608066744405" />
<state width="1676" height="403" key="GridCell.Tab.0.center" timestamp="1608066744404">
<screen x="0" y="0" width="3440" height="1400" />
</state>
<state width="1676" height="403" key="GridCell.Tab.0.center/0.0.3440.1400@0.0.3440.1400" timestamp="1608066744404" />
<state width="1676" height="403" key="GridCell.Tab.0.left" timestamp="1608066744404">
<screen x="0" y="0" width="3440" height="1400" />
</state>
<state width="1676" height="403" key="GridCell.Tab.0.left/0.0.3440.1400@0.0.3440.1400" timestamp="1608066744404" />
<state width="1676" height="403" key="GridCell.Tab.0.right" timestamp="1608066744404">
<screen x="0" y="0" width="3440" height="1400" />
</state>
<state width="1676" height="403" key="GridCell.Tab.0.right/0.0.3440.1400@0.0.3440.1400" timestamp="1608066744404" />
<state x="431" y="297" key="com.intellij.openapi.editor.actions.MultiplePasteAction$ClipboardContentChooser" timestamp="1608062415393">
<screen x="0" y="0" width="3440" height="1400" />
</state>
<state x="431" y="297" key="com.intellij.openapi.editor.actions.MultiplePasteAction$ClipboardContentChooser/0.0.3440.1400@0.0.3440.1400" timestamp="1608062415393" />
</component>
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ChangeListManager">
<list default="true" id="fc2840de-29dc-4fca-8e0e-a283562f60ca" name="Default Changelist" comment="">
<change afterPath="$PROJECT_DIR$/reporter.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/bindata.go" beforeDir="false" afterPath="$PROJECT_DIR$/bindata.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/client.go" beforeDir="false" afterPath="$PROJECT_DIR$/client.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/cmd/main.go" beforeDir="false" afterPath="$PROJECT_DIR$/cmd/main.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/execute.go" beforeDir="false" afterPath="$PROJECT_DIR$/execute.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/go.mod" beforeDir="false" afterPath="$PROJECT_DIR$/go.mod" afterDir="false" />
<change beforePath="$PROJECT_DIR$/go.sum" beforeDir="false" afterPath="$PROJECT_DIR$/go.sum" afterDir="false" />
<change beforePath="$PROJECT_DIR$/iperf.go" beforeDir="false" afterPath="$PROJECT_DIR$/iperf.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/protocol.go" beforeDir="false" afterPath="$PROJECT_DIR$/protocol.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/report.go" beforeDir="false" afterPath="$PROJECT_DIR$/report.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/sample.json" beforeDir="false" afterPath="$PROJECT_DIR$/sample.json" afterDir="false" />
<change beforePath="$PROJECT_DIR$/server.go" beforeDir="false" afterPath="$PROJECT_DIR$/server.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/shared.go" beforeDir="false" afterPath="$PROJECT_DIR$/shared.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/tests/client/client.go" beforeDir="false" afterPath="$PROJECT_DIR$/tests/client/client.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/tests/server/server.go" beforeDir="false" afterPath="$PROJECT_DIR$/tests/server/server.go" afterDir="false" />
</list>
<option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" />
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
<option name="LAST_RESOLUTION" value="IGNORE" />
</component>
<component name="FileTemplateManagerImpl">
<option name="RECENT_TEMPLATES">
<list>
<option value="Go Application" />
<option value="Go File" />
</list>
</option>
</component>
<component name="GOROOT" path="C:\Go" />
<component name="Git.Settings">
<option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" />
</component>
<component name="GoLibraries">
<option name="indexEntireGoPath" value="false" />
</component>
<component name="ProjectId" id="1lhjlU9mkZIWjchkp4HkX1szrI1" />
<component name="ProjectViewState">
<option name="hideEmptyMiddlePackages" value="true" />
<option name="showLibraryContents" value="true" />
</component>
<component name="PropertiesComponent">
<property name="DefaultGoTemplateProperty" value="Go File" />
<property name="RunOnceActivity.OpenProjectViewOnStart" value="true" />
<property name="RunOnceActivity.ShowReadmeOnStart" value="true" />
<property name="WebServerToolWindowFactoryState" value="false" />
<property name="go.import.settings.migrated" value="true" />
<property name="go.sdk.automatically.set" value="true" />
<property name="go.tried.to.enable.integration.vgo.integrator" value="true" />
<property name="last_opened_file_path" value="$PROJECT_DIR$" />
<property name="nodejs_interpreter_path.stuck_in_default_project" value="undefined stuck path" />
<property name="nodejs_npm_path_reset_for_default_project" value="true" />
</component>
<component name="RecentsManager">
<key name="CopyFile.RECENT_KEYS">
<recent name="C:\Users\BGrewell\repos\go-iperf\embedded" />
</key>
<key name="MoveFile.RECENT_KEYS">
<recent name="C:\Users\BGrewell\source\repos\go-iperf\tests\server" />
</key>
</component>
<component name="RunManager" selected="Go Build.go build github.com/BGrewell/go-iperf/tests/client">
<configuration name="go build github.com/BGrewell/go-iperf/cmd" type="GoApplicationRunConfiguration" factoryName="Go Application" temporary="true" nameIsGenerated="true">
<module name="go-iperf" />
<working_directory value="$PROJECT_DIR$" />
<kind value="PACKAGE" />
<filePath value="$PROJECT_DIR$/cmd/main.go" />
<package value="github.com/BGrewell/go-iperf/cmd" />
<directory value="$PROJECT_DIR$" />
<method v="2" />
</configuration>
<configuration name="go build github.com/BGrewell/go-iperf/tests/client" type="GoApplicationRunConfiguration" factoryName="Go Application" temporary="true" nameIsGenerated="true">
<module name="go-iperf" />
<working_directory value="$PROJECT_DIR$" />
<kind value="PACKAGE" />
<filePath value="$PROJECT_DIR$/tests/client/client.go" />
<package value="github.com/BGrewell/go-iperf/tests/client" />
<directory value="$PROJECT_DIR$" />
<method v="2" />
</configuration>
<configuration name="go build github.com/BGrewell/go-iperf/tests/server" type="GoApplicationRunConfiguration" factoryName="Go Application" temporary="true" nameIsGenerated="true">
<module name="go-iperf" />
<working_directory value="$PROJECT_DIR$" />
<kind value="PACKAGE" />
<filePath value="$PROJECT_DIR$/tests/server/server.go" />
<package value="github.com/BGrewell/go-iperf/tests/server" />
<directory value="$PROJECT_DIR$" />
<method v="2" />
</configuration>
<recent_temporary>
<list>
<item itemvalue="Go Build.go build github.com/BGrewell/go-iperf/tests/client" />
<item itemvalue="Go Build.go build github.com/BGrewell/go-iperf/tests/server" />
<item itemvalue="Go Build.go build github.com/BGrewell/go-iperf/cmd" />
</list>
</recent_temporary>
</component>
<component name="SpellCheckerSettings" RuntimeDictionaries="0" Folders="0" CustomDictionaries="0" DefaultDictionary="application-level" UseSingleDictionary="true" transferred="true" />
<component name="TaskManager">
<task active="true" id="Default" summary="Default task">
<changelist id="fc2840de-29dc-4fca-8e0e-a283562f60ca" name="Default Changelist" comment="" />
<created>1614708953320</created>
<option name="number" value="Default" />
<option name="presentableId" value="Default" />
<updated>1614708953320</updated>
</task>
<servers />
</component>
<component name="TypeScriptGeneratedFilesManager">
<option name="version" value="3" />
</component>
<component name="VgoProject">
<integration-enabled>true</integration-enabled>
</component>
<component name="XDebuggerManager">
<breakpoint-manager>
<breakpoints>
<line-breakpoint enabled="true" type="DlvLineBreakpoint">
<url>file://$PROJECT_DIR$/tests/client/client.go</url>
<line>57</line>
<option name="timeStamp" value="5" />
</line-breakpoint>
</breakpoints>
</breakpoint-manager>
</component>
</project>

@ -1,25 +1,25 @@
BSD 2-Clause License
Copyright (c) 2020, Ben Grewell
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
BSD 2-Clause License
Copyright (c) 2020, Ben Grewell
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

@ -1,6 +1,6 @@
# go-iperf
A Go based wrapper around iperf3
```
go-bindata -pkg iperf -prefix "embedded/" embedded/```
# go-iperf
A Go based wrapper around iperf3
```
go-bindata -pkg iperf -prefix "embedded/" embedded/```

@ -1,43 +1,491 @@
package iperf
import "github.com/google/uuid"
import (
"context"
"encoding/json"
"errors"
"fmt"
"github.com/google/uuid"
"io"
"io/ioutil"
"log"
"os"
"strings"
)
func NewClient() *Client {
func NewClient(host string) *Client {
json := true
proto := Protocol(PROTO_TCP)
time := 10
length := "128KB"
streams := 1
c := &Client{
JSON: &json,
Proto: &proto,
TimeSec: &time,
Length: &length,
Streams: &streams,
options: &ClientOptions{
JSON: &json,
Proto: &proto,
TimeSec: &time,
Length: &length,
Streams: &streams,
Host: &host,
},
}
c.Id = uuid.New().String()
c.Done = make(chan bool)
return c
}
type Client struct {
type ClientOptions struct {
SharedOptions
Host string
Proto *Protocol
Bandwidth *string
TimeSec *int
Bytes *string
BlockCount *string
Length *string
Streams *int
Reverse *bool
Window *string
MSS *int
NoDelay *bool
Version4 *bool
Version6 *bool
TOS *int
ZeroCopy *bool
OmitSec *int
Prefix *string
JSON *bool
Host *string
Proto *Protocol
Bandwidth *string
TimeSec *int
Bytes *string
BlockCount *string
Length *string
Streams *int
Reverse *bool
Window *string
MSS *int
NoDelay *bool
Version4 *bool
Version6 *bool
TOS *int
ZeroCopy *bool
OmitSec *int
Prefix *string
JSON *bool
LogFile *string
IncludeServer *bool
}
type Client struct {
Id string
Running bool
Done chan bool
options *ClientOptions
exitCode *int
report *TestReport
outputStream io.ReadCloser
errorStream io.ReadCloser
cancel context.CancelFunc
mode TestMode
live bool
reportingChan chan *StreamIntervalReport
reportingFile string
}
func (c *Client) LoadOptionsJSON(jsonStr string) (err error) {
return json.Unmarshal([]byte(jsonStr), c.options)
}
func (c *Client) commandString() (cmd string, err error) {
builder := strings.Builder{}
if c.options.Host == nil || *c.options.Host == "" {
return "", errors.New("unable to execute client. The field 'host' is required")
}
fmt.Fprintf(&builder, "%s -c %s", binaryLocation, c.Host())
if c.options.Proto != nil && *c.options.Proto == PROTO_UDP {
fmt.Fprintf(&builder, " -u")
}
if c.options.Bandwidth != nil {
fmt.Fprintf(&builder, " -b %s", c.Bandwidth())
}
if c.options.TimeSec != nil {
fmt.Fprintf(&builder, " -t %d", c.TimeSec())
}
if c.options.Bytes != nil {
fmt.Fprintf(&builder, " -n %s", c.Bytes())
}
if c.options.BlockCount != nil {
fmt.Fprintf(&builder, " -k %s", c.BlockCount())
}
if c.options.Length != nil {
fmt.Fprintf(&builder, " -l %s", c.Length())
}
if c.options.Streams != nil {
fmt.Fprintf(&builder, " -P %d", c.Streams())
}
if c.options.Reverse != nil && *c.options.Reverse {
builder.WriteString(" -R")
}
if c.options.Window != nil {
fmt.Fprintf(&builder, " -w %s", c.Window())
}
if c.options.MSS != nil {
fmt.Fprintf(&builder, " -M %d", c.MSS())
}
if c.options.NoDelay != nil && *c.options.NoDelay {
builder.WriteString(" -N")
}
if c.options.Version6 != nil && *c.options.Version6 {
builder.WriteString(" -6")
}
if c.options.TOS != nil {
fmt.Fprintf(&builder, " -S %d", c.TOS())
}
if c.options.ZeroCopy != nil && *c.options.ZeroCopy {
builder.WriteString(" -Z")
}
if c.options.OmitSec != nil {
fmt.Fprintf(&builder, " -O %d", c.OmitSec())
}
if c.options.Prefix != nil {
fmt.Fprintf(&builder, " -T %s", c.Prefix())
}
if c.options.LogFile != nil && *c.options.LogFile != "" {
fmt.Fprintf(&builder, " --logfile %s", c.LogFile())
}
if c.options.JSON != nil && *c.options.JSON {
builder.WriteString(" -J")
}
if c.options.IncludeServer != nil && *c.options.IncludeServer {
builder.WriteString(" --get-server-output")
}
return builder.String(), nil
}
func (c *Client) Host() string {
if c.options.Host == nil {
return ""
}
return *c.options.Host
}
func (c *Client) SetHost(host string) {
c.options.Host = &host
}
func (c *Client) Proto() Protocol {
if c.options.Proto == nil {
return PROTO_TCP
}
return *c.options.Proto
}
func (c *Client) SetProto(proto Protocol) {
c.options.Proto = &proto
}
func (c *Client) Bandwidth() string {
if c.options.Bandwidth == nil && c.Proto() == PROTO_TCP {
return "0"
} else if c.options.Bandwidth == nil && c.Proto() == PROTO_UDP {
return "1M"
}
return *c.options.Bandwidth
}
func (c *Client) SetBandwidth(bandwidth string) {
c.options.Bandwidth = &bandwidth
}
func (c *Client) TimeSec() int {
if c.options.TimeSec == nil {
return 10
}
return *c.options.TimeSec
}
func (c *Client) SetTimeSec(timeSec int) {
c.options.TimeSec = &timeSec
}
func (c *Client) Bytes() string {
if c.options.Bytes == nil {
return ""
}
return *c.options.Bytes
}
func (c *Client) SetBytes(bytes string) {
c.options.Bytes = &bytes
}
func (c *Client) BlockCount() string {
if c.options.BlockCount == nil {
return ""
}
return *c.options.BlockCount
}
func (c *Client) SetBlockCount(blockCount string) {
c.options.BlockCount = &blockCount
}
func (c *Client) Length() string {
if c.options.Length == nil {
if c.Proto() == PROTO_UDP {
return "1460"
} else {
return "128K"
}
}
return *c.options.Length
}
func (c *Client) SetLength(length string) {
c.options.Length = &length
}
func (c *Client) Streams() int {
if c.options.Streams == nil {
return 1
}
return *c.options.Streams
}
func (c *Client) SetStreams(streamCount int) {
c.options.Streams = &streamCount
}
func (c *Client) Reverse() bool {
if c.options.Reverse == nil {
return false
}
return *c.options.Reverse
}
func (c *Client) SetReverse(reverse bool) {
c.options.Reverse = &reverse
}
func (c *Client) Window() string {
if c.options.Window == nil {
return ""
}
return *c.options.Window
}
func (c *Client) SetWindow(window string) {
c.options.Window = &window
}
func (c *Client) MSS() int {
if c.options.MSS == nil {
return 1460
}
return *c.options.MSS
}
func (c *Client) SetMSS(mss int) {
c.options.MSS = &mss
}
func (c *Client) NoDelay() bool {
if c.options.NoDelay == nil {
return false
}
return *c.options.NoDelay
}
func (c *Client) SetNoDelay(noDelay bool) {
c.options.NoDelay = &noDelay
}
func (c *Client) Version4() bool {
if c.options.Version6 == nil && c.options.Version4 == nil {
return true
} else if c.options.Version6 != nil && *c.options.Version6 == true {
return false
}
return *c.options.Version4
}
func (c *Client) SetVersion4(set bool) {
c.options.Version4 = &set
}
func (c *Client) Version6() bool {
if c.options.Version6 == nil {
return false
}
return *c.options.Version6
}
func (c *Client) SetVersion6(set bool) {
c.options.Version6 = &set
}
func (c *Client) TOS() int {
if c.options.TOS == nil {
return 0
}
return *c.options.TOS
}
func (c *Client) SetTOS(value int) {
c.options.TOS = &value
}
func (c *Client) ZeroCopy() bool {
if c.options.ZeroCopy == nil {
return false
}
return *c.options.ZeroCopy
}
func (c *Client) SetZeroCopy(set bool) {
c.options.ZeroCopy = &set
}
func (c *Client) OmitSec() int {
if c.options.OmitSec == nil {
return 0
}
return *c.options.OmitSec
}
func (c *Client) SetOmitSec(value int) {
c.options.OmitSec = &value
}
func (c *Client) Prefix() string {
if c.options.Prefix == nil {
return ""
}
return *c.options.Prefix
}
func (c *Client) SetPrefix(prefix string) {
c.options.Prefix = &prefix
}
func (c *Client) LogFile() string {
if c.options.LogFile == nil {
return ""
}
return *c.options.LogFile
}
func (c *Client) SetLogFile(logfile string) {
c.options.LogFile = &logfile
}
func (c *Client) JSON() bool {
if c.options.JSON == nil {
return false
}
return *c.options.JSON
}
func (c *Client) SetJSON(set bool) {
c.options.JSON = &set
}
func (c *Client) IncludeServer() bool {
if c.options.IncludeServer == nil {
return false
}
return *c.options.IncludeServer
}
func (c *Client) SetIncludeServer(set bool) {
c.options.IncludeServer = &set
}
func (c *Client) ExitCode() *int {
return c.exitCode
}
func (c *Client) Report() *TestReport {
return c.report
}
func (c *Client) Mode() TestMode {
return c.mode
}
func (c *Client) SetModeJson() {
c.SetJSON(true)
c.reportingChan = nil
c.reportingFile = ""
}
func (c *Client) SetModeLive() <-chan *StreamIntervalReport {
c.SetJSON(false) // having JSON == true will cause reporting to fail
c.live = true
c.reportingChan = make(chan *StreamIntervalReport, 10000)
f, err := ioutil.TempFile("", "iperf_")
if err != nil {
log.Fatalf("failed to create logfile: %v", err)
}
c.reportingFile = f.Name()
c.SetLogFile(c.reportingFile)
return c.reportingChan
}
func (c *Client) Start() (err error) {
//todo: Need to build the string based on the options above that are set
cmd, err := c.commandString()
if err != nil {
return err
}
fmt.Println(cmd)
var exit chan int
c.outputStream, c.errorStream, exit, c.cancel, err = ExecuteAsyncWithCancel(cmd)
if err != nil {
return err
}
c.Running = true
//go func() {
// ds := DebugScanner{Silent: false}
// ds.Scan(c.outputStream)
//}()
//go func() {
// ds := DebugScanner{Silent: false}
// ds.Scan(c.errorStream)
//}()
go func() {
var reporter *Reporter
if c.live {
reporter = &Reporter{
ReportingChannel: c.reportingChan,
LogFile: c.reportingFile,
}
reporter.Start()
} else {
testOutput, err := ioutil.ReadAll(c.outputStream)
if err != nil {
return
}
c.report, err = Loads(string(testOutput))
}
exitCode := <-exit
c.exitCode = &exitCode
c.Running = false
c.Done <- true
if reporter != nil {
reporter.Stop()
}
}()
return nil
}
func (c *Client) Stop() {
if c.Running && c.cancel != nil {
c.cancel()
os.Remove(c.reportingFile)
c.Done <- true
}
}

@ -2,13 +2,17 @@ package main
import (
"fmt"
"github.com/BGrewell/go-conversions"
"github.com/BGrewell/go-iperf"
"time"
)
func main() {
s := iperf.NewServer()
c := iperf.NewClient()
c := iperf.NewClient("127.0.0.1")
includeServer := true
c.IncludeServer = &includeServer
fmt.Println(s.Id)
fmt.Println(c.Id)
@ -16,10 +20,27 @@ func main() {
if err != nil {
fmt.Println(err)
}
for s.Running {
err = c.Start()
if err != nil {
fmt.Println(err)
}
for c.Running {
time.Sleep(1 * time.Second)
}
fmt.Println("stopping server")
s.Stop()
fmt.Printf("Client exit code: %d\n", *c.ExitCode)
fmt.Printf("Server exit code: %d\n", *s.ExitCode)
iperf.Cleanup()
if c.Report.Error != "" {
fmt.Println(c.Report.Error)
} else {
fmt.Printf("Recv Rate: %s\n", conversions.IntBitRateToString(int64(c.Report.End.SumReceived.BitsPerSecond)))
fmt.Printf("Send Rate: %s\n", conversions.IntBitRateToString(int64(c.Report.End.SumSent.BitsPerSecond)))
}
}

@ -1,41 +1,82 @@
package iperf
import (
"context"
"io"
"os/exec"
"strings"
"syscall"
)
func Execute(cmd string, outPipe io.ReadCloser, errPipe io.ReadCloser, exit chan <- int) (err error) {
func ExecuteAsync(cmd string) (outPipe io.ReadCloser, errPipe io.ReadCloser, exitCode chan int, err error) {
exitCode = make(chan int)
cmdParts := strings.Fields(cmd)
binary, err := exec.LookPath(cmdParts[0])
if err != nil {
return err
return nil, nil, nil, err
}
exe := exec.Command(binary, cmdParts[1:]...)
outPipe, err = exe.StdoutPipe()
if err != nil {
return err
return nil, nil, nil, err
}
errPipe, err = exe.StderrPipe()
if err != nil {
return err
return nil, nil, nil, err
}
err = exe.Start()
if err != nil {
return err
return nil, nil, nil, err
}
go func() {
if err := exe.Wait(); err != nil {
if exiterr, ok := err.(*exec.ExitError); ok {
if status, ok := exiterr.Sys().(syscall.WaitStatus); ok {
exit <- status.ExitStatus()
exitCode <- status.ExitStatus()
}
}
} else {
exit <- 0
exitCode <- 0
}
}()
return nil
return outPipe, errPipe, exitCode, nil
}
func ExecuteAsyncWithCancel(cmd string) (stdOut io.ReadCloser, stdErr io.ReadCloser, exitCode chan int, cancelToken context.CancelFunc, err error) {
exitCode = make(chan int)
ctx, cancel := context.WithCancel(context.Background())
cmdParts := strings.Fields(cmd)
binary, err := exec.LookPath(cmdParts[0])
if err != nil {
defer cancel()
return nil, nil, nil, nil, err
}
exe := exec.CommandContext(ctx, binary, cmdParts[1:]...)
stdOut, err = exe.StdoutPipe()
if err != nil {
defer cancel()
return nil, nil, nil, nil, err
}
stdErr, err = exe.StderrPipe()
if err != nil {
defer cancel()
return nil, nil, nil, nil, err
}
err = exe.Start()
if err != nil {
defer cancel()
return nil, nil, nil, nil, err
}
go func() {
if err := exe.Wait(); err != nil {
if exiterr, ok := err.(*exec.ExitError); ok {
if status, ok := exiterr.Sys().(syscall.WaitStatus); ok {
exitCode <- status.ExitStatus()
}
}
} else {
exitCode <- 0
}
}()
return stdOut, stdErr, exitCode, cancel, nil
}

@ -2,4 +2,14 @@ module github.com/BGrewell/go-iperf
go 1.15
require github.com/google/uuid v1.1.2
require (
github.com/BGrewell/go-conversions v0.0.0-20201203155646-5e189e4ca087
github.com/BGrewell/go-execute v0.0.0-20201203155726-b7c037ebde49 // indirect
github.com/BGrewell/tail v1.0.0
github.com/fatih/gomodifytags v1.13.0 // indirect
github.com/fsnotify/fsnotify v1.4.9 // indirect
github.com/google/uuid v1.1.2
github.com/hpcloud/tail v1.0.0 // indirect
gopkg.in/fsnotify.v1 v1.4.7 // indirect
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect
)

@ -1,2 +1,25 @@
github.com/BGrewell/go-conversions v0.0.0-20201203155646-5e189e4ca087 h1:/ZR3IHtTSPqwzYQSfZYwGjrmDI5c1u2jEDDtT75Fmqw=
github.com/BGrewell/go-conversions v0.0.0-20201203155646-5e189e4ca087/go.mod h1:XtXcz/MP04vhr6c6R/5gZPJZVQpbXlFsKTHr5yy/5sU=
github.com/BGrewell/go-execute v0.0.0-20201203155726-b7c037ebde49 h1:HV+WdlqXjmzP59lhgb100vTSIcDuKuLmK11KdnPY0cg=
github.com/BGrewell/go-execute v0.0.0-20201203155726-b7c037ebde49/go.mod h1:vQZr3vuuuKuknvi74K22ne7NzOlwKTPKXOFMVr9Qa6A=
github.com/BGrewell/tail v1.0.0 h1:sG+Uvv+UApHtj5z+AWWB9i5m2SCH0RLfxYqXujYQo+Q=
github.com/BGrewell/tail v1.0.0/go.mod h1:0PFYWAobUZKZLEYIxxmjFgnfvCLA600LkFbGO9KFIRA=
github.com/fatih/camelcase v1.0.0 h1:hxNvNX/xYBp0ovncs8WyWZrOrpBNub/JfaMvbURyft8=
github.com/fatih/camelcase v1.0.0/go.mod h1:yN2Sb0lFhZJUdVvtELVWefmrXpuZESvPmqwoZc+/fpc=
github.com/fatih/gomodifytags v1.13.0 h1:fmhwoecjZ5c34Q2chjRB9cL8Rgag+1TOSMy+grissMc=
github.com/fatih/gomodifytags v1.13.0/go.mod h1:TbUyEjH1Zo0GkJd2Q52oVYqYcJ0eGNqG8bsiOb75P9c=
github.com/fatih/structtag v1.2.0 h1:/OdNE99OxoI/PqaW/SuSK9uxxT3f/tcSZgon/ssNSx4=
github.com/fatih/structtag v1.2.0/go.mod h1:mBJUNpUnHmRKrKlQQlmCrh5PuhftFbNv8Ys4/aAZl94=
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9 h1:L2auWcuQIvxz9xSEqzESnV/QN/gNRXNApHi3fYwl2w0=
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/tools v0.0.0-20180824175216-6c1c5e93cdc1/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=

@ -10,8 +10,8 @@ import (
)
var (
Debug = true
binaryDir = ""
Debug = true
binaryDir = ""
binaryLocation = ""
)
@ -67,4 +67,4 @@ func extractEmbeddedBinaries(files []string) (err error) {
}
}
return nil
}
}

@ -1 +1,393 @@
package iperf
import (
"bytes"
"encoding/json"
"io/ioutil"
)
type StreamInterval struct {
Streams []*StreamIntervalReport `json:"streams"`
Sum *StreamIntervalSumReport `json:"sum"`
}
func (si *StreamInterval) String() string {
b, err := json.Marshal(si)
if err != nil {
return "error converting to json"
}
var pretty bytes.Buffer
err = json.Indent(&pretty, b, "", " ")
if err != nil {
return "error converting json to indented format"
}
return string(pretty.Bytes())
}
type StreamIntervalReport struct {
Socket int `json:"socket"`
StartInterval float32 `json:"start"`
EndInterval float32 `json:"end"`
Seconds float32 `json:"seconds"`
Bytes int `json:"bytes"`
BitsPerSecond float64 `json:"bits_per_second"`
Omitted bool `json:"omitted"`
}
func (sir *StreamIntervalReport) String() string {
b, err := json.Marshal(sir)
if err != nil {
return "error converting to json"
}
var pretty bytes.Buffer
err = json.Indent(&pretty, b, "", " ")
if err != nil {
return "error converting json to indented format"
}
return string(pretty.Bytes())
}
type StreamIntervalSumReport struct {
StartInterval float32 `json:"start"`
EndInterval float32 `json:"end"`
Seconds float32 `json:"seconds"`
Bytes int `json:"bytes"`
BitsPerSecond float64 `json:"bits_per_second"`
Omitted bool `json:"omitted"`
}
func (sisr *StreamIntervalSumReport) String() string {
b, err := json.Marshal(sisr)
if err != nil {
return "error converting to json"
}
var pretty bytes.Buffer
err = json.Indent(&pretty, b, "", " ")
if err != nil {
return "error converting json to indented format"
}
return string(pretty.Bytes())
}
type StreamEndReport struct {
Sender TcpStreamEndReport `json:"sender"`
Receiver TcpStreamEndReport `json:"receiver"`
Udp UdpStreamEndReport `json:"udp"`
}
func (ser *StreamEndReport) String() string {
b, err := json.Marshal(ser)
if err != nil {
return "error converting to json"
}
var pretty bytes.Buffer
err = json.Indent(&pretty, b, "", " ")
if err != nil {
return "error converting json to indented format"
}
return string(pretty.Bytes())
}
type UdpStreamEndReport struct {
Socket int `json:"socket"`
Start float32 `json:"start"`
End float32 `json:"end"`
Seconds float32 `json:"seconds"`
Bytes int `json:"bytes"`
BitsPerSecond float64 `json:"bits_per_second"`
JitterMs float32 `json:"jitter_ms"`
LostPackets int `json:"lost_packets"`
Packets int `json:"packets"`
LostPercent float32 `json:"lost_percent"`
OutOfOrder int `json:"out_of_order"`
}
func (user *UdpStreamEndReport) String() string {
b, err := json.Marshal(user)
if err != nil {
return "error converting to json"
}
var pretty bytes.Buffer
err = json.Indent(&pretty, b, "", " ")
if err != nil {
return "error converting json to indented format"
}
return string(pretty.Bytes())
}
type TcpStreamEndReport struct {
Socket int `json:"socket"`
Start float32 `json:"start"`
End float32 `json:"end"`
Seconds float32 `json:"seconds"`
Bytes int `json:"bytes"`
BitsPerSecond float64 `json:"bits_per_second"`
}
func (tser *TcpStreamEndReport) String() string {
b, err := json.Marshal(tser)
if err != nil {
return "error converting to json"
}
var pretty bytes.Buffer
err = json.Indent(&pretty, b, "", " ")
if err != nil {
return "error converting json to indented format"
}
return string(pretty.Bytes())
}
type StreamEndSumReport struct {
Start float32 `json:"start"`
End float32 `json:"end"`
Seconds float32 `json:"seconds"`
Bytes int `json:"bytes"`
BitsPerSecond float64 `json:"bits_per_second"`
}
func (sesr *StreamEndSumReport) String() string {
b, err := json.Marshal(sesr)
if err != nil {
return "error converting to json"
}
var pretty bytes.Buffer
err = json.Indent(&pretty, b, "", " ")
if err != nil {
return "error converting json to indented format"
}
return string(pretty.Bytes())
}
type CpuUtilizationReport struct {
HostTotal float32 `json:"host_total"`
HostUser float32 `json:"host_user"`
HostSystem float32 `json:"host_system"`
RemoteTotal float32 `json:"remote_total"`
RemoteUser float32 `json:"remote_user"`
RemoteSystem float32 `json:"remote_system"`
}
func (cur *CpuUtilizationReport) String() string {
b, err := json.Marshal(cur)
if err != nil {
return "error converting to json"
}
var pretty bytes.Buffer
err = json.Indent(&pretty, b, "", " ")
if err != nil {
return "error converting json to indented format"
}
return string(pretty.Bytes())
}
type ConnectionInfo struct {
Socket int `json:"socket"`
LocalHost string `json:"local_host"`
LocalPort int `json:"local_port"`
RemoteHost string `json:"remote_host"`
RemotePort int `json:"remote_port"`
}
func (ci *ConnectionInfo) String() string {
b, err := json.Marshal(ci)
if err != nil {
return "error converting to json"
}
var pretty bytes.Buffer
err = json.Indent(&pretty, b, "", " ")
if err != nil {
return "error converting json to indented format"
}
return string(pretty.Bytes())
}
type TimestampInfo struct {
Time string `json:"time"`
TimeSecs int `json:"timesecs"`
}
func (tsi *TimestampInfo) String() string {
b, err := json.Marshal(tsi)
if err != nil {
return "error converting to json"
}
var pretty bytes.Buffer
err = json.Indent(&pretty, b, "", " ")
if err != nil {
return "error converting json to indented format"
}
return string(pretty.Bytes())
}
type ConnectingToInfo struct {
Host string `json:"host"`
Port int `json:"port"`
}
func (cti *ConnectingToInfo) String() string {
b, err := json.Marshal(cti)
if err != nil {
return "error converting to json"
}
var pretty bytes.Buffer
err = json.Indent(&pretty, b, "", " ")
if err != nil {
return "error converting json to indented format"
}
return string(pretty.Bytes())
}
type TestStartInfo struct {
Protocol string `json:"protocol"`
NumStreams int `json:"num_streams"`
BlkSize int `json:"blksize"`
Omit int `json:"omit"`
Duration int `json:"duration"`
Bytes int `json:"bytes"`
Blocks int `json:"blocks"`
Reverse int `json:"reverse"`
}
func (tsi *TestStartInfo) String() string {
b, err := json.Marshal(tsi)
if err != nil {
return "error converting to json"
}
var pretty bytes.Buffer
err = json.Indent(&pretty, b, "", " ")
if err != nil {
return "error converting json to indented format"
}
return string(pretty.Bytes())
}
type StartInfo struct {
Connected []*ConnectionInfo `json:"connected"`
Version string `json:"version"`
SystemInfo string `json:"system_info"`
Timestamp TimestampInfo `json:"timestamp"`
ConnectingTo ConnectingToInfo `json:"connecting_to"`
Cookie string `json:"cookie"`
TcpMssDefault int `json:"tcp_mss_default"`
TestStart TestStartInfo `json:"test_start"`
}
func (si *StartInfo) String() string {
b, err := json.Marshal(si)
if err != nil {
return "error converting to json"
}
var pretty bytes.Buffer
err = json.Indent(&pretty, b, "", " ")
if err != nil {
return "error converting json to indented format"
}
return string(pretty.Bytes())
}
type EndInfo struct {
Streams []*StreamEndReport `json:"streams"`
SumSent StreamEndSumReport `json:"sum_sent"`
SumReceived StreamEndSumReport `json:"sum_received"`
CpuReport CpuUtilizationReport `json:"cpu_utilization_percent"`
}
func (ei *EndInfo) String() string {
b, err := json.Marshal(ei)
if err != nil {
return "error converting to json"
}
var pretty bytes.Buffer
err = json.Indent(&pretty, b, "", " ")
if err != nil {
return "error converting json to indented format"
}
return string(pretty.Bytes())
}
type ServerReport struct {
Start StartInfo `json:"start"`
Intervals []*StreamInterval `json:"intervals"`
End EndInfo `json:"end"`
Error string `json:"error"`
}
func (sr *ServerReport) String() string {
b, err := json.Marshal(sr)
if err != nil {
return "error converting to json"
}
var pretty bytes.Buffer
err = json.Indent(&pretty, b, "", " ")
if err != nil {
return "error converting json to indented format"
}
return string(pretty.Bytes())
}
type TestReport struct {
Start StartInfo `json:"start"`
Intervals []*StreamInterval `json:"intervals"`
End EndInfo `json:"end"`
Error string `json:"error"`
ServerOutputJson ServerReport `json:"server_output_json"`
}
func (tr *TestReport) String() string {
b, err := json.Marshal(tr)
if err != nil {
return "error converting to json"
}
var pretty bytes.Buffer
err = json.Indent(&pretty, b, "", " ")
if err != nil {
return "error converting json to indented format"
}
return string(pretty.Bytes())
}
func Loads(jsonStr string) (report *TestReport, err error) {
r := &TestReport{}
err = json.Unmarshal([]byte(jsonStr), r)
return r, err
}
func Load(filename string) (report *TestReport, err error) {
contents, err := ioutil.ReadFile(filename)
if err != nil {
return nil, err
}
return Loads(string(contents))
}

@ -1 +1,92 @@
package iperf
import (
"fmt"
"github.com/BGrewell/tail"
"github.com/BGrewell/go-conversions"
"log"
"strconv"
"strings"
)
type Reporter struct {
ReportingChannel chan *StreamIntervalReport
LogFile string
running bool
}
func (r *Reporter) Start() {
r.running = true
go r.runLogProcessor()
}
func (r *Reporter) Stop() {
r.running = false
close(r.ReportingChannel)
}
func (r *Reporter) runLogProcessor() {
tailer, err := tail.TailFile(r.LogFile, tail.Config{
Follow: true,
ReOpen: true,
Poll: true,
MustExist: true,
})
if err != nil {
log.Fatalf("failed to tail log file: %v", err)
}
for line := range tailer.Lines {
// TODO: For now this only cares about individual streams it ignores the sum lines
if len(line.Text) > 5 {
id := line.Text[1:4]
stream, err := strconv.Atoi(strings.TrimSpace(id))
if err != nil {
continue
}
fields := strings.Fields(line.Text[5:])
if len(fields) >= 6 {
if fields[0] == "local" {
continue
}
timeFields := strings.Split(fields[0], "-")
start, err := strconv.ParseFloat(timeFields[0], 32)
if err != nil {
log.Printf("failed to convert start time: %s\n", err)
}
end, err := strconv.ParseFloat(timeFields[1], 32)
transferedStr := fmt.Sprintf("%s%s", fields[2], fields[3])
transferedBytes, err := conversions.StringBitRateToInt(transferedStr)
if err != nil {
log.Printf("failed to convert units: %s\n", err)
}
transferedBytes = transferedBytes / 8
rateStr := fmt.Sprintf("%s%s", fields[4], fields[5])
rate, err := conversions.StringBitRateToInt(rateStr)
if err != nil {
log.Printf("failed to convert units: %s\n", err)
}
omitted := false
if len(fields) >= 7 && fields[6] == "(omitted)" {
omitted = true
}
report := &StreamIntervalReport{
Socket: stream,
StartInterval: float32(start),
EndInterval: float32(end),
Seconds: float32(end - start),
Bytes: int(transferedBytes),
BitsPerSecond: float64(rate),
Omitted: omitted,
}
r.ReportingChannel <- report
}
}
if !r.running {
return
}
}
}

@ -0,0 +1,291 @@
{
"start":{
"connected":[
{
"socket":4,
"local_host":"127.0.0.1",
"local_port":64200,
"remote_host":"127.0.0.1",
"remote_port":5201
}
],
"version":"iperf 3.1.3",
"system_info":"CYGWIN_NT-10.0 BGrewell-MOBL3 2.5.1(0.297/5/3) 2016-04-21 22:14 x86_64",
"timestamp":{
"time":"Tue, 02 Mar 2021 18:28:14 GMT",
"timesecs":1614709694
},
"connecting_to":{
"host":"127.0.0.1",
"port":5201
},
"cookie":"BGrewell-MOBL3.1614709694.715824.0bf",
"tcp_mss_default":0,
"test_start":{
"protocol":"TCP",
"num_streams":1,
"blksize":131072,
"omit":0,
"duration":10,
"bytes":0,
"blocks":0,
"reverse":0
}
},
"intervals":[
{
"streams":[
{
"socket":4,
"start":0,
"end":1.000554,
"seconds":1.000554,
"bytes":1635647488,
"bits_per_second":1.307794e+10,
"omitted":false
}
],
"sum":{
"start":0,
"end":1.000554,
"seconds":1.000554,
"bytes":1635647488,
"bits_per_second":1.307794e+10,
"omitted":false
}
},
{
"streams":[
{
"socket":4,
"start":1.000554,
"end":2.000056,
"seconds":0.999502,
"bytes":1527250944,
"bits_per_second":1.222409e+10,
"omitted":false
}
],
"sum":{
"start":1.000554,
"end":2.000056,
"seconds":0.999502,
"bytes":1527250944,
"bits_per_second":1.222409e+10,
"omitted":false
}
},
{
"streams":[
{
"socket":4,
"start":2.000056,
"end":3.000353,
"seconds":1.000297,
"bytes":1712062464,
"bits_per_second":1.369244e+10,
"omitted":false
}
],
"sum":{
"start":2.000056,
"end":3.000353,
"seconds":1.000297,
"bytes":1712062464,
"bits_per_second":1.369244e+10,
"omitted":false
}
},
{
"streams":[
{
"socket":4,
"start":3.000353,
"end":4.000669,
"seconds":1.000316,
"bytes":1730412544,
"bits_per_second":1.383893e+10,
"omitted":false
}
],
"sum":{
"start":3.000353,
"end":4.000669,
"seconds":1.000316,
"bytes":1730412544,
"bits_per_second":1.383893e+10,
"omitted":false
}
},
{
"streams":[
{
"socket":4,
"start":4.000669,
"end":5.000021,
"seconds":0.999352,
"bytes":1777467392,
"bits_per_second":1.422896e+10,
"omitted":false
}
],
"sum":{
"start":4.000669,
"end":5.000021,
"seconds":0.999352,
"bytes":1777467392,
"bits_per_second":1.422896e+10,
"omitted":false
}
},
{
"streams":[
{
"socket":4,
"start":5.000021,
"end":6.000122,
"seconds":1.000101,
"bytes":1673920512,
"bits_per_second":1.339001e+10,
"omitted":false
}
],
"sum":{
"start":5.000021,
"end":6.000122,
"seconds":1.000101,
"bytes":1673920512,
"bits_per_second":1.339001e+10,
"omitted":false
}
},
{
"streams":[
{
"socket":4,
"start":6.000122,
"end":7.000517,
"seconds":1.000395,
"bytes":1585446912,
"bits_per_second":1.267857e+10,
"omitted":false
}
],
"sum":{
"start":6.000122,
"end":7.000517,
"seconds":1.000395,
"bytes":1585446912,
"bits_per_second":1.267857e+10,
"omitted":false
}
},
{
"streams":[
{
"socket":4,
"start":7.000517,
"end":8.000053,
"seconds":0.999536,
"bytes":1642987520,
"bits_per_second":1.315000e+10,
"omitted":false
}
],
"sum":{
"start":7.000517,
"end":8.000053,
"seconds":0.999536,
"bytes":1642987520,
"bits_per_second":1.315000e+10,
"omitted":false
}
},
{
"streams":[
{
"socket":4,
"start":8.000053,
"end":9.000105,
"seconds":1.000052,
"bytes":1547173888,
"bits_per_second":1.237675e+10,
"omitted":false
}
],
"sum":{
"start":8.000053,
"end":9.000105,
"seconds":1.000052,
"bytes":1547173888,
"bits_per_second":1.237675e+10,
"omitted":false
}
},
{
"streams":[
{
"socket":4,
"start":9.000105,
"end":10.000386,
"seconds":1.000281,
"bytes":1773142016,
"bits_per_second":1.418115e+10,
"omitted":false
}
],
"sum":{
"start":9.000105,
"end":10.000386,
"seconds":1.000281,
"bytes":1773142016,
"bits_per_second":1.418115e+10,
"omitted":false
}
}
],
"end":{
"streams":[
{
"sender":{
"socket":4,
"start":0,
"end":10.000386,
"seconds":10.000386,
"bytes":16605511680,
"bits_per_second":1.328390e+10
},
"receiver":{
"socket":4,
"start":0,
"end":10.000386,
"seconds":10.000386,
"bytes":16605511680,
"bits_per_second":1.328390e+10
}
}
],
"sum_sent":{
"start":0,
"end":10.000386,
"seconds":10.000386,
"bytes":16605511680,
"bits_per_second":1.328390e+10
},
"sum_received":{
"start":0,
"end":10.000386,
"seconds":10.000386,
"bytes":16605511680,
"bits_per_second":1.328390e+10
},
"cpu_utilization_percent":{
"host_total":83.983525,
"host_user":5.155306,
"host_system":78.828219,
"remote_total":34.722681,
"remote_user":7.224950,
"remote_system":27.497731
}
}
}

@ -1,44 +1,47 @@
package iperf
import (
"context"
"fmt"
"github.com/google/uuid"
"io"
"time"
)
func NewServer() *Server {
s := &Server{
}
s := &Server{}
s.Id = uuid.New().String()
return s
}
type Server struct {
SharedOptions
OneOff *bool
ExitCode *int
Running bool
Id string
OneOff *bool
ExitCode *int
Running bool
outputStream io.ReadCloser
errorStream io.ReadCloser
errorStream io.ReadCloser
cancel context.CancelFunc
}
func (s *Server) Start() (err error) {
exit := make(chan int, 0)
err = Execute(fmt.Sprintf("%s -s", binaryLocation), s.outputStream, s.errorStream, exit)
var exit chan int
s.outputStream, s.errorStream, exit, s.cancel, err = ExecuteAsyncWithCancel(fmt.Sprintf("%s -s -J", binaryLocation))
if err != nil {
return err
}
s.Running = true
go func() {
ds := DebugScanner{}
ds := DebugScanner{Silent: true}
ds.Scan(s.outputStream)
}()
go func() {
ds := DebugScanner{}
ds := DebugScanner{Silent: true}
ds.Scan(s.errorStream)
}()
go func() {
exitCode := <- exit
exitCode := <-exit
s.ExitCode = &exitCode
s.Running = false
}()
@ -46,5 +49,8 @@ func (s *Server) Start() (err error) {
}
func (s *Server) Stop() {
if s.Running && s.cancel != nil {
s.cancel()
time.Sleep(100 * time.Millisecond)
}
}

@ -6,15 +6,20 @@ import (
"io"
)
type TestMode string
const (
MODE_JSON TestMode = "json"
MODE_LIVE TestMode = "live"
)
type SharedOptions struct {
Id string
Port *int
Format *rune
Port *int
Format *rune
Interval *int
}
type DebugScanner struct {
Silent bool
}
func (ds *DebugScanner) Scan(buff io.ReadCloser) {
@ -26,6 +31,8 @@ func (ds *DebugScanner) Scan(buff io.ReadCloser) {
scanner.Split(bufio.ScanWords)
for scanner.Scan() {
text := scanner.Text()
fmt.Println(text)
if !ds.Silent {
fmt.Println(text)
}
}
}
}

@ -1,5 +1,23 @@
package main
import (
"fmt"
"github.com/BGrewell/go-iperf"
"os"
"time"
)
func main() {
$END$
s := iperf.NewServer()
err := s.Start()
if err != nil {
fmt.Println("failed to start server")
os.Exit(-1)
}
for s.Running {
time.Sleep(1 * time.Second)
}
fmt.Println("server has exited")
}

Loading…
Cancel
Save