diff --git a/ListenerApp.xcodeproj/project.pbxproj b/ListenerApp.xcodeproj/project.pbxproj index f10fa66..deffbc0 100644 --- a/ListenerApp.xcodeproj/project.pbxproj +++ b/ListenerApp.xcodeproj/project.pbxproj @@ -15,6 +15,12 @@ 9D51561026A1EF7C0075EBC7 /* ListenerAppUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9D51560F26A1EF7C0075EBC7 /* ListenerAppUITests.swift */; }; 9D51562226A1F0DF0075EBC7 /* LICENSE in Resources */ = {isa = PBXBuildFile; fileRef = 9D51562126A1F0DF0075EBC7 /* LICENSE */; }; 9D51563126A278BB0075EBC7 /* Speech.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9D51563026A278BB0075EBC7 /* Speech.framework */; }; + 9D51565226A36B410075EBC7 /* Result.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9D51564C26A36B410075EBC7 /* Result.swift */; }; + 9D51565326A36B410075EBC7 /* yudpsocket.c in Sources */ = {isa = PBXBuildFile; fileRef = 9D51564D26A36B410075EBC7 /* yudpsocket.c */; }; + 9D51565426A36B410075EBC7 /* ytcpsocket.c in Sources */ = {isa = PBXBuildFile; fileRef = 9D51564E26A36B410075EBC7 /* ytcpsocket.c */; }; + 9D51565526A36B410075EBC7 /* UDPClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9D51564F26A36B410075EBC7 /* UDPClient.swift */; }; + 9D51565626A36B410075EBC7 /* Socket.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9D51565026A36B410075EBC7 /* Socket.swift */; }; + 9D51565726A36B410075EBC7 /* TCPClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9D51565126A36B410075EBC7 /* TCPClient.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -50,6 +56,14 @@ 9D51562126A1F0DF0075EBC7 /* LICENSE */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = LICENSE; sourceTree = ""; }; 9D51562A26A1F1B40075EBC7 /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; 9D51563026A278BB0075EBC7 /* Speech.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Speech.framework; path = System/Library/Frameworks/Speech.framework; sourceTree = SDKROOT; }; + 9D51563726A36AF20075EBC7 /* ListenerApp-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "ListenerApp-Bridging-Header.h"; sourceTree = ""; }; + 9D51564B26A36B410075EBC7 /* SwiftSocket.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SwiftSocket.h; sourceTree = ""; }; + 9D51564C26A36B410075EBC7 /* Result.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Result.swift; sourceTree = ""; }; + 9D51564D26A36B410075EBC7 /* yudpsocket.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = yudpsocket.c; sourceTree = ""; }; + 9D51564E26A36B410075EBC7 /* ytcpsocket.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ytcpsocket.c; sourceTree = ""; }; + 9D51564F26A36B410075EBC7 /* UDPClient.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UDPClient.swift; sourceTree = ""; }; + 9D51565026A36B410075EBC7 /* Socket.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Socket.swift; sourceTree = ""; }; + 9D51565126A36B410075EBC7 /* TCPClient.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TCPClient.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -106,6 +120,7 @@ children = ( 9D5155F226A1EF7B0075EBC7 /* ListenerAppApp.swift */, 9D5155F426A1EF7B0075EBC7 /* ContentView.swift */, + 9D51563626A36AD60075EBC7 /* SwiftSocket */, 9D5155F626A1EF7C0075EBC7 /* Assets.xcassets */, 9D5155FB26A1EF7C0075EBC7 /* Info.plist */, 9D5155F826A1EF7C0075EBC7 /* Preview Content */, @@ -147,6 +162,21 @@ name = Frameworks; sourceTree = ""; }; + 9D51563626A36AD60075EBC7 /* SwiftSocket */ = { + isa = PBXGroup; + children = ( + 9D51564C26A36B410075EBC7 /* Result.swift */, + 9D51565026A36B410075EBC7 /* Socket.swift */, + 9D51564B26A36B410075EBC7 /* SwiftSocket.h */, + 9D51565126A36B410075EBC7 /* TCPClient.swift */, + 9D51564F26A36B410075EBC7 /* UDPClient.swift */, + 9D51564E26A36B410075EBC7 /* ytcpsocket.c */, + 9D51564D26A36B410075EBC7 /* yudpsocket.c */, + 9D51563726A36AF20075EBC7 /* ListenerApp-Bridging-Header.h */, + ); + path = SwiftSocket; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -215,6 +245,7 @@ TargetAttributes = { 9D5155EE26A1EF7B0075EBC7 = { CreatedOnToolsVersion = 12.4; + LastSwiftMigration = 1240; }; 9D5155FF26A1EF7C0075EBC7 = { CreatedOnToolsVersion = 12.4; @@ -298,8 +329,14 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 9D51565726A36B410075EBC7 /* TCPClient.swift in Sources */, + 9D51565526A36B410075EBC7 /* UDPClient.swift in Sources */, + 9D51565626A36B410075EBC7 /* Socket.swift in Sources */, + 9D51565326A36B410075EBC7 /* yudpsocket.c in Sources */, + 9D51565226A36B410075EBC7 /* Result.swift in Sources */, 9D5155F526A1EF7B0075EBC7 /* ContentView.swift in Sources */, 9D5155F326A1EF7B0075EBC7 /* ListenerAppApp.swift in Sources */, + 9D51565426A36B410075EBC7 /* ytcpsocket.c in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -456,6 +493,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CLANG_ENABLE_MODULES = YES; CODE_SIGN_STYLE = Automatic; DEVELOPMENT_ASSET_PATHS = "\"ListenerApp/Preview Content\""; DEVELOPMENT_TEAM = VD9FGCW36C; @@ -468,6 +506,8 @@ ); PRODUCT_BUNDLE_IDENTIFIER = com.halcyontouch.ListenerApp; PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "ListenerApp/SwiftSocket/ListenerApp-Bridging-Header.h"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; }; @@ -478,6 +518,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CLANG_ENABLE_MODULES = YES; CODE_SIGN_STYLE = Automatic; DEVELOPMENT_ASSET_PATHS = "\"ListenerApp/Preview Content\""; DEVELOPMENT_TEAM = VD9FGCW36C; @@ -490,6 +531,7 @@ ); PRODUCT_BUNDLE_IDENTIFIER = com.halcyontouch.ListenerApp; PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "ListenerApp/SwiftSocket/ListenerApp-Bridging-Header.h"; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; }; diff --git a/ListenerApp/Info.plist b/ListenerApp/Info.plist index 220e1b2..38a0acc 100644 --- a/ListenerApp/Info.plist +++ b/ListenerApp/Info.plist @@ -17,7 +17,7 @@ CFBundleShortVersionString 1.0 CFBundleVersion - 39 + 42 LSRequiresIPhoneOS NSSpeechRecognitionUsageDescription diff --git a/ListenerApp/SwiftSocket/ListenerApp-Bridging-Header.h b/ListenerApp/SwiftSocket/ListenerApp-Bridging-Header.h new file mode 100644 index 0000000..1b2cb5d --- /dev/null +++ b/ListenerApp/SwiftSocket/ListenerApp-Bridging-Header.h @@ -0,0 +1,4 @@ +// +// Use this file to import your target's public headers that you would like to expose to Swift. +// + diff --git a/ListenerApp/SwiftSocket/Result.swift b/ListenerApp/SwiftSocket/Result.swift new file mode 100644 index 0000000..880a287 --- /dev/null +++ b/ListenerApp/SwiftSocket/Result.swift @@ -0,0 +1,28 @@ +import Foundation + +public enum Result { + case success + case failure(Error) + + public var isSuccess: Bool { + switch self { + case .success: + return true + case .failure: + return false + } + } + + public var isFailure: Bool { + return !isSuccess + } + + public var error: Error? { + switch self { + case .success: + return nil + case .failure(let error): + return error + } + } +} diff --git a/ListenerApp/SwiftSocket/Socket.swift b/ListenerApp/SwiftSocket/Socket.swift new file mode 100644 index 0000000..f49635b --- /dev/null +++ b/ListenerApp/SwiftSocket/Socket.swift @@ -0,0 +1,53 @@ +// +// Copyright (c) <2014>, skysent +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// 3. All advertising materials mentioning features or use of this software +// must display the following acknowledgement: +// This product includes software developed by skysent. +// 4. Neither the name of the skysent nor the +// names of its contributors may be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY skysent ''AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL skysent BE LIABLE FOR ANY +// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// + +import Foundation + +public typealias Byte = UInt8 + +open class Socket { + + public let address: String + internal(set) public var port: Int32 + internal(set) public var fd: Int32? + + public init(address: String, port: Int32) { + self.address = address + self.port = port + } + +} + +public enum SocketError: Error { + case queryFailed + case connectionClosed + case connectionTimeout + case unknownError +} diff --git a/ListenerApp/SwiftSocket/SwiftSocket.h b/ListenerApp/SwiftSocket/SwiftSocket.h new file mode 100644 index 0000000..dc196c5 --- /dev/null +++ b/ListenerApp/SwiftSocket/SwiftSocket.h @@ -0,0 +1,9 @@ +@import Foundation; + +//! Project version number for SwiftSocket +FOUNDATION_EXPORT double SwiftSocketVersionNumber; + +//! Project version string for SwiftSocket +FOUNDATION_EXPORT const unsigned char SwiftSocketVersionString[]; + +// In this header, you should import all the public headers of your framework using statements like #import diff --git a/ListenerApp/SwiftSocket/TCPClient.swift b/ListenerApp/SwiftSocket/TCPClient.swift new file mode 100644 index 0000000..9489f16 --- /dev/null +++ b/ListenerApp/SwiftSocket/TCPClient.swift @@ -0,0 +1,201 @@ +// +// Copyright (c) <2014>, skysent +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// 3. All advertising materials mentioning features or use of this software +// must display the following acknowledgement: +// This product includes software developed by skysent. +// 4. Neither the name of the skysent nor the +// names of its contributors may be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY skysent ''AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL skysent BE LIABLE FOR ANY +// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// + +import Foundation + +@_silgen_name("ytcpsocket_connect") private func c_ytcpsocket_connect(_ host:UnsafePointer,port:Int32,timeout:Int32) -> Int32 +@_silgen_name("ytcpsocket_close") private func c_ytcpsocket_close(_ fd:Int32) -> Int32 +@_silgen_name("ytcpsocket_bytes_available") private func c_ytcpsocket_bytes_available(_ fd:Int32) -> Int32 +@_silgen_name("ytcpsocket_send") private func c_ytcpsocket_send(_ fd:Int32,buff:UnsafePointer,len:Int32) -> Int32 +@_silgen_name("ytcpsocket_pull") private func c_ytcpsocket_pull(_ fd:Int32,buff:UnsafePointer,len:Int32,timeout:Int32) -> Int32 +@_silgen_name("ytcpsocket_listen") private func c_ytcpsocket_listen(_ address:UnsafePointer,port:Int32)->Int32 +@_silgen_name("ytcpsocket_accept") private func c_ytcpsocket_accept(_ onsocketfd:Int32,ip:UnsafePointer,port:UnsafePointer,timeout:Int32) -> Int32 +@_silgen_name("ytcpsocket_port") private func c_ytcpsocket_port(_ fd:Int32) -> Int32 + +open class TCPClient: Socket { + /* + * connect to server + * return success or fail with message + */ + open func connect(timeout: Int) -> Result { + let rs: Int32 = c_ytcpsocket_connect(self.address, port: Int32(self.port), timeout: Int32(timeout)) + if rs > 0 { + self.fd = rs + return .success + } else { + switch rs { + case -1: + return .failure(SocketError.queryFailed) + case -2: + return .failure(SocketError.connectionClosed) + case -3: + return .failure(SocketError.connectionTimeout) + default: + return .failure(SocketError.unknownError) + } + } + } + + /* + * close socket + * return success or fail with message + */ + open func close() { + guard let fd = self.fd else { return } + + _ = c_ytcpsocket_close(fd) + self.fd = nil + } + + /* + * send data + * return success or fail with message + */ + open func send(data: [Byte]) -> Result { + guard let fd = self.fd else { return .failure(SocketError.connectionClosed) } + + let sendsize: Int32 = c_ytcpsocket_send(fd, buff: data, len: Int32(data.count)) + if Int(sendsize) == data.count { + return .success + } else { + return .failure(SocketError.unknownError) + } + } + + /* + * send string + * return success or fail with message + */ + open func send(string: String) -> Result { + guard let fd = self.fd else { return .failure(SocketError.connectionClosed) } + + let sendsize = c_ytcpsocket_send(fd, buff: string, len: Int32(strlen(string))) + if sendsize == Int32(strlen(string)) { + return .success + } else { + return .failure(SocketError.unknownError) + } + } + + /* + * + * send nsdata + */ + open func send(data: Data) -> Result { + guard let fd = self.fd else { return .failure(SocketError.connectionClosed) } + + var buff = [Byte](repeating: 0x0,count: data.count) + (data as NSData).getBytes(&buff, length: data.count) + let sendsize = c_ytcpsocket_send(fd, buff: buff, len: Int32(data.count)) + if sendsize == Int32(data.count) { + return .success + } else { + return .failure(SocketError.unknownError) + } + } + + /* + * read data with expect length + * return success or fail with message + */ + open func read(_ expectlen:Int, timeout:Int = -1) -> [Byte]? { + guard let fd:Int32 = self.fd else { return nil } + + var buff = [Byte](repeating: 0x0,count: expectlen) + let readLen = c_ytcpsocket_pull(fd, buff: &buff, len: Int32(expectlen), timeout: Int32(timeout)) + if readLen <= 0 { return nil } + let rs = buff[0...Int(readLen-1)] + let data: [Byte] = Array(rs) + + return data + } + + /* + * gets byte available for reading + */ + open func bytesAvailable() -> Int32? { + guard let fd:Int32 = self.fd else { return nil } + + let bytesAvailable = c_ytcpsocket_bytes_available(fd); + + if (bytesAvailable < 0) { + return nil + } + + return bytesAvailable + } +} + +open class TCPServer: Socket { + + open func listen() -> Result { + let fd = c_ytcpsocket_listen(self.address, port: Int32(self.port)) + if fd > 0 { + self.fd = fd + + // If port 0 is used, get the actual port number which the server is listening to + if (self.port == 0) { + let p = c_ytcpsocket_port(fd) + if (p == -1) { + return .failure(SocketError.unknownError) + } else { + self.port = p + } + } + + return .success + } else { + return .failure(SocketError.unknownError) + } + } + + open func accept(timeout :Int32 = 0) -> TCPClient? { + guard let serferfd = self.fd else { return nil } + + var buff: [Int8] = [Int8](repeating: 0x0,count: 16) + var port: Int32 = 0 + let clientfd: Int32 = c_ytcpsocket_accept(serferfd, ip: &buff, port: &port, timeout: timeout) + + guard clientfd >= 0 else { return nil } + guard let address = String(cString: buff, encoding: String.Encoding.utf8) else { return nil } + + let client = TCPClient(address: address, port: port) + client.fd = clientfd + + return client + } + + open func close() { + guard let fd: Int32=self.fd else { return } + + _ = c_ytcpsocket_close(fd) + self.fd = nil + } +} diff --git a/ListenerApp/SwiftSocket/UDPClient.swift b/ListenerApp/SwiftSocket/UDPClient.swift new file mode 100644 index 0000000..305ffb0 --- /dev/null +++ b/ListenerApp/SwiftSocket/UDPClient.swift @@ -0,0 +1,185 @@ +// +// Copyright (c) <2014>, skysent +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// 3. All advertising materials mentioning features or use of this software +// must display the following acknowledgement: +// This product includes software developed by skysent. +// 4. Neither the name of the skysent nor the +// names of its contributors may be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY skysent ''AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL skysent BE LIABLE FOR ANY +// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// + +import Foundation + +@_silgen_name("yudpsocket_server") func c_yudpsocket_server(_ host:UnsafePointer,port:Int32) -> Int32 +@_silgen_name("yudpsocket_recive") func c_yudpsocket_recive(_ fd:Int32,buff:UnsafePointer,len:Int32,ip:UnsafePointer,port:UnsafePointer) -> Int32 +@_silgen_name("yudpsocket_close") func c_yudpsocket_close(_ fd:Int32) -> Int32 +@_silgen_name("yudpsocket_client") func c_yudpsocket_client() -> Int32 +@_silgen_name("yudpsocket_get_server_ip") func c_yudpsocket_get_server_ip(_ host:UnsafePointer,ip:UnsafePointer) -> Int32 +@_silgen_name("yudpsocket_sentto") func c_yudpsocket_sentto(_ fd:Int32,buff:UnsafePointer,len:Int32,ip:UnsafePointer,port:Int32) -> Int32 +@_silgen_name("enable_broadcast") func c_enable_broadcast(_ fd:Int32) + +open class UDPClient: Socket { + public override init(address: String, port: Int32) { + let remoteipbuff: [Int8] = [Int8](repeating: 0x0,count: 16) + let ret = c_yudpsocket_get_server_ip(address, ip: remoteipbuff) + guard let ip = String(cString: remoteipbuff, encoding: String.Encoding.utf8), ret == 0 else { + super.init(address: "", port: 0) //TODO: change to init? + return + } + + super.init(address: ip, port: port) + + let fd: Int32 = c_yudpsocket_client() + if fd > 0 { + self.fd = fd + } + } + + /* + * send data + * return success or fail with message + */ + open func send(data: [Byte]) -> Result { + guard let fd = self.fd else { return .failure(SocketError.connectionClosed) } + + let sendsize: Int32 = c_yudpsocket_sentto(fd, buff: data, len: Int32(data.count), ip: self.address, port: Int32(self.port)) + if Int(sendsize) == data.count { + return .success + } else { + return .failure(SocketError.unknownError) + } + } + + /* + * send string + * return success or fail with message + */ + open func send(string: String) -> Result { + guard let fd = self.fd else { return .failure(SocketError.connectionClosed) } + + let sendsize = c_yudpsocket_sentto(fd, buff: string, len: Int32(strlen(string)), ip: address, port: port) + if sendsize == Int32(strlen(string)) { + return .success + } else { + return .failure(SocketError.unknownError) + } + } + + /* + * enableBroadcast + */ + open func enableBroadcast() { + guard let fd: Int32 = self.fd else { return } + + c_enable_broadcast(fd) + } + + /* + * + * send nsdata + */ + open func send(data: Data) -> Result { + guard let fd = self.fd else { return .failure(SocketError.connectionClosed) } + + var buff = [Byte](repeating: 0x0,count: data.count) + (data as NSData).getBytes(&buff, length: data.count) + let sendsize = c_yudpsocket_sentto(fd, buff: buff, len: Int32(data.count), ip: address, port: port) + if sendsize == Int32(data.count) { + return .success + } else { + return .failure(SocketError.unknownError) + } + } + + //TODO add multycast and boardcast + open func recv(_ expectlen: Int) -> ([Byte]?, String, Int) { + guard let fd = self.fd else { + return (nil, "no ip", 0) + } + var buff: [Byte] = [Byte](repeating: 0x0, count: expectlen) + var remoteipbuff: [Int8] = [Int8](repeating: 0x0, count: 16) + var remoteport: Int32 = 0 + let readLen: Int32 = c_yudpsocket_recive(fd, buff: buff, len: Int32(expectlen), ip: &remoteipbuff, port: &remoteport) + let port: Int = Int(remoteport) + let address = String(cString: remoteipbuff, encoding: String.Encoding.utf8) ?? "" + + if readLen <= 0 { + return (nil, address, port) + } + + let data: [Byte] = Array(buff[0.. 0 { + self.fd = fd + } + } + + //TODO add multycast and boardcast + open func recv(_ expectlen: Int) -> ([Byte]?, String, Int) { + if let fd = self.fd { + var buff: [Byte] = [Byte](repeating: 0x0,count: expectlen) + var remoteipbuff: [Int8] = [Int8](repeating: 0x0,count: 16) + var remoteport: Int32 = 0 + let readLen: Int32 = c_yudpsocket_recive(fd, buff: buff, len: Int32(expectlen), ip: &remoteipbuff, port: &remoteport) + let port: Int = Int(remoteport) + var address = "" + if let ip = String(cString: remoteipbuff, encoding: String.Encoding.utf8) { + address = ip + } + + if readLen <= 0 { + return (nil, address, port) + } + + let rs = buff[0...Int(readLen-1)] + let data: [Byte] = Array(rs) + return (data, address, port) + } + + return (nil, "no ip", 0) + } + + open func close() { + guard let fd = self.fd else { return } + + _ = c_yudpsocket_close(fd) + self.fd = nil + } +} diff --git a/ListenerApp/SwiftSocket/ytcpsocket.c b/ListenerApp/SwiftSocket/ytcpsocket.c new file mode 100644 index 0000000..50ad313 --- /dev/null +++ b/ListenerApp/SwiftSocket/ytcpsocket.c @@ -0,0 +1,223 @@ +// +// Copyright (c) <2014>, skysent +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// 3. All advertising materials mentioning features or use of this software +// must display the following acknowledgement: +// This product includes software developed by skysent. +// 4. Neither the name of the skysent nor the +// names of its contributors may be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY skysent ''AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL skysent BE LIABLE FOR ANY +// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +void ytcpsocket_set_block(int socket, int on) { + int flags; + flags = fcntl(socket, F_GETFL, 0); + if (on == 0) { + fcntl(socket, F_SETFL, flags | O_NONBLOCK); + } else { + flags &= ~ O_NONBLOCK; + fcntl(socket, F_SETFL, flags); + } +} + +int ytcpsocket_connect(const char *host, int port, int timeout) { + struct sockaddr_in sa; + struct hostent *hp; + int sockfd = -1; + hp = gethostbyname(host); + if (hp == NULL) { + return -1; + } + + bcopy((char *)hp->h_addr, (char *)&sa.sin_addr, hp->h_length); + sa.sin_family = hp->h_addrtype; + sa.sin_port = htons(port); + sockfd = socket(hp->h_addrtype, SOCK_STREAM, 0); + ytcpsocket_set_block(sockfd,0); + connect(sockfd, (struct sockaddr *)&sa, sizeof(sa)); + fd_set fdwrite; + struct timeval tvSelect; + FD_ZERO(&fdwrite); + FD_SET(sockfd, &fdwrite); + tvSelect.tv_sec = timeout; + tvSelect.tv_usec = 0; + + int retval = select(sockfd + 1, NULL, &fdwrite, NULL, &tvSelect); + if (retval < 0) { + close(sockfd); + return -2; + } else if(retval == 0) {//timeout + close(sockfd); + return -3; + } else { + int error = 0; + int errlen = sizeof(error); + getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &error, (socklen_t *)&errlen); + if (error != 0) { + close(sockfd); + return -4;//connect fail + } + + int set = 1; + setsockopt(sockfd, SOL_SOCKET, SO_NOSIGPIPE, (void *)&set, sizeof(int)); + return sockfd; + } +} + +int ytcpsocket_close(int socketfd){ + return close(socketfd); +} + +int ytcpsocket_pull(int socketfd, char *data, int len, int timeout_sec) { + int readlen = 0; + int datalen = 0; + if (timeout_sec > 0) { + fd_set fdset; + struct timeval timeout; + timeout.tv_usec = 0; + timeout.tv_sec = timeout_sec; + FD_ZERO(&fdset); + FD_SET(socketfd, &fdset); + int ret = select(socketfd + 1, &fdset, NULL, NULL, &timeout); + if (ret <= 0) { + return ret; // select-call failed or timeout occurred (before anything was sent) + } + } + // use loop to make sure receive all data + do { + readlen = (int)read(socketfd, data + datalen, len - datalen); + if (readlen > 0) { + datalen += readlen; + } + } while (readlen > 0); + + return datalen; +} + +int ytcpsocket_bytes_available(int socketfd) { + int count; + int callResult = ioctl(socketfd, FIONREAD, &count); + + if (callResult < 0) { + return callResult; + } + + return count; +} + +int ytcpsocket_send(int socketfd, const char *data, int len){ + int byteswrite = 0; + while (len - byteswrite > 0) { + int writelen = (int)write(socketfd, data + byteswrite, len - byteswrite); + if (writelen < 0) { + return -1; + } + byteswrite += writelen; + } + return byteswrite; +} + +//return socket fd +int ytcpsocket_listen(const char *address, int port) { + //create socket + int socketfd = socket(AF_INET, SOCK_STREAM, 0); + int reuseon = 1; + setsockopt(socketfd, SOL_SOCKET, SO_REUSEADDR, &reuseon, sizeof(reuseon)); + + //bind + struct sockaddr_in serv_addr; + memset( &serv_addr, '\0', sizeof(serv_addr)); + serv_addr.sin_family = AF_INET; + serv_addr.sin_addr.s_addr = inet_addr(address); + serv_addr.sin_port = htons(port); + int r = bind(socketfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)); + if (r == 0) { + if (listen(socketfd, 128) == 0) { + return socketfd; + } else { + return -2;//listen error + } + } else { + return -1;//bind error + } +} + +//return client socket fd +int ytcpsocket_accept(int onsocketfd, char *remoteip, int *remoteport, int timeouts) { + socklen_t clilen; + struct sockaddr_in cli_addr; + clilen = sizeof(cli_addr); + fd_set fdset; + FD_ZERO(&fdset); + FD_SET(onsocketfd, &fdset); + struct timeval *timeptr = NULL; + struct timeval timeout; + if (timeouts > 0) { + timeout.tv_sec = timeouts; + timeout.tv_usec = 0; + timeptr = &timeout; + } + int status = select(FD_SETSIZE, &fdset, NULL, NULL, timeptr); + if (status != 1) { + return -1; + } + int newsockfd = accept(onsocketfd, (struct sockaddr *) &cli_addr, &clilen); + char *clientip=inet_ntoa(cli_addr.sin_addr); + memcpy(remoteip, clientip, strlen(clientip)); + *remoteport = cli_addr.sin_port; + if (newsockfd > 0) { + int set = 1; + setsockopt(newsockfd, SOL_SOCKET, SO_NOSIGPIPE, (void*) &set, sizeof(int)); + return newsockfd; + } else { + return -1; + } +} + +//return socket port +int ytcpsocket_port(int socketfd) { + struct sockaddr_in sin; + socklen_t len = sizeof(sin); + if (getsockname(socketfd, (struct sockaddr *)&sin, &len) == -1) { + return -1; + } else { + return ntohs(sin.sin_port); + } +} diff --git a/ListenerApp/SwiftSocket/yudpsocket.c b/ListenerApp/SwiftSocket/yudpsocket.c new file mode 100644 index 0000000..e59a725 --- /dev/null +++ b/ListenerApp/SwiftSocket/yudpsocket.c @@ -0,0 +1,139 @@ +// +// Copyright (c) <2014>, skysent +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// 3. All advertising materials mentioning features or use of this software +// must display the following acknowledgement: +// This product includes software developed by skysent. +// 4. Neither the name of the skysent nor the +// names of its contributors may be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY skysent ''AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL skysent BE LIABLE FOR ANY +// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// + +#include +#include +#include +#include +#include +#include +#include +#include +#define yudpsocket_buff_len 8192 + +//return socket fd +int yudpsocket_server(const char *address, int port) { + + //create socket + int socketfd=socket(AF_INET, SOCK_DGRAM, 0); + int reuseon = 1; + int r = -1; + + //bind + struct sockaddr_in serv_addr; + memset( &serv_addr, '\0', sizeof(serv_addr)); + serv_addr.sin_len = sizeof(struct sockaddr_in); + serv_addr.sin_family = AF_INET; + if (address == NULL || strlen(address) == 0 || strcmp(address, "255.255.255.255") == 0) { + r = setsockopt(socketfd, SOL_SOCKET, SO_BROADCAST, &reuseon, sizeof(reuseon)); + serv_addr.sin_port = htons(port); + serv_addr.sin_addr.s_addr = htonl(INADDR_ANY); + } else { + r = setsockopt(socketfd, SOL_SOCKET, SO_REUSEADDR, &reuseon, sizeof(reuseon)); + serv_addr.sin_addr.s_addr = inet_addr(address); + serv_addr.sin_port = htons(port); + } + + if (r == -1) { + return -1; + } + + r = bind(socketfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)); + if (r == 0) { + return socketfd; + } else { + return -1; + } +} + +int yudpsocket_recive(int socket_fd, char *outdata, int expted_len, char *remoteip, int *remoteport) { + struct sockaddr_in cli_addr; + socklen_t clilen = sizeof(cli_addr); + memset(&cli_addr, 0x0, sizeof(struct sockaddr_in)); + int len = (int)recvfrom(socket_fd, outdata, expted_len, 0, (struct sockaddr *)&cli_addr, &clilen); + char *clientip = inet_ntoa(cli_addr.sin_addr); + memcpy(remoteip, clientip, strlen(clientip)); + *remoteport = cli_addr.sin_port; + + return len; +} + +int yudpsocket_close(int socket_fd) { + return close(socket_fd); +} + +//return socket fd +int yudpsocket_client() { + //create socket + int socketfd = socket(AF_INET, SOCK_DGRAM, 0); + int reuseon = 1; + setsockopt(socketfd, SOL_SOCKET, SO_REUSEADDR, &reuseon, sizeof(reuseon)); + + //disable SIGPIPE as we'll handle send errors ourselves + int noSigPipe = 1; + setsockopt(socketfd, SOL_SOCKET, SO_NOSIGPIPE, &noSigPipe, sizeof(noSigPipe)); + + return socketfd; +} + +//enable broadcast +void enable_broadcast(int socket_fd) { + int reuseon = 1; + setsockopt(socket_fd, SOL_SOCKET, SO_BROADCAST, &reuseon, sizeof(reuseon)); +} + +int yudpsocket_get_server_ip(char *host, char *ip) { + struct hostent *hp; + struct sockaddr_in address; + + hp = gethostbyname(host); + if (hp == NULL) { + return -1; + } + + bcopy((char *)hp->h_addr, (char *)&address.sin_addr, hp->h_length); + char *clientip = inet_ntoa(address.sin_addr); + memcpy(ip, clientip, strlen(clientip)); + + return 0; +} + +//send message to address and port +int yudpsocket_sentto(int socket_fd, char *msg, int len, char *toaddr, int topotr) { + struct sockaddr_in address; + socklen_t addrlen = sizeof(address); + memset(&address, 0x0, sizeof(struct sockaddr_in)); + address.sin_family = AF_INET; + address.sin_port = htons(topotr); + address.sin_addr.s_addr = inet_addr(toaddr); + int sendlen = (int)sendto(socket_fd, msg, len, 0, (struct sockaddr *)&address, addrlen); + + return sendlen; +}