Add SwiftSocket to the project. I fought with cocoapods for a while but opted to just directly include the code because I was tired of fighting.

This commit is contained in:
Jeremy Rand 2021-07-17 15:48:24 -04:00
parent b7c020be62
commit be95708466
10 changed files with 885 additions and 1 deletions

View File

@ -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 = "<group>"; };
9D51562A26A1F1B40075EBC7 /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = "<group>"; };
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 = "<group>"; };
9D51564B26A36B410075EBC7 /* SwiftSocket.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SwiftSocket.h; sourceTree = "<group>"; };
9D51564C26A36B410075EBC7 /* Result.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Result.swift; sourceTree = "<group>"; };
9D51564D26A36B410075EBC7 /* yudpsocket.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = yudpsocket.c; sourceTree = "<group>"; };
9D51564E26A36B410075EBC7 /* ytcpsocket.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ytcpsocket.c; sourceTree = "<group>"; };
9D51564F26A36B410075EBC7 /* UDPClient.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UDPClient.swift; sourceTree = "<group>"; };
9D51565026A36B410075EBC7 /* Socket.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Socket.swift; sourceTree = "<group>"; };
9D51565126A36B410075EBC7 /* TCPClient.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TCPClient.swift; sourceTree = "<group>"; };
/* 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 = "<group>";
};
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 = "<group>";
};
/* 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";
};

View File

@ -17,7 +17,7 @@
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleVersion</key>
<string>39</string>
<string>42</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>NSSpeechRecognitionUsageDescription</key>

View File

@ -0,0 +1,4 @@
//
// Use this file to import your target's public headers that you would like to expose to Swift.
//

View File

@ -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
}
}
}

View File

@ -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
}

View File

@ -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 <SwiftSocket_iOS/PublicHeader.h>

View File

@ -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<Byte>,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<Byte>,len:Int32) -> Int32
@_silgen_name("ytcpsocket_pull") private func c_ytcpsocket_pull(_ fd:Int32,buff:UnsafePointer<Byte>,len:Int32,timeout:Int32) -> Int32
@_silgen_name("ytcpsocket_listen") private func c_ytcpsocket_listen(_ address:UnsafePointer<Int8>,port:Int32)->Int32
@_silgen_name("ytcpsocket_accept") private func c_ytcpsocket_accept(_ onsocketfd:Int32,ip:UnsafePointer<Int8>,port:UnsafePointer<Int32>,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
}
}

View File

@ -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<Int8>,port:Int32) -> Int32
@_silgen_name("yudpsocket_recive") func c_yudpsocket_recive(_ fd:Int32,buff:UnsafePointer<Byte>,len:Int32,ip:UnsafePointer<Int8>,port:UnsafePointer<Int32>) -> 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<Int8>,ip:UnsafePointer<Int8>) -> Int32
@_silgen_name("yudpsocket_sentto") func c_yudpsocket_sentto(_ fd:Int32,buff:UnsafePointer<Byte>,len:Int32,ip:UnsafePointer<Int8>,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..<Int(readLen)])
return (data, address, port)
}
open func close() {
guard let fd = self.fd else { return }
_ = c_yudpsocket_close(fd)
self.fd = nil
}
//TODO add multycast and boardcast
}
open class UDPServer: Socket {
public override init(address: String, port: Int32) {
super.init(address: address, port: port)
let fd = c_yudpsocket_server(address, port: port)
if fd > 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
}
}

View File

@ -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 <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <dirent.h>
#include <netdb.h>
#include <unistd.h>
#include <fcntl.h>
#include <signal.h>
#include <sys/select.h>
#include <sys/ioctl.h>
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);
}
}

View File

@ -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 <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <string.h>
#include <unistd.h>
#include <netdb.h>
#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;
}