| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196 |
- //
- // Socket.swift
- // Swifter
- //
- // Copyright (c) 2014-2016 Damian Kołakowski. All rights reserved.
- //
- #if os(Linux)
- import Glibc
- #else
- import Foundation
- #endif
- /* Low level routines for POSIX sockets */
- public enum SocketError: Error {
- case socketCreationFailed(String)
- case socketSettingReUseAddrFailed(String)
- case bindFailed(String)
- case listenFailed(String)
- case writeFailed(String)
- case getPeerNameFailed(String)
- case convertingPeerNameFailed
- case getNameInfoFailed(String)
- case acceptFailed(String)
- case recvFailed(String)
- case getSockNameFailed(String)
- }
- open class Socket: Hashable, Equatable {
-
- let socketFileDescriptor: Int32
- private var shutdown = false
-
- public init(socketFileDescriptor: Int32) {
- self.socketFileDescriptor = socketFileDescriptor
- }
-
- deinit {
- shutdwn()
- }
-
- public var hashValue: Int { return Int(self.socketFileDescriptor) }
-
- public func release() {
- if shutdown {
- return
- }
- shutdown = true
- Socket.release(self.socketFileDescriptor)
- }
-
- public func shutdwn() {
- if shutdown {
- return
- }
- shutdown = true
- Socket.shutdwn(self.socketFileDescriptor)
- }
-
- public func port() throws -> in_port_t {
- var addr = sockaddr_in()
- return try withUnsafePointer(to: &addr) { pointer in
- var len = socklen_t(MemoryLayout<sockaddr_in>.size)
- if getsockname(socketFileDescriptor, UnsafeMutablePointer(OpaquePointer(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(to: &addr) { pointer in
- var len = socklen_t(MemoryLayout<sockaddr_in>.size)
- if getsockname(socketFileDescriptor, UnsafeMutablePointer(OpaquePointer(pointer)), &len) != 0 {
- throw SocketError.getSockNameFailed(Errno.description())
- }
- return Int32(addr.sin_family) == AF_INET
- }
- }
-
- public func writeUTF8(_ string: String) throws {
- try writeUInt8(ArraySlice(string.utf8))
- }
-
- public func writeUInt8(_ data: [UInt8]) throws {
- try writeUInt8(ArraySlice(data))
- }
-
- public func writeUInt8(_ data: ArraySlice<UInt8>) throws {
- try data.withUnsafeBufferPointer {
- try writeBuffer($0.baseAddress!, length: data.count)
- }
- }
- public func writeData(_ data: NSData) throws {
- try writeBuffer(data.bytes, length: data.length)
- }
-
- public func writeData(_ data: Data) throws {
- try data.withUnsafeBytes { (pointer: UnsafePointer<UInt8>) -> Void in
- try self.writeBuffer(pointer, length: data.count)
- }
- }
- private func writeBuffer(_ pointer: UnsafeRawPointer, length: Int) throws {
- var sent = 0
- while sent < length {
- #if os(Linux)
- let s = send(self.socketFileDescriptor, pointer + sent, Int(length - sent), Int32(MSG_NOSIGNAL))
- #else
- let s = write(self.socketFileDescriptor, pointer + sent, Int(length - sent))
- #endif
- if s <= 0 {
- throw SocketError.writeFailed(Errno.description())
- }
- sent += s
- }
- }
-
- open func read() throws -> UInt8 {
- 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(Errno.description())
- }
- return buffer[0]
- }
-
- private static let CR = UInt8(13)
- private static let NL = UInt8(10)
-
- public func readLine() throws -> String {
- var characters: String = ""
- var n: UInt8 = 0
- repeat {
- n = try self.read()
- if n > Socket.CR { characters.append(Character(UnicodeScalar(n))) }
- } while n != Socket.NL
- return characters
- }
-
- public func peername() throws -> String {
- var addr = sockaddr(), len: socklen_t = socklen_t(MemoryLayout<sockaddr>.size)
- if getpeername(self.socketFileDescriptor, &addr, &len) != 0 {
- 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(Errno.description())
- }
- return String(cString: hostBuffer)
- }
-
- 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.
- #else
- // Prevents crashes when blocking calls are pending and the app is paused ( via Home button ).
- var no_sig_pipe: Int32 = 1
- setsockopt(socket, SOL_SOCKET, SO_NOSIGPIPE, &no_sig_pipe, socklen_t(MemoryLayout<Int32>.size))
- #endif
- }
-
- public class func shutdwn(_ socket: Int32) {
- #if os(Linux)
- shutdown(socket, Int32(SHUT_RDWR))
- #else
- let _ = Darwin.shutdown(socket, SHUT_RDWR)
- #endif
- }
-
- public class func release(_ socket: Int32) {
- #if os(Linux)
- shutdown(socket, Int32(SHUT_RDWR))
- close(socket)
- #else
- if Darwin.shutdown(socket, SHUT_RDWR) != -1 {
- // If you close socket which was already closed it produces exception visible in TestFlight's crash log.
- // This is easily can be fixed by checking result on shutdown function != -1.
- close(socket)
- }
- #endif
- }
- }
- public func == (socket1: Socket, socket2: Socket) -> Bool {
- return socket1.socketFileDescriptor == socket2.socketFileDescriptor
- }
|