|
|
@@ -24,92 +24,21 @@ public enum SocketError: Error {
|
|
|
case getNameInfoFailed(String)
|
|
|
case acceptFailed(String)
|
|
|
case recvFailed(String)
|
|
|
+ case getSockNameFailed(String)
|
|
|
}
|
|
|
|
|
|
public class Socket: Hashable, Equatable {
|
|
|
|
|
|
- public class func tcpSocketForListen(_ port: in_port_t, forceIPv4: Bool = false, maxPendingConnection: Int32 = SOMAXCONN) throws -> Socket {
|
|
|
-
|
|
|
- #if os(Linux)
|
|
|
- let socketFileDescriptor = socket(forceIPv4 ? AF_INET : AF_INET6, Int32(SOCK_STREAM.rawValue), 0)
|
|
|
- #else
|
|
|
- let socketFileDescriptor = socket(forceIPv4 ? AF_INET : AF_INET6, SOCK_STREAM, 0)
|
|
|
- #endif
|
|
|
-
|
|
|
- if socketFileDescriptor == -1 {
|
|
|
- throw SocketError.socketCreationFailed(Socket.descriptionOfLastError())
|
|
|
- }
|
|
|
-
|
|
|
- var value: Int32 = 1
|
|
|
- if setsockopt(socketFileDescriptor, SOL_SOCKET, SO_REUSEADDR, &value, socklen_t(sizeof(Int32.self))) == -1 {
|
|
|
- let details = Socket.descriptionOfLastError()
|
|
|
- Socket.release(socketFileDescriptor)
|
|
|
- throw SocketError.socketSettingReUseAddrFailed(details)
|
|
|
- }
|
|
|
- Socket.setNoSigPipe(socketFileDescriptor)
|
|
|
-
|
|
|
- #if os(Linux)
|
|
|
- var bindResult: Int32 = -1
|
|
|
- if forceIPv4 {
|
|
|
- var addr = sockaddr_in(sin_family: sa_family_t(AF_INET),
|
|
|
- sin_port: Socket.htonsPort(port),
|
|
|
- sin_addr: in_addr(s_addr: in_addr_t(0)),
|
|
|
- sin_zero:(0, 0, 0, 0, 0, 0, 0, 0))
|
|
|
-
|
|
|
- bindResult = withUnsafePointer(&addr) { bind(socketFileDescriptor, UnsafePointer<sockaddr>($0), socklen_t(sizeof(sockaddr_in))) }
|
|
|
- } else {
|
|
|
- var addr = sockaddr_in6(sin6_family: sa_family_t(AF_INET6),
|
|
|
- sin6_port: Socket.htonsPort(port),
|
|
|
- sin6_flowinfo: 0,
|
|
|
- sin6_addr: in6addr_any,
|
|
|
- sin6_scope_id: 0)
|
|
|
-
|
|
|
- bindResult = withUnsafePointer(&addr) { bind(socketFileDescriptor, UnsafePointer<sockaddr>($0), socklen_t(sizeof(sockaddr_in6))) }
|
|
|
- }
|
|
|
- #else
|
|
|
- var bindResult: Int32 = -1
|
|
|
- if forceIPv4 {
|
|
|
- var addr = sockaddr_in(sin_len: UInt8(strideof(sockaddr_in.self)),
|
|
|
- sin_family: UInt8(AF_INET),
|
|
|
- sin_port: Socket.htonsPort(port),
|
|
|
- sin_addr: in_addr(s_addr: in_addr_t(0)),
|
|
|
- sin_zero:(0, 0, 0, 0, 0, 0, 0, 0))
|
|
|
-
|
|
|
- bindResult = withUnsafePointer(&addr) { bind(socketFileDescriptor, UnsafePointer<sockaddr>($0), socklen_t(sizeof(sockaddr_in.self))) }
|
|
|
- } else {
|
|
|
- // “Apple recommends always making an IPv6 socket to listen on. The OS will automatically
|
|
|
- // “downgrade” it to an IPv4 socket if necessary, so there is no need to listen on two different sockets”.
|
|
|
- var addr = sockaddr_in6(sin6_len: UInt8(strideof(sockaddr_in6.self)),
|
|
|
- sin6_family: UInt8(AF_INET6),
|
|
|
- sin6_port: Socket.htonsPort(port),
|
|
|
- sin6_flowinfo: 0,
|
|
|
- sin6_addr: in6addr_any,
|
|
|
- sin6_scope_id: 0)
|
|
|
-
|
|
|
- bindResult = withUnsafePointer(&addr) { bind(socketFileDescriptor, UnsafePointer<sockaddr>($0), socklen_t(sizeof(sockaddr_in6.self))) }
|
|
|
- }
|
|
|
- #endif
|
|
|
-
|
|
|
- if bindResult == -1 {
|
|
|
- let details = Socket.descriptionOfLastError()
|
|
|
- Socket.release(socketFileDescriptor)
|
|
|
- throw SocketError.bindFailed(details)
|
|
|
- }
|
|
|
-
|
|
|
- if listen(socketFileDescriptor, maxPendingConnection ) == -1 {
|
|
|
- let details = Socket.descriptionOfLastError()
|
|
|
- Socket.release(socketFileDescriptor)
|
|
|
- throw SocketError.listenFailed(details)
|
|
|
- }
|
|
|
- return Socket(socketFileDescriptor: socketFileDescriptor)
|
|
|
- }
|
|
|
-
|
|
|
- internal let socketFileDescriptor: Int32
|
|
|
+ let socketFileDescriptor: Int32
|
|
|
|
|
|
public init(socketFileDescriptor: Int32) {
|
|
|
self.socketFileDescriptor = socketFileDescriptor
|
|
|
}
|
|
|
|
|
|
+ deinit {
|
|
|
+ shutdwn()
|
|
|
+ }
|
|
|
+
|
|
|
public var hashValue: Int { return Int(self.socketFileDescriptor) }
|
|
|
|
|
|
public func release() {
|
|
|
@@ -120,15 +49,30 @@ public class Socket: Hashable, Equatable {
|
|
|
Socket.shutdwn(self.socketFileDescriptor)
|
|
|
}
|
|
|
|
|
|
- public func acceptClientSocket() throws -> Socket {
|
|
|
- var addr = sockaddr()
|
|
|
- var len: socklen_t = 0
|
|
|
- let clientSocket = accept(self.socketFileDescriptor, &addr, &len)
|
|
|
- if clientSocket == -1 {
|
|
|
- throw SocketError.acceptFailed(Socket.descriptionOfLastError())
|
|
|
+ public func port() throws -> in_port_t {
|
|
|
+ var addr = sockaddr_in()
|
|
|
+ return try withUnsafePointer(&addr) { pointer in
|
|
|
+ var len = socklen_t(sizeof(sockaddr_in.self))
|
|
|
+ if getsockname(socketFileDescriptor, UnsafeMutablePointer(pointer), &len) != 0 {
|
|
|
+ throw SocketError.getSockNameFailed(Errno.description)
|
|
|
+ }
|
|
|
+ #if os(Linux)
|
|
|
+ return ntohs(addr.sin_port)
|
|
|
+ #else
|
|
|
+ return Int(OSHostByteOrder()) != OSLittleEndian ? addr.sin_port.littleEndian : addr.sin_port.bigEndian
|
|
|
+ #endif
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ public func isIPv4() throws -> Bool {
|
|
|
+ var addr = sockaddr_in()
|
|
|
+ return try withUnsafePointer(&addr) { pointer in
|
|
|
+ var len = socklen_t(sizeof(sockaddr_in.self))
|
|
|
+ if getsockname(socketFileDescriptor, UnsafeMutablePointer(pointer), &len) != 0 {
|
|
|
+ throw SocketError.getSockNameFailed(Errno.description)
|
|
|
+ }
|
|
|
+ return Int32(addr.sin_family) == AF_INET
|
|
|
}
|
|
|
- Socket.setNoSigPipe(clientSocket)
|
|
|
- return Socket(socketFileDescriptor: clientSocket)
|
|
|
}
|
|
|
|
|
|
public func writeUTF8(_ string: String) throws {
|
|
|
@@ -152,7 +96,7 @@ public class Socket: Hashable, Equatable {
|
|
|
let s = write(self.socketFileDescriptor, baseAddress + sent, Int(data.count - sent))
|
|
|
#endif
|
|
|
if s <= 0 {
|
|
|
- throw SocketError.writeFailed(Socket.descriptionOfLastError())
|
|
|
+ throw SocketError.writeFailed(Errno.description)
|
|
|
}
|
|
|
sent += s
|
|
|
}
|
|
|
@@ -163,7 +107,7 @@ public class Socket: Hashable, Equatable {
|
|
|
var buffer = [UInt8](repeating: 0, count: 1)
|
|
|
let next = recv(self.socketFileDescriptor as Int32, &buffer, Int(buffer.count), 0)
|
|
|
if next <= 0 {
|
|
|
- throw SocketError.recvFailed(Socket.descriptionOfLastError())
|
|
|
+ throw SocketError.recvFailed(Errno.description)
|
|
|
}
|
|
|
return buffer[0]
|
|
|
}
|
|
|
@@ -184,20 +128,16 @@ public class Socket: Hashable, Equatable {
|
|
|
public func peername() throws -> String {
|
|
|
var addr = sockaddr(), len: socklen_t = socklen_t(sizeof(sockaddr.self))
|
|
|
if getpeername(self.socketFileDescriptor, &addr, &len) != 0 {
|
|
|
- throw SocketError.getPeerNameFailed(Socket.descriptionOfLastError())
|
|
|
+ throw SocketError.getPeerNameFailed(Errno.description)
|
|
|
}
|
|
|
var hostBuffer = [CChar](repeating: 0, count: Int(NI_MAXHOST))
|
|
|
if getnameinfo(&addr, len, &hostBuffer, socklen_t(hostBuffer.count), nil, 0, NI_NUMERICHOST) != 0 {
|
|
|
- throw SocketError.getNameInfoFailed(Socket.descriptionOfLastError())
|
|
|
+ throw SocketError.getNameInfoFailed(Errno.description)
|
|
|
}
|
|
|
return String(cString: hostBuffer)
|
|
|
}
|
|
|
|
|
|
- private class func descriptionOfLastError() -> String {
|
|
|
- return String(cString: UnsafePointer(strerror(errno))) ?? "Error: \(errno)"
|
|
|
- }
|
|
|
-
|
|
|
- private class func setNoSigPipe(_ socket: Int32) {
|
|
|
+ public class func setNoSigPipe(_ socket: Int32) {
|
|
|
#if os(Linux)
|
|
|
// There is no SO_NOSIGPIPE in Linux (nor some other systems). You can instead use the MSG_NOSIGNAL flag when calling send(),
|
|
|
// or use signal(SIGPIPE, SIG_IGN) to make your entire application ignore SIGPIPE.
|
|
|
@@ -208,7 +148,7 @@ public class Socket: Hashable, Equatable {
|
|
|
#endif
|
|
|
}
|
|
|
|
|
|
- private class func shutdwn(_ socket: Int32) {
|
|
|
+ public class func shutdwn(_ socket: Int32) {
|
|
|
#if os(Linux)
|
|
|
shutdown(socket, Int32(SHUT_RDWR))
|
|
|
#else
|
|
|
@@ -216,7 +156,7 @@ public class Socket: Hashable, Equatable {
|
|
|
#endif
|
|
|
}
|
|
|
|
|
|
- private class func release(_ socket: Int32) {
|
|
|
+ public class func release(_ socket: Int32) {
|
|
|
#if os(Linux)
|
|
|
shutdown(socket, Int32(SHUT_RDWR))
|
|
|
close(socket)
|
|
|
@@ -228,15 +168,6 @@ public class Socket: Hashable, Equatable {
|
|
|
}
|
|
|
#endif
|
|
|
}
|
|
|
-
|
|
|
- private class func htonsPort(_ port: in_port_t) -> in_port_t {
|
|
|
- #if os(Linux)
|
|
|
- return htons(port)
|
|
|
- #else
|
|
|
- let isLittleEndian = Int(OSHostByteOrder()) == OSLittleEndian
|
|
|
- return isLittleEndian ? _OSSwapInt16(port) : port
|
|
|
- #endif
|
|
|
- }
|
|
|
}
|
|
|
|
|
|
public func ==(socket1: Socket, socket2: Socket) -> Bool {
|