Skip to content

Commit

Permalink
Merge pull request #3 from igorskh/develop
Browse files Browse the repository at this point in the history
Develop
  • Loading branch information
igorskh authored Mar 24, 2021
2 parents 3a3ba6c + e73404d commit 6e1bdcb
Show file tree
Hide file tree
Showing 4 changed files with 79 additions and 25 deletions.
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Swift wrapper for iPerf

An easy to use Swift wrapper for iPerf.

## Usage

An appliction using this package: [iPerf SwiftUI](https://github.com/igorskh/iperf-swiftui)

Package implements iPerf server and client.
Expand Down Expand Up @@ -60,3 +64,7 @@ class IperfRunnerController: ObservableObject, Identifiable {
}

```

## Not implemented

The code which requires OpenSSL library is currently commented.
8 changes: 7 additions & 1 deletion Sources/IperfSwift/IperfIntervalResult.swift
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
//
// File.swift
// IperfIntervalResult.swift
//
//
// Created by Igor Kim on 08.11.20.
Expand All @@ -23,6 +23,9 @@ public struct IperfIntervalResult: Identifiable {
public var state: IperfState = .UNKNOWN
public var debugDescription: String = ""

public var startTime: TimeInterval = 0.0
public var endTime: TimeInterval = 0.0

public var throughput = IperfThroughput.init(bytesPerSecond: 0.0)
public var hasError: Bool {
error != .IENONE
Expand Down Expand Up @@ -56,7 +59,10 @@ public struct IperfIntervalResult: Identifiable {
}
}
if let first = streams.first {
startTime = first.startTime
endTime = first.endTime
duration = first.intervalDuration

if self.prot == .udp {
averageJitter = sumJitter / Double(streams.count)
}
Expand Down
82 changes: 58 additions & 24 deletions Sources/IperfSwift/IperfRunner.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ public enum IperfRunnerState {
case running
case error
case stopping
case finished
}

public enum IperfState: Int8 {
Expand All @@ -40,12 +41,11 @@ public typealias errorFunctionType = (_ error: IperfError) -> Void
public typealias runnerStateFunctionType = (_ error: IperfRunnerState) -> Void

public class IperfRunner {
private var configuration: IperfConfiguration = IperfConfiguration()

private var onReporterFunction: reporterFunctionType = {result in }
private var onErrorFunction: errorFunctionType = {error in }
private var onRunnerStateFunction: runnerStateFunctionType = {error in }

private var configuration: IperfConfiguration? = nil
private var observer: NSObjectProtocol? = nil
private var currentTest: UnsafeMutablePointer<iperf_test>? = nil

Expand All @@ -54,11 +54,8 @@ public class IperfRunner {
onRunnerStateFunction(newValue)
}
}
private var uid = UUID().uuidString

// MARK: Initialisers
public init() { }

public init(with configuration: IperfConfiguration) {
self.configuration = configuration
}
Expand All @@ -67,7 +64,7 @@ public class IperfRunner {
private let reporterCallback: @convention(c) (UnsafeMutablePointer<iperf_test>?) -> Void = { refTest in
DispatchQueue.main.async {
if let testPointer = refTest {
let testUID = String(cString: testPointer.pointee.title)
let testUID = String(testPointer.hashValue)
NotificationCenter.default.post(name: Notification.Name(IperfNotificationName.status.rawValue + testUID), object: refTest)
}
}
Expand All @@ -77,7 +74,8 @@ public class IperfRunner {
if state != .running {
return
}
guard let pointer = notification.object as? UnsafeMutablePointer<iperf_test> else {
guard let pointer = notification.object as? UnsafeMutablePointer<iperf_test>,
let configuration = configuration else {
return
}

Expand All @@ -86,8 +84,11 @@ public class IperfRunner {
result.debugDescription = "OK"
result.state = IperfState(rawValue: runningTest.state) ?? .UNKNOWN

if configuration.role == .server && result.state == .IPERF_DONE {
return
if result.state == .IPERF_DONE {
state = .finished
if configuration.role == .server {
return
}
}

guard var stream: UnsafeMutablePointer<iperf_stream> = runningTest.streams.slh_first else {
Expand All @@ -112,6 +113,10 @@ public class IperfRunner {

// MARK: Private methods
private func applyConfiguration() {
guard let configuration = configuration else {
return
}

var addr: UnsafePointer<Int8>? = nil
if let address = configuration.address, !address.isEmpty {
addr = NSString(string: address).utf8String
Expand Down Expand Up @@ -164,31 +169,57 @@ public class IperfRunner {

private func startIperfProcess() {
DispatchQueue.global(qos: .userInitiated).async {
defer {
DispatchQueue.main.async { self.cleanState() }
}

i_errno = IperfError.IENONE.rawValue

DispatchQueue.main.sync { self.state = .running }

var code: Int32
if self.configuration.role == .client {
if let configuration = self.configuration,
configuration.role == .client {
code = iperf_run_client(self.currentTest)
} else {
code = iperf_run_server(self.currentTest)
}
if code < 0 || i_errno != IperfError.IENONE.rawValue {
self.onErrorFunction(IperfError.init(rawValue: i_errno) ?? .UNKNOWN)
self.onError(IperfError.init(rawValue: i_errno) ?? .UNKNOWN)
} else {
guard let configuration = self.configuration else {
return self.cleanState()
}
DispatchQueue.main.asyncAfter(deadline: .now() + (configuration.reporterInterval ?? 2.0)) {
if self.currentTest != nil {
self.cleanState()
}
}
}
}
}

private func cleanState() {
state = .ready
if let observer = self.observer {
private func cleanState(isExit: Bool = true) {
if let observer = observer {
NotificationCenter.default.removeObserver(observer)
self.observer = nil
}

if isExit && configuration != nil {
configuration = nil
}
if currentTest != nil {
iperf_free_test(currentTest)
currentTest = nil
}
if isExit && (state == .running || state == .stopping) {
state = .finished
}
}

private func onError(_ error: IperfError) {
state = .error
onErrorFunction(error)

cleanState()
// Reset global error code
i_errno = IperfError.IENONE.rawValue
}

// MARK: Public methods
Expand All @@ -212,26 +243,28 @@ public class IperfRunner {
onErrorFunction = onError
onRunnerStateFunction = onRunnerState

cleanState()
cleanState(isExit: false)
state = .initialising

currentTest = iperf_new_test()
guard let testPointer = currentTest else {
return onErrorFunction(.INIT_ERROR)
return self.onError(.INIT_ERROR)
}

let code = iperf_defaults(currentTest)
if code < 0 {
return onErrorFunction(.INIT_ERROR_DEFAULTS)
return self.onError(.INIT_ERROR_DEFAULTS)
}

applyConfiguration()

// Cofingure callbacks and notifications
testPointer.pointee.title = strdup(uid)
testPointer.pointee.reporter_callback = reporterCallback
observer = NotificationCenter.default.addObserver(
forName: Notification.Name(IperfNotificationName.status.rawValue + uid), object: nil, queue: nil, using: reporterNotificationCallback
forName: Notification.Name(IperfNotificationName.status.rawValue + String(testPointer.hashValue)),
object: nil,
queue: nil,
using: reporterNotificationCallback
)

startIperfProcess()
Expand All @@ -245,7 +278,8 @@ public class IperfRunner {
state = .stopping
if pointer.pointee.state != IPERF_DONE {
pointer.pointee.done = 1
if configuration.role == .server {
if let configuration = configuration,
configuration.role == .server {
shutdown(pointer.pointee.listener, SHUT_RDWR)
close(pointer.pointee.listener)
}
Expand Down
6 changes: 6 additions & 0 deletions Sources/IperfSwift/IperfStreamIntervalResult.swift
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ public struct IperfStreamIntervalResult {
var rttvar: Int32 = 0
var pmtu: Int32 = 0

var startTime: Double = 0
var endTime: Double = 0

var intervalTimeDiff = TimeInterval(0.0)

init(_ results: iperf_interval_results) {
Expand All @@ -49,6 +52,9 @@ public struct IperfStreamIntervalResult {
time2Pointer = pointer
}

startTime = Double(timeConv2.secs) + Double(timeConv2.usecs)*1e-6
endTime = Double(timeConv1.secs) + Double(timeConv1.usecs)*1e-6

iperf_time_diff(time1Pointer, time2Pointer, &diff)
intervalTimeDiff = Double(diff.secs) + Double(diff.usecs)*1e-6

Expand Down

0 comments on commit 6e1bdcb

Please sign in to comment.