2019-01-02 13:12:26 +08:00
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// Copyright (C) Microsoft. All rights reserved.
|
|
|
|
// Licensed under the MIT license.
|
|
|
|
// See LICENSE.txt file in the project root for full license information.
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"net"
|
2019-01-15 05:56:36 +08:00
|
|
|
"os"
|
|
|
|
"sync/atomic"
|
2019-01-02 13:12:26 +08:00
|
|
|
"time"
|
|
|
|
)
|
|
|
|
|
2019-01-04 09:25:00 +08:00
|
|
|
func runXClient(testParam EthrTestParam, clientParam ethrClientParam, server string) {
|
2019-01-02 13:12:26 +08:00
|
|
|
initClient()
|
|
|
|
test, err := newTest(server, nil, testParam, nil, nil)
|
|
|
|
if err != nil {
|
2019-09-30 20:43:51 +08:00
|
|
|
ui.printErr("runXClient: failed to create the new test.")
|
2019-01-02 13:12:26 +08:00
|
|
|
return
|
|
|
|
}
|
2019-01-15 05:56:36 +08:00
|
|
|
xcRunTest(test, clientParam.duration, clientParam.gap)
|
2019-01-02 13:12:26 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
func initXClient() {
|
|
|
|
initClientUI()
|
|
|
|
}
|
|
|
|
|
2019-01-15 05:56:36 +08:00
|
|
|
func xcRunTest(test *ethrTest, d, g time.Duration) {
|
|
|
|
startStatsTimer()
|
2019-01-02 13:12:26 +08:00
|
|
|
if test.testParam.TestID.Protocol == TCP {
|
|
|
|
if test.testParam.TestID.Type == ConnLatency {
|
2019-01-15 05:56:36 +08:00
|
|
|
go xcRunTCPConnLatencyTest(test, g)
|
|
|
|
} else if test.testParam.TestID.Type == Bandwidth {
|
|
|
|
go xcRunTCPBandwidthTest(test)
|
2020-09-05 10:56:22 +08:00
|
|
|
} else if test.testParam.TestID.Type == Cps {
|
|
|
|
go xcRunTCPCpsTest(test)
|
2019-01-02 13:12:26 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
test.isActive = true
|
|
|
|
toStop := make(chan int, 1)
|
|
|
|
runDurationTimer(d, toStop)
|
2020-09-23 07:28:41 +08:00
|
|
|
handleInterrupt(toStop)
|
2019-01-02 13:12:26 +08:00
|
|
|
reason := <-toStop
|
2019-01-15 05:56:36 +08:00
|
|
|
close(test.done)
|
|
|
|
stopStatsTimer()
|
2019-01-02 13:12:26 +08:00
|
|
|
switch reason {
|
|
|
|
case timeout:
|
|
|
|
ui.printMsg("Ethr done, duration: " + d.String() + ".")
|
|
|
|
case interrupt:
|
|
|
|
ui.printMsg("Ethr done, received interrupt signal.")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-01-15 05:56:36 +08:00
|
|
|
func xcRunTCPConnLatencyTest(test *ethrTest, g time.Duration) {
|
2019-01-02 13:12:26 +08:00
|
|
|
server := test.session.remoteAddr
|
|
|
|
// TODO: Override NumThreads for now, fix it later to support parallel
|
|
|
|
// threads.
|
|
|
|
test.testParam.NumThreads = 1
|
|
|
|
for th := uint32(0); th < test.testParam.NumThreads; th++ {
|
|
|
|
go func() {
|
|
|
|
var min, max, avg time.Duration
|
|
|
|
var sum, sent, rcvd, lost int64
|
|
|
|
min = time.Hour
|
|
|
|
sent = -1
|
|
|
|
warmup := true
|
2019-01-02 23:52:35 +08:00
|
|
|
warmupText := "[warmup] "
|
2019-01-02 13:12:26 +08:00
|
|
|
ExitForLoop:
|
|
|
|
for {
|
|
|
|
select {
|
|
|
|
case <-test.done:
|
|
|
|
ui.printMsg("TCP connect statistics for %s:", server)
|
|
|
|
ui.printMsg(" Sent = %d, Received = %d, Lost = %d", sent, rcvd, lost)
|
2020-06-19 01:07:05 +08:00
|
|
|
ui.printMsg(" Min = %s, Max = %s, Avg = %s", durationToString(min),
|
|
|
|
durationToString(max), durationToString(avg))
|
2019-01-02 13:12:26 +08:00
|
|
|
break ExitForLoop
|
|
|
|
default:
|
|
|
|
sent++
|
|
|
|
t0 := time.Now()
|
2019-01-04 09:25:00 +08:00
|
|
|
conn, err := net.Dial(tcp(ipVer), server)
|
2019-01-02 13:12:26 +08:00
|
|
|
if err != nil {
|
|
|
|
lost++
|
2019-01-15 05:56:36 +08:00
|
|
|
ui.printDbg("Unable to dial TCP connection to [%s], error: %v", server, err)
|
|
|
|
ui.printMsg("[tcp] %sConnection %s: Timed out", warmupText, server)
|
2019-01-02 13:12:26 +08:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
t1 := time.Since(t0)
|
2019-01-02 23:52:35 +08:00
|
|
|
rserver, rport, _ := net.SplitHostPort(conn.RemoteAddr().String())
|
|
|
|
lserver, lport, _ := net.SplitHostPort(conn.LocalAddr().String())
|
|
|
|
ui.printMsg("[tcp] %sConnection from [%s]:%s to [%s]:%s: %s",
|
2020-06-19 01:07:05 +08:00
|
|
|
warmupText, lserver, lport, rserver, rport, durationToString(t1))
|
2019-01-02 13:12:26 +08:00
|
|
|
if !warmup {
|
|
|
|
rcvd++
|
|
|
|
sum += t1.Nanoseconds()
|
|
|
|
avg = time.Duration(sum / rcvd)
|
|
|
|
if t1 < min {
|
|
|
|
min = t1
|
|
|
|
}
|
|
|
|
if t1 > max {
|
|
|
|
max = t1
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
warmup = false
|
2019-01-02 23:52:35 +08:00
|
|
|
warmupText = ""
|
2019-01-02 13:12:26 +08:00
|
|
|
server = fmt.Sprintf("[%s]:%s", rserver, rport)
|
|
|
|
}
|
2019-01-15 05:56:36 +08:00
|
|
|
tcpconn, ok := conn.(*net.TCPConn)
|
|
|
|
if ok {
|
|
|
|
tcpconn.SetLinger(0)
|
|
|
|
}
|
2019-01-02 13:12:26 +08:00
|
|
|
conn.Close()
|
|
|
|
t1 = time.Since(t0)
|
2019-01-15 05:56:36 +08:00
|
|
|
if t1 < g {
|
|
|
|
time.Sleep(g - t1)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-09-05 10:56:22 +08:00
|
|
|
func xcRunTCPCpsTest(test *ethrTest) {
|
|
|
|
server := test.session.remoteAddr
|
|
|
|
for th := uint32(0); th < test.testParam.NumThreads; th++ {
|
|
|
|
go func() {
|
|
|
|
ExitForLoop:
|
|
|
|
for {
|
|
|
|
select {
|
|
|
|
case <-test.done:
|
|
|
|
break ExitForLoop
|
|
|
|
default:
|
|
|
|
conn, err := net.Dial(tcp(ipVer), server)
|
|
|
|
if err == nil {
|
|
|
|
atomic.AddUint64(&test.testResult.data, 1)
|
|
|
|
tcpconn, ok := conn.(*net.TCPConn)
|
|
|
|
if ok {
|
|
|
|
tcpconn.SetLinger(0)
|
|
|
|
}
|
|
|
|
conn.Close()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-01-15 05:56:36 +08:00
|
|
|
func xcCloseConn(conn net.Conn, test *ethrTest) {
|
|
|
|
test.delConn(conn)
|
|
|
|
err := conn.Close()
|
|
|
|
if err != nil {
|
|
|
|
ui.printDbg("Failed to close TCP connection, error: %v", err)
|
|
|
|
}
|
|
|
|
xcDeleteTest(test)
|
|
|
|
}
|
|
|
|
|
|
|
|
func xcDeleteTest(test *ethrTest) {
|
|
|
|
if test != nil {
|
|
|
|
if safeDeleteTest(test) {
|
|
|
|
ui.printMsg("Ethr done, server terminated the session.")
|
|
|
|
os.Exit(0)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func xcRunTCPBandwidthTest(test *ethrTest) {
|
|
|
|
server := test.session.remoteAddr
|
|
|
|
ui.printMsg("Connecting to host %s, port %s", server, tcpBandwidthPort)
|
|
|
|
for th := uint32(0); th < test.testParam.NumThreads; th++ {
|
|
|
|
buff := make([]byte, test.testParam.BufferSize)
|
|
|
|
for i := uint32(0); i < test.testParam.BufferSize; i++ {
|
|
|
|
buff[i] = byte(i)
|
|
|
|
}
|
|
|
|
go func() {
|
|
|
|
conn, err := net.Dial(tcp(ipVer), server)
|
|
|
|
if err != nil {
|
2019-09-30 20:43:51 +08:00
|
|
|
ui.printErr("xcRunTCPBandwidthTest: error in dialing TCP connection: %v", err)
|
2019-01-15 05:56:36 +08:00
|
|
|
os.Exit(1)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
ec := test.newConn(conn)
|
|
|
|
rserver, rport, _ := net.SplitHostPort(conn.RemoteAddr().String())
|
|
|
|
lserver, lport, _ := net.SplitHostPort(conn.LocalAddr().String())
|
|
|
|
ui.printMsg("[%3d] local %s port %s connected to %s port %s",
|
|
|
|
ec.fd, lserver, lport, rserver, rport)
|
|
|
|
blen := len(buff)
|
|
|
|
addRef(test)
|
|
|
|
defer xcCloseConn(conn, test)
|
|
|
|
ExitForLoop:
|
|
|
|
for {
|
|
|
|
select {
|
|
|
|
case <-test.done:
|
|
|
|
break ExitForLoop
|
|
|
|
default:
|
|
|
|
n, err := conn.Write(buff)
|
|
|
|
if err != nil {
|
|
|
|
// ui.printErr(err)
|
|
|
|
// test.ctrlConn.Close()
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if n < blen {
|
|
|
|
// ui.printErr("Partial write: " + strconv.Itoa(n))
|
|
|
|
// test.ctrlConn.Close()
|
|
|
|
return
|
2019-01-02 13:12:26 +08:00
|
|
|
}
|
2019-01-15 05:56:36 +08:00
|
|
|
atomic.AddUint64(&ec.data, uint64(blen))
|
|
|
|
atomic.AddUint64(&test.testResult.data, uint64(blen))
|
2019-01-02 13:12:26 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
}
|
|
|
|
}
|