Skip to content

Commit

Permalink
Merge pull request #3 from RockfordWei/dev-async
Browse files Browse the repository at this point in the history
Dev async
  • Loading branch information
RockfordWei authored Aug 21, 2023
2 parents 7726549 + 224e039 commit abfc248
Show file tree
Hide file tree
Showing 2 changed files with 159 additions and 43 deletions.
96 changes: 53 additions & 43 deletions Sources/PerfectNet/Net.swift
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,10 @@ public enum PerfectNetError: Error {

func ThrowNetworkError(file: String = #file, function: String = #function, line: Int = #line) throws -> Never {
let err = errno
let msg = String(validatingUTF8: strerror(err))!
let bufferSize = 1024
var buffer = [CChar](repeating: 0, count: bufferSize)
_ = strerror_r(errno, &buffer, bufferSize)
let msg = String(cString: buffer)
throw PerfectNetError.networkError(err, msg + " \(file) \(function) \(line)")
}

Expand Down Expand Up @@ -203,48 +206,55 @@ open class Net {
fd.switchToNonBlocking()
}
}
//
// func makeAddress(_ sin: inout sockaddr_storage, host: String, port: UInt16) -> Int {
// let aiFlags: Int32 = 0
// let family: Int32 = AF_UNSPEC
// let bPort = port.bigEndian
// var hints = addrinfo(ai_flags: aiFlags, ai_family: family, ai_socktype: SOCK_STREAM, ai_protocol: 0, ai_addrlen: 0, ai_canonname: nil, ai_addr: nil, ai_next: nil)
// var resultList = UnsafeMutablePointer<addrinfo>(bitPattern: 0)
// var result = getaddrinfo(host, nil, &hints, &resultList)
// while EAI_AGAIN == result {
// Threading.sleep(seconds: 0.1)
// result = getaddrinfo(host, nil, &hints, &resultList)
// }
// if result == EAI_NONAME {
// hints = addrinfo(ai_flags: aiFlags, ai_family: AF_INET6, ai_socktype: SOCK_STREAM, ai_protocol: 0, ai_addrlen: 0, ai_canonname: nil, ai_addr: nil, ai_next: nil)
// result = getaddrinfo(host, nil, &hints, &resultList)
// }
// if result == 0, var resultList = resultList {
// defer {
// freeaddrinfo(resultList)
// }
// guard let addr = resultList.pointee.ai_addr else {
// return -1
// }
// switch Int32(addr.pointee.sa_family) {
// case AF_INET6:
// memcpy(&sin, addr, MemoryLayout<sockaddr_in6>.size)
// UnsafeMutablePointer(&sin).withMemoryRebound(to: sockaddr_in6.self, capacity: 1) {
// $0.pointee.sin6_port = in_port_t(bPort)
// }
// case AF_INET:
// memcpy(&sin, addr, MemoryLayout<sockaddr_in>.size)
// UnsafeMutablePointer(&sin).withMemoryRebound(to: sockaddr_in.self, capacity: 1) {
// $0.pointee.sin_port = in_port_t(bPort)
// }
// default:
// return -1
// }
// } else {
// return -1
// }
// return 0
// }

func makeAddress(_ sin: inout sockaddr_storage, host: String, port: UInt16) -> Int {
let aiFlags: Int32 = 0
let family: Int32 = AF_UNSPEC
let bPort = port.bigEndian
#if os(Linux)
let SOCK_STREAM = __socket_type(1)
#endif
var hints = addrinfo(ai_flags: aiFlags, ai_family: family, ai_socktype: SOCK_STREAM, ai_protocol: 0, ai_addrlen: 0, ai_canonname: nil, ai_addr: nil, ai_next: nil)
var resultList = UnsafeMutablePointer<addrinfo>(bitPattern: 0)
var result = getaddrinfo(host, nil, &hints, &resultList)
while EAI_AGAIN == result {
Threading.sleep(seconds: 0.1)
result = getaddrinfo(host, nil, &hints, &resultList)
}
if result == EAI_NONAME {
hints = addrinfo(ai_flags: aiFlags, ai_family: AF_INET6, ai_socktype: SOCK_STREAM, ai_protocol: 0, ai_addrlen: 0, ai_canonname: nil, ai_addr: nil, ai_next: nil)
result = getaddrinfo(host, nil, &hints, &resultList)
}
if result == 0, let resultList {
defer {
freeaddrinfo(resultList)
}
guard let addr = resultList.pointee.ai_addr else {
return -1
}
switch Int32(addr.pointee.sa_family) {
case AF_INET6:
let size = MemoryLayout<sockaddr_in6>.size
memcpy(&sin, addr, size)
var addr6 = sockaddr_in6()
memcpy(&addr6, &sin, size)
addr6.sin6_port = in_port_t(bPort)
memcpy(&sin, &addr6, size)
case AF_INET:
let size = MemoryLayout<sockaddr_in>.size
memcpy(&sin, addr, size)
var addr4 = sockaddr_in()
memcpy(&addr4, &sin, size)
addr4.sin_port = in_port_t(bPort)
memcpy(&sin, &addr4, size)
default:
return -1
}
} else {
return -1
}
return 0
}

func isEAgain(err: Int) -> Bool {
return err == -1 && errno == EAGAIN
Expand Down
106 changes: 106 additions & 0 deletions Sources/PerfectNet/NetExt.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
//
// NetExt.swift
//
//
// Created by Rocky Wei on 2023-08-21.
//

import Foundation

@available(macOS 10.15.0, *)
public extension NetTCP {
/// Read the indicated number of bytes and deliver them concurrently.
/// - parameter count: The number of bytes to read
/// - parameter timeoutSeconds: The number of seconds to wait for the requested number of bytes. A timeout value of negative one indicates that the request should have no timeout.
/// - returns: If the timeout occurs before the requested number of bytes have been read, an empty array will be delivered to the callback.
/// If an error or disconnection occurs then a nil object will be delivered.
func readBytesFullyWithContinuation(count: Int, timeoutSeconds: Double) async -> [UInt8]? {
return await withCheckedContinuation { continuation in
readBytesFully(count: count, timeoutSeconds: timeoutSeconds) { data in
continuation.resume(returning: data)
}
}
}

/// Read up to the indicated number of bytes and deliver them concurrently
/// - parameter count: The maximum number of bytes to read.
/// - returns: If an error occurs during the read then a nil object will be passed, otherwise, the immediately available number of bytes, which may be zero, will be passed.
func readSomeBytes(count: Int) async -> [UInt8]? {
return await withCheckedContinuation { continuation in
readSomeBytes(count: count) { data in
continuation.resume(returning: data)
}
}
}

/// Write the string and return the number of bytes which were written.
/// - parameter s: The string to write. The string will be written based on its UTF-8 encoding.
/// - returns: The number of bytes which were successfuly written, which may be zero.
func writeWithContinuation(string: String) async -> Int {
return await withCheckedContinuation { continuation in
write(string: string) { written in
continuation.resume(returning: written)
}
}
}

/// Write the indicated bytes and return the number of bytes which were written.
/// - parameter bytes: The array of UInt8 to write.
/// - returns: The number of bytes which were successfuly written, which may be zero.
func writeWithContinuation(bytes: [UInt8]) async -> Int {
return await withCheckedContinuation { continuation in
write(bytes: bytes) { written in
continuation.resume(returning: written)
}
}
}

/// Write the indicated bytes and return the number of bytes which were written.
/// - parameter bytes: The array of UInt8 to write.
/// - parameter offsetBy: The offset within `bytes` at which to begin writing.
/// - parameter count: The number of bytes to write.
/// - returns: The number of bytes which were successfuly written, which may be zero.
func writeWithContinuation(bytes: [UInt8], offsetBy: Int, count: Int) async -> Int {
return await withCheckedContinuation { continuation in
write(bytes: bytes, offsetBy: offsetBy, count: count) { written in
continuation.resume(returning: written)
}
}
}

/// Connect to the indicated server
/// - parameter address: The server's address, expressed as a string.
/// - parameter port: The port on which to connect.
/// - parameter timeoutSeconds: The number of seconds to wait for the connection to complete. A timeout of negative one indicates that there is no timeout.
/// - returns: If the connection completes successfully then the current NetTCP instance will be passed to the callback, otherwise, a nil object will be passed.
/// - returns: `PerfectError.NetworkError`
func connectWithContinuation(address: String, port: UInt16, timeoutSeconds: Double) async throws -> NetTCP? {
return try await withCheckedThrowingContinuation { continuation in
do {
try connect(address: address, port: port, timeoutSeconds: timeoutSeconds) {
continuation.resume(returning: $0)
}
} catch {
continuation.resume(throwing: error)
}
}
}

/// Connect to the indicated server
/// - parameter address: The server's address, expressed as a string.
/// - parameter port: The port on which to connect.
/// - parameter timeoutSeconds: The number of seconds to wait for the connection to complete. A timeout of negative one indicates that there is no timeout.
/// - returns:. If the connection completes successfully then the current NetTCP instance will be passed to the callback, otherwise, a nil object will be passed.
/// - throws: `PerfectError.NetworkError`
func acceptWithContinuation(timeoutSeconds: Double) async throws -> NetTCP? {
return try await withCheckedThrowingContinuation { continuation in
do {
try accept(timeoutSeconds: timeoutSeconds) {
continuation.resume(returning: $0)
}
} catch {
continuation.resume(throwing: error)
}
}
}
}

0 comments on commit abfc248

Please sign in to comment.