aboutsummaryrefslogtreecommitdiff
path: root/Hotline/Shared
diff options
context:
space:
mode:
authorDustin Mierau <dustin@mierau.me>2024-01-13 19:12:12 -0800
committerDustin Mierau <dustin@mierau.me>2024-01-13 19:12:12 -0800
commit3f4e2c33a4c2719388d9378def981688fcaed210 (patch)
tree01928a9347ca2dc18478da4107365a968637ccf5 /Hotline/Shared
parent2f210991684c87073dd7534d2d0416d91cf50275 (diff)
Rewrite HotlineClient to use new simpler NetSocket stream based socket lib. Seems to be working well and has fixed some connection issues with certain servers. This does away with Apple's NWConnection for everything but file transfers which I still want to be processing on another thread so will keep NWConnection for now.
Diffstat (limited to 'Hotline/Shared')
-rw-r--r--Hotline/Shared/NetSocket.swift58
1 files changed, 50 insertions, 8 deletions
diff --git a/Hotline/Shared/NetSocket.swift b/Hotline/Shared/NetSocket.swift
index e474f5a..5b33f61 100644
--- a/Hotline/Shared/NetSocket.swift
+++ b/Hotline/Shared/NetSocket.swift
@@ -41,6 +41,10 @@ final class NetSocket: NSObject, StreamDelegate {
private var status: NetSocketStatus = .disconnected
+ @MainActor public func has(_ length: Int) -> Bool {
+ return (self.available >= length)
+ }
+
override init() {}
@MainActor public func connect(host: String, port: Int) {
@@ -59,14 +63,16 @@ final class NetSocket: NSObject, StreamDelegate {
inputStream?.delegate = self
outputStream?.delegate = self
- inputStream?.schedule(in: .current, forMode: .common)
- outputStream?.schedule(in: .current, forMode: .common)
+ inputStream?.schedule(in: .current, forMode: .default)
+ outputStream?.schedule(in: .current, forMode: .default)
inputStream?.open()
outputStream?.open()
}
@MainActor public func close(_ err: Error? = nil) {
+ print("NetSocket: Closed")
+
let disconnected = (self.status != .disconnected)
self.status = .disconnected
@@ -75,8 +81,8 @@ final class NetSocket: NSObject, StreamDelegate {
self.output?.delegate = nil
self.input?.close()
self.output?.close()
- self.input?.remove(from: .current, forMode: .common)
- self.output?.remove(from: .current, forMode: .common)
+ self.input?.remove(from: .current, forMode: .default)
+ self.output?.remove(from: .current, forMode: .default)
self.input = nil
self.output = nil
self.inputBuffer = []
@@ -85,8 +91,6 @@ final class NetSocket: NSObject, StreamDelegate {
if disconnected {
self.delegate?.netsocketDisconnected(socket: self, error: err)
}
-
- print("NetSocket: Closed")
}
@MainActor public func write(_ data: Data) {
@@ -166,6 +170,7 @@ final class NetSocket: NSObject, StreamDelegate {
}
let bytesWritten = output.write(self.outputBuffer, maxLength: self.outputBuffer.count)
+ print("NetSocket => \(bytesWritten) bytes")
if bytesWritten > 0 {
self.outputBuffer.removeFirst(bytesWritten)
self.delegate?.netsocketSent(socket: self, count: bytesWritten)
@@ -180,9 +185,10 @@ final class NetSocket: NSObject, StreamDelegate {
return
}
- let bytesRead = input.read(&self.readBuffer, maxLength: self.readBuffer.capacity)
+ let bytesRead = input.read(&self.readBuffer, maxLength: 4 * 1024)
+ print("NetSocket <= \(bytesRead) bytes")
if bytesRead > 0 {
- self.inputBuffer.append(contentsOf: readBuffer[0..<bytesRead])
+ self.inputBuffer.append(contentsOf: self.readBuffer[0..<bytesRead])
self.delegate?.netsocketReceived(socket: self, bytes: self.inputBuffer)
}
else if bytesRead == -1 {
@@ -199,6 +205,9 @@ final class NetSocket: NSObject, StreamDelegate {
switch eventCode {
case .openCompleted:
+ if aStream == input {
+ self.setupStreamOptions()
+ }
if input.streamStatus == .open && output.streamStatus == .open {
if self.status == .connecting {
print("NetSocket: Connected")
@@ -224,4 +233,37 @@ final class NetSocket: NSObject, StreamDelegate {
break
}
}
+
+ // MARK: -
+
+ private func setupStreamOptions() {
+ if let input = self.input {
+ let socketData: Data = CFReadStreamCopyProperty(input as CFReadStream, CFStreamPropertyKey.socketNativeHandle) as! Data;
+ var socketHandle: CFSocketNativeHandle = 0;
+ (socketData as NSData).getBytes(&socketHandle, length: MemoryLayout.size(ofValue: socketHandle));
+
+ var value: Int = 0;
+ let size = UInt32(MemoryLayout.size(ofValue: value));
+
+ value = 1;
+ if setsockopt(socketHandle, IPPROTO_TCP, TCP_NODELAY, &value, size) != 0 {
+ print("NetSocket: failed to set TCP_NODELAY");
+ }
+ // Enable keepalive
+ value = 1;
+ if setsockopt(socketHandle, SOL_SOCKET, SO_KEEPALIVE, &value, size) != 0 {
+ print("NetSocket: failed to set SO_KEEPALIVE");
+ }
+ // Number of keepalives before close (including first keepalive packet)
+ value = 5
+ if setsockopt(socketHandle, IPPROTO_TCP, TCP_KEEPCNT, &value, size) != 0 {
+ print("NetSocket: failed to set TCP_KEEPCNT");
+ }
+ // Idle time used when SO_KEEPALIVE is enabled. Sets how long connection must be idle before keepalive is sent.
+ value = 60
+ if setsockopt(socketHandle, IPPROTO_TCP, TCP_KEEPALIVE, &value, size) != 0 {
+ print("NetSocket: failed to set TCP_KEEPALIVE")
+ }
+ }
+ }
}