How to read data from NSInputStream explicitly in swift? - sockets

I am using a socket connect in my application.
Here's my SocketConnection.swift
init(host: String, port:UInt32){
self.host = host
self.port = port
self.status = false
output = ""
super.init()
}
func stream(aStream: NSStream, handleEvent aStreamEvent: NSStreamEvent) {
switch aStreamEvent {
case NSStreamEvent.OpenCompleted:
break
case NSStreamEvent.HasBytesAvailable:
break
case NSStreamEvent.HasSpaceAvailable:
break
case NSStreamEvent.EndEncountered:
// aStream.close()
aStream.removeFromRunLoop(NSRunLoop.currentRunLoop(), forMode: NSDefaultRunLoopMode)
break
case NSStreamEvent.None:
break
case NSStreamEvent.ErrorOccurred:
break
default:
println("# something weird happend")
break
}
}
func connect() {
println("# connecting to \(host):\(port)")
var cfReadStream : Unmanaged<CFReadStream>?
var cfWriteStream : Unmanaged<CFWriteStream>?
CFStreamCreatePairWithSocketToHost(kCFAllocatorDefault, host, port, &cfReadStream, &cfWriteStream)
inputStream = cfReadStream!.takeRetainedValue()
outputStream = cfWriteStream!.takeRetainedValue()
inputStream!.delegate = self
outputStream!.delegate = self
inputStream!.scheduleInRunLoop(NSRunLoop.currentRunLoop(), forMode: NSDefaultRunLoopMode)
outputStream!.scheduleInRunLoop(NSRunLoop.currentRunLoop(), forMode: NSDefaultRunLoopMode)
inputStream!.open()
outputStream!.open()
}
func read(){
var buffer = [UInt8](count: bufferSize, repeatedValue: 0)
output = ""
while (self.inputStream!.hasBytesAvailable){
var bytesRead: Int = inputStream!.read(&buffer, maxLength: buffer.count)
if bytesRead >= 0 {
output += NSString(bytes: UnsafePointer(buffer), length: bytesRead, encoding: encoding)! as String
} else {
println("# error")
}
println("> \(output)")
}
}
func send(message:String){
let data:NSData = message.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)!
let bytesWritten = self.outputStream!.write(UnsafePointer(data.bytes), maxLength: data.length)
println("< send to \(host)")
}
In my ViewController.swift,
I am connecting to the server like this
var socketConnection = SocketConnection(host: _ip, port: _port)
socketConnection.connect()
socketConnection.send(urlString)
socketConnection.read()
Now I can send my url string via socket but when I am reading explicitly I am not getting the data from the server if I call the same read function from the NSStreamEvent.HasBytesAvailable case it printing the server response.. but how can I trigger the event queue?
I want to call this socketConnection.read() explicitly.. How can I do that?
After 2 sec of connection, its closes the connection channel, I want to keep alive my connection until I close.
Help me out from this problem.
Thanks

Change your read() as below and then call read() func in case NSStreamEvent.HasBytesAvailable:
private func read(stream: InputStream) {
let maxReadLength = 1024
let buffer = UnsafeMutablePointer<UInt8>.allocate(capacity: maxReadLength)
while stream.hasBytesAvailable {
let numberOfBytesRead = inputStream?.read(buffer, maxLength: maxReadLength)
if numberOfBytesRead! < 0 {
if let _ = inputStream?.streamError {
break
}
}
if let message = processedMessageString(buffer: buffer, length: numberOfBytesRead!) {
delegate?.receivedMessage(message: message)
}
}
}

You do not need to call read functionality explicitly. Change your read func as below and call in case case Stream.Event.hasBytesAvailable:.
func read(stream: InputStream) {
let maxReadLength = 1024
var buffer = [uint8](repeating: 0, count: maxReadLength)
while stream.hasBytesAvailable {
let numberOfBytesRead : Int = stream.read(&buffer, maxLength: maxReadLength)
if numberOfBytesRead < 0 {
if let _ = stream.streamError {
break
}
}
if let message = processedMessageString(buffer: buffer, length: numberOfBytesRead) {
delegate?.receivedMessage(message: message)
}
}}
If you want to call read() explicitly then you need to pass the stream as parameters and call the read function in ViewController class—
socketConnection.read(stream)

Related

Swift: receive UDP packet using Network framework

I'm studying swift 5.6 Network framework. For this I have a Java-based server, that waits a udp packet of size 64 at localhost port 10000 and sends it back to localhost port 20000. Here is my implementation for Swift :
import Foundation
import Network
class UdpConnection {
private var connection: NWConnection?
private var isConnectionReady = false
init?(host: String, port: UInt16) {
self.connection = NWConnection(
host: NWEndpoint.Host(host),
port: NWEndpoint.Port(integerLiteral: port),
using: .udp
)
let connectionEstablishWaiter = DispatchSemaphore(value: 0)
self.connection?.stateUpdateHandler = { [weak self] (newState) in
switch (newState) {
case .ready:
self?.isConnectionReady = true
default :
self?.isConnectionReady = false
}
connectionEstablishWaiter.signal()
}
self.connection?.start(queue: .global())
switch connectionEstablishWaiter.wait(timeout: .now() + 1) {
case .timedOut:
return nil
default:
()
}
}
func sendUDP(content: Data) {
let sema = DispatchSemaphore(value: 0)
self.connection?.send(content: content, completion: NWConnection.SendCompletion.contentProcessed(({ (NWError) in
if (NWError == nil) {
print("Data was sent to UDP")
} else {
print("ERROR! Error when data (Type: Data) sending. NWError: \n \(NWError!)")
}
sema.signal()
})))
sema.wait()
}
func receiveUDP() {
self.connection?.receiveMessage { (data, context, isComplete, error) in
if (isComplete) {
if let data = data {
print("Receive is complete : \(data.count)")
} else {
print("Data == nil")
}
}
}
}
}
And here is my test app :
import Foundation
if let udpRequestConnection = UdpConnection(host: "127.0.0.1", port: 10_000) {
print("connection established OK")
if let udpResponseConnection = UdpConnection(host: "127.0.0.1", port: 20_000) {
let data = Data(count: 64)
udpResponseConnection.receiveUDP()
udpRequestConnection.sendUDP(content: data)
print("sent")
}
} else {
print("connection establishing FAILURE")
}
I see no packet received and moreover I see a strange picture in Wireshark :
What am I doing wrong? Why is there an ICMP packet ? What am I missing to get this UDP ?
Ok, it seems that I got the misunderstanding -> NWConnection ctor is responsible for setting outbound connection params. So to listen to UDP stream I need the following implementation :
import Foundation
import Network
import Combine
class UDPListener: ObservableObject {
var listener: NWListener?
var connection: NWConnection?
var queue = DispatchQueue.global(qos: .userInitiated)
/// New data will be place in this variable to be received by observers
#Published private(set) public var messageReceived: Data?
/// When there is an active listening NWConnection this will be `true`
#Published private(set) public var isReady: Bool = false
/// Default value `true`, this will become false if the UDPListener ceases listening for any reason
#Published public var listening: Bool = true
/// A convenience init using Int instead of NWEndpoint.Port
convenience init(on port: Int) {
self.init(on: NWEndpoint.Port(integerLiteral: NWEndpoint.Port.IntegerLiteralType(port)))
}
/// Use this init or the one that takes an Int to start the listener
init(on port: NWEndpoint.Port) {
let params = NWParameters.udp
params.allowFastOpen = true
self.listener = try? NWListener(using: params, on: port)
self.listener?.stateUpdateHandler = { update in
switch update {
case .ready:
self.isReady = true
case .failed, .cancelled:
// Announce we are no longer able to listen
self.listening = false
self.isReady = false
default:
()
}
}
self.listener?.newConnectionHandler = { connection in
self.createConnection(connection: connection)
}
self.listener?.start(queue: self.queue)
}
private func createConnection(connection: NWConnection) {
self.connection = connection
self.connection?.stateUpdateHandler = { (newState) in
switch (newState) {
case .ready:
self.receive()
case .cancelled, .failed:
// Cancel the listener, something went wrong
self.listener?.cancel()
// Announce we are no longer able to listen
self.listening = false
default:
()
}
}
self.connection?.start(queue: .global())
}
func receive() {
self.connection?.receiveMessage { data, context, isComplete, error in
if error != nil {
return
}
guard isComplete, let data = data else {
return
}
self.messageReceived = data
print("Rx data size = \(data.count)")
if self.listening {
self.receive()
}
}
}
func cancel() {
self.listening = false
self.connection?.cancel()
}
}
With this I am able to get the UDP response I expect.

Stream Microphone Audio from one device to another using Multipeer connectivy and EZAudio

[TLDR: Receiving an ASSERTION FAILURE on CABufferList.h (find error at the bottom) when trying to save streamed audio data]
I am having trouble saving microphone audio that is streamed between devices using Multipeer Connectivity. So far I have two devices connected to each other using Multipeer Connectivity and have them sending messages and streams to each other.
Finally I have the StreamDelegate method
func stream(_ aStream: Stream, handle eventCode: Stream.Event) {
// create a buffer for capturing the inputstream data
let bufferSize = 2048
let buffer = UnsafeMutablePointer<UInt8>.allocate(capacity: bufferSize)
defer {
buffer.deallocate()
}
var audioBuffer: AudioBuffer!
var audioBufferList: AudioBufferList!
switch eventCode {
case .hasBytesAvailable:
// if the input stream has bytes available
// return the actual number of bytes placed in the buffer;
let read = self.inputStream.read(buffer, maxLength: bufferSize)
if read < 0 {
//Stream error occured
print(self.inputStream.streamError!)
} else if read == 0 {
//EOF
break
}
guard let mData = UnsafeMutableRawPointer(buffer) else { return }
audioBuffer = AudioBuffer(mNumberChannels: 1, mDataByteSize: UInt32(read), mData: mData)
audioBufferList = AudioBufferList(mNumberBuffers: 1, mBuffers: audioBuffer)
let audioBufferListPointer = UnsafeMutablePointer<AudioBufferList>.allocate(capacity: read)
audioBufferListPointer.pointee = audioBufferList
DispatchQueue.main.async {
if self.ezRecorder == nil {
self.recordAudio()
}
self.ezRecorder?.appendData(from: audioBufferListPointer, withBufferSize: UInt32(read))
}
print("hasBytesAvailable \(audioBuffer!)")
case .endEncountered:
print("endEncountered")
if self.inputStream != nil {
self.inputStream.delegate = nil
self.inputStream.remove(from: .current, forMode: .default)
self.inputStream.close()
self.inputStream = nil
}
case .errorOccurred:
print("errorOccurred")
case .hasSpaceAvailable:
print("hasSpaceAvailable")
case .openCompleted:
print("openCompleted")
default:
break
}
}
I am getting the stream of data however when I try to save it as an audio file using EZRecorder, I get the following error message
[default] CABufferList.h:184 ASSERTION FAILURE [(nBytes <= buf->mDataByteSize) != 0 is false]:
I suspect the error could be arising when I create AudioStreamBasicDescription for EZRecorder.
I understand there may be other errors here and I appreciate any suggestions to solve the bug and improve the code. Thanks
EZAudio comes with TPCircularBuffer - use that.
Because writing the buffer to file is an async operation, this becomes a great use case for a circular buffer where we have one producer and one consumer.
Use the EZAudioUtilities where possible.
Update: EZRecorder write expects bufferSize to be number of frames to write and not bytes
So something like this should work:
class StreamDelegateInstance: NSObject {
private static let MaxReadSize = 2048
private static let BufferSize = MaxReadSize * 4
private var availableReadBytesPtr = UnsafeMutablePointer<Int32>.allocate(capacity: 1)
private var availableWriteBytesPtr = UnsafeMutablePointer<Int32>.allocate(capacity: 1)
private var ezRecorder: EZRecorder?
private var buffer = UnsafeMutablePointer<TPCircularBuffer>.allocate(capacity: 1)
private var inputStream: InputStream?
init(inputStream: InputStream? = nil) {
self.inputStream = inputStream
super.init()
EZAudioUtilities.circularBuffer(buffer, withSize: Int32(StreamDelegateInstance.BufferSize))
ensureWriteStream()
}
deinit {
EZAudioUtilities.freeCircularBuffer(buffer)
buffer.deallocate()
availableReadBytesPtr.deallocate()
availableWriteBytesPtr.deallocate()
self.ezRecorder?.closeAudioFile()
self.ezRecorder = nil
}
private func ensureWriteStream() {
guard self.ezRecorder == nil else { return }
// stores audio to temporary folder
let audioOutputPath = NSTemporaryDirectory() + "audioOutput2.aiff"
let audioOutputURL = URL(fileURLWithPath: audioOutputPath)
print(audioOutputURL)
// let audioStreamBasicDescription = AudioStreamBasicDescription(mSampleRate: 44100.0, mFormatID: kAudioFormatLinearPCM, mFormatFlags: kAudioFormatFlagIsSignedInteger | kAudioFormatFlagsNativeEndian | kAudioFormatFlagIsPacked, mBytesPerPacket: 4, mFramesPerPacket: 1, mBytesPerFrame: 4, mChannelsPerFrame: 1, mBitsPerChannel: 32, mReserved: 1081729024)
// EZAudioUtilities.audioBufferList(withNumberOfFrames: <#T##UInt32#>,
// numberOfChannels: 1,
// interleaved: true)
// if you don't need a custom format, consider using EZAudioUtilities.m4AFormat
let format = EZAudioUtilities.aiffFormat(withNumberOfChannels: 1,
sampleRate: 44800)
self.ezRecorder = EZRecorder.init(url: audioOutputURL,
clientFormat: format,
fileType: .AIFF)
}
private func writeStream() {
let ptr = TPCircularBufferTail(buffer, availableWriteBytesPtr)
// ensure we have non 0 bytes to write - which should always be true, but you may want to refactor things
guard availableWriteBytesPtr.pointee > 0 else { return }
let framesToWrite = availableWriteBytesPtr.pointee / 4 // sizeof(float)
let audioBuffer = AudioBuffer(mNumberChannels: 1,
mDataByteSize: UInt32(availableWriteBytesPtr.pointee),
mData: ptr)
let audioBufferList = AudioBufferList(mNumberBuffers: 1, mBuffers: audioBuffer)
self.ezRecorder?.appendData(from: &audioBufferList,
withBufferSize: UInt32(framesToWrite))
TPCircularBufferConsume(buffer, framesToWrite * 4)
}
}
extension StreamDelegateInstance: StreamDelegate {
func stream(_ aStream: Stream, handle eventCode: Stream.Event) {
switch eventCode {
case .hasBytesAvailable:
// if the input stream has bytes available
// return the actual number of bytes placed in the buffer;
guard let ptr = TPCircularBufferHead(buffer, availableReadBytesPtr) else {
print("couldn't get buffer ptr")
break;
}
let bytedsToRead = min(Int(availableReadBytesPtr.pointee), StreamDelegateInstance.MaxReadSize)
let mutablePtr = ptr.bindMemory(to: UInt8.self, capacity: Int(bytedsToRead))
let bytesRead = self.inputStream?.read(mutablePtr,
maxLength: bytedsToRead) ?? 0
if bytesRead < 0 {
//Stream error occured
print(self.inputStream?.streamError! ?? "No bytes read")
break
} else if bytesRead == 0 {
//EOF
break
}
TPCircularBufferProduce(buffer, Int32(bytesRead))
DispatchQueue.main.async { [weak self] in
self?.writeStream()
}
case .endEncountered:
print("endEncountered")
if self.inputStream != nil {
self.inputStream?.delegate = nil
self.inputStream?.remove(from: .current, forMode: .default)
self.inputStream?.close()
self.inputStream = nil
}
case .errorOccurred:
print("errorOccurred")
case .hasSpaceAvailable:
print("hasSpaceAvailable")
case .openCompleted:
print("openCompleted")
default:
break
}
}
}

ORSSerialPort.send doesn't send anything

I am new (very new!!) to swift and straggling to make my UI to send a string over the serial port. I've managed to open the port and read/parse the incoming traffic but when it comes to send a string, nothing is sent.
What I need to do is typing in the sendTextField and when press the SendButton to send the string to serial port. Also, when I print the data which is what I want to send over serial port, it prints the number of bytes I try to send (i.e. 5 bytes). Shouldn't this be the string "Hello" that I try to send to serial port?
I am using Xcode Version 11.2 (11B52) and Swift 5.
Any help will be really appreciated. Thank you in advance!
This is how I call the "send" function:
#IBAction func SendButton(_ sender: Any) {
let TxData = sendTextField.stringValue
SFSerialIn.SendSerialData(TxData)
}
My main program is below:
import ORSSerial
import IOKit
import IOKit.serial
let SFSerialRegexp =
"(?<SFmode>[A-Z]+),\\s*" + "(?<prox>[0-1]),\\s*"
class SFSerialIn: NSObject, ORSSerialPortDelegate {
let path = "/dev/cu.usbserial-AI0484S9"
let baudRate: NSNumber = 115200
var serialPort: ORSSerialPort?
var delegate: SFSerialDelegate?
var stringBuffer = ""
var regex: NSRegularExpression!
var receivedBufferStart = false
override init() {
regex = try! NSRegularExpression(pattern: SFSerialRegexp)
}
deinit {
disconnect()
}
func SendSerialData(_ TxData: String){
let data = Data(TxData.utf8)
serialPort?.send(data)
print(TxData)
print(data)
}
func connect() {
if let serialPort = ORSSerialPort(path: path) {
serialPort.baudRate = baudRate
serialPort.delegate = self
serialPort.open()
} else {
print("Failed to open serial port")
}
}
func disconnect() {
serialPort?.close()
print("closing port...")
}
func serialPort(_ serialPort: ORSSerialPort, didReceive data: Data) {
guard let string = String(data: data, encoding: .utf8)
else {
return
}
stringBuffer += string
parseBuffer()
}
func parseBuffer() {
let lines = stringBuffer.split { $0.isNewline }
guard lines.count > 1 else {
return
}
let nextLines = lines[1...].joined()
if !receivedBufferStart {
stringBuffer = nextLines
receivedBufferStart = true
return
}
let line = String(lines[0])
if let matchResult = regex.firstMatch(in: line, range: NSRange(..<line.endIndex, in: line)) {
let sensorFrame = SFFrame(matchResult: matchResult, string: line)
delegate?.receive(sensorFrame: sensorFrame)
stringBuffer = nextLines
return
}
print("Failed to parse line :(")
stringBuffer = nextLines
}
func serialPort(_ serialPort: ORSSerialPort, didEncounterError error: Error) {
print("Serial port encountered error", error)
}
func serialPortWasOpened(_ serialPort: ORSSerialPort) {
print("Serial port opened")
}
func serialPortWasClosed(_ serialPort: ORSSerialPort) {
print("Serial port closed")
}
func serialPortWasRemovedFromSystem(_ serialPort: ORSSerialPort) {
print("Serial port was removed from system")
}
}
protocol SFSerialDelegate {
func receive(sensorFrame: SFFrame)
}
extension StringProtocol {
var data: Data { .init(utf8) }
}
It doesn't look to me like you're ever storing the opened serial port in your serialPort instance property. So, when you do serialPort?.send(data), serialPort is nil, and the ? (optional chaining) operator means that send() isn't called.
Try storing the serial port in your property after opening it:
func connect() {
if let serialPort = ORSSerialPort(path: path) {
serialPort.baudRate = baudRate
serialPort.delegate = self
serialPort.open()
self.serialPort = serialPort
} else {
print("Failed to open serial port")
}
}

Swift 3 OutputStream hanging?

I wrote a simple Swift program to write to a server; I want to send data back to back or in a loop to a server; I tried doing something simple where I can simply send a message back to back (two pieces of data) but there is a hang; that is, the server only receives the first message but not the second message; why is this?
I am using StreamDelegate in Swift 3 and I am using OutputStream write method
print("Hello, World!")
import Foundation
class Connection: NSObject, StreamDelegate {
private var inputStream: InputStream!
private var outputStream: OutputStream!
private var my_address: CFString
private var my_port: UInt32
init (address: CFString, port:UInt32) {
self.my_address = address
self.my_port = port
super.init()
}
func connect() {
print("connecting...")
var readStream: Unmanaged<CFReadStream>?
var writeStream: Unmanaged<CFWriteStream>?
CFStreamCreatePairWithSocketToHost(nil, self.my_address, self.my_port, &readStream, &writeStream)
// Documentation suggests readStream and writeStream can be assumed to
// be non-nil. It might be wise to test if either is nil
// and implement error-handling as needed.
// self.inputStream = readStream!.takeRetainedValue()
self.outputStream = writeStream!.takeRetainedValue()
// self.inputStream.delegate = self
self.outputStream.delegate = self
// self.inputStream.schedule(in: RunLoop.current, forMode: RunLoopMode.defaultRunLoopMode)
// self.outputStream.schedule(in: RunLoop.current, forMode: RunLoopMode.defaultRunLoopMode)
// self.inputStream.open()
self.outputStream.open()
}
func disconnect() {
self.inputStream.close()
self.outputStream.close()
}
func stream(aStream: Stream, handleEvent eventCode: Stream.Event) {
switch (eventCode){
case Stream.Event.errorOccurred:
NSLog("ErrorOccurred")
case Stream.Event.endEncountered:
NSLog("EndEncountered")
case Stream.Event.hasBytesAvailable:
NSLog("HasBytesAvaible")
var buffer = [UInt8](repeating: 0, count: 4096)
while (inputStream.hasBytesAvailable){
var len = inputStream.read(&buffer, maxLength: buffer.count)
if(len > 0){
var output = NSString(bytes: &buffer, length: buffer.count, encoding: String.Encoding.utf8.rawValue)
if (output != ""){
NSLog("server said: %#", output!)
}
} else {
print("empty string from stream")
}
}
case Stream.Event.openCompleted:
NSLog("OpenCompleted")
case Stream.Event.hasSpaceAvailable:
NSLog("HasSpaceAvailable")
default: print("default reached. unknown stream event")
}
}
func sendMessage(message: String) {
let buff = [UInt8](message.utf8)
self.outputStream.write(buff, maxLength: buff.count)
}
func getOutputStream() -> OutputStream {
return self.outputStream
}
}
var socket = Connection(address: "000.000.0.000" as CFString, port: 0000)
socket.connect()
socket.sendMessage(message: "Hey you! 1")
socket.sendMessage(message: "Hey you! 2")
\
import Darwin
import Foundation
import UIKit
import Dispatch
class ViewController: UIViewController {
#IBOutlet private weak var joystickMove: Joystick!
#IBOutlet private weak var joystickRotate: Joystick!
private var joystick = Joystick()
private var contour = Contours()
private var contour_index: Int = 0
private var socket = Connection(address: "000.000.0.000" as CFString, port: 0000)
override func viewDidLoad() {
super.viewDidLoad()
createJoystick()
createContours()
createButton()
}
private func createJoystick() {
let n: CGFloat = 100.0
let x: CGFloat = (UIScreen.main.bounds.width/2) - (n/2.0)
let y: CGFloat = UIScreen.main.bounds.height - (UIScreen.main.bounds.height/4.0)
self.joystick.frame = CGRect(x: x, y: y, width: n, height: n)
self.joystick.backgroundColor = UIColor.clear
self.joystick.substrateColor = UIColor.lightGray
self.joystick.substrateBorderColor = UIColor.gray
self.joystick.substrateBorderWidth = 1.0
self.joystick.stickSize = CGSize(width: 50.0, height: 50.0)
self.joystick.stickColor = UIColor.darkGray
self.joystick.stickBorderColor = UIColor.black
self.joystick.stickBorderWidth = 2.0
self.joystick.fade = 0.5
var packet = ""
DispatchQueue.global(qos: .userInitiated).async { // do some task
self.joystick.trackingHandler = { (data) -> () in
let power = sqrt(pow(Double(data.velocity.x), 2.0) + pow(Double(data.velocity.y), 2.0))
let theta = atan2(Double(-data.velocity.y), Double(data.velocity.x))
let degrees = theta * (180.0 / M_PI)
if degrees >= 55 && degrees <= 125 { // move forward
packet = "\(1) \(1) \(power) \(power)"
} else if degrees >= -125 && degrees <= -55 { // move backwards
packet = "\(-1) \(-1) \(power) \(power)"
} else if degrees >= -55 && degrees <= 55 { // turn right
packet = "\(1) \(-1) \(power) \(power)"
} else if (degrees >= 125 && degrees <= 180) && (degrees >= -180 && degrees <= -125) { // turn left
packet = "\(-1) \(1) \(power) \(power)"
}
self.socket.connect()
let sent = self.socket.sendMessage(message: packet)
if sent {
print("packet sent!\n \(packet)")
}
self.socket.stream(aStream: self.socket.inputStream, handleEvent: Stream.Event.hasBytesAvailable)
// self.socket.changeWhenHasBytesAvailable = { str in
// print("my-str: \(str.characters)")
// }
self.socket.disconnect()
}
}
view.addSubview(joystick)
}
private func createContours() {
let n: CGFloat = 350.0
let x: CGFloat = (UIScreen.main.bounds.width/2.0) - (n/2.0)
let y: CGFloat = UIScreen.main.bounds.height - (UIScreen.main.bounds.height/4.0) - n - 100.0
self.contour.frame = CGRect(x: x, y: y, width: n, height: n)
self.contour.backgroundColor = UIColor.clear
view.addSubview(self.contour)
}
private func createButton() {
let width: CGFloat = 150.0
let height: CGFloat = 75.0
let x: CGFloat = (UIScreen.main.bounds.width/2.0) - (width/2.0)
let y: CGFloat = UIScreen.main.bounds.height - (UIScreen.main.bounds.height/4.0) - width
let button: UIButton = UIButton(frame: CGRect(x: x, y: y, width: width, height: height))
button.backgroundColor = UIColor.blue
button.setTitle("Contour Views", for: .normal)
button.addTarget(self, action: #selector(self.buttonAction), for: .touchUpInside)
button.tag = 1
view.addSubview(button)
}
#objc private func buttonAction(sender: UIButton!) {
var btnsendtag: UIButton = sender
if btnsendtag.tag == 1 {
self.contour_index = (self.contour_index + 1) % 2
switch self.contour_index {
case 0:
for index in 0...356 {
if self.contour.distx[index] != -1 && self.contour.disty[index] != -1 {
self.contour.circles[index].alpha = 0
}
}
case 1:
for index in 0...356 {
if self.contour.distx[index] != -1 && self.contour.disty[index] != -1 {
self.contour.circles[index].alpha = 1
}
}
default:
for index in 0...356 {
if self.contour.distx[index] != -1 && self.contour.disty[index] != -1 {
self.contour.circles[index].alpha = 1
UIColor.cyan.setFill()
self.contour.lines[index].fill()
self.contour.lines[index].stroke()
}
}
}
}
}
override func viewDidAppear(_ animated: Bool) {
DispatchQueue.global(qos: .background).async { // do some task
// self.socket.connect()
// var packet = "1 1 0 0"
// let sent = self.socket.sendMessage(message: packet)
// if sent {
// print("packet sent!\n \(packet)")
// }
// self.socket.disconnect()
}
}
public func delayWithSeconds(_ seconds: Double, completion: #escaping () -> ()) {
DispatchQueue.main.asyncAfter(deadline: .now() + seconds) {
completion()
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
override func viewWillDisappear(_ animated: Bool) {
// self.socket.disconnect()
}
}
\
import Foundation
class Connection: NSObject, StreamDelegate {
public var inputStream: InputStream!
private var outputStream: OutputStream!
private var my_address: CFString
private var my_port: UInt32
public var changeWhenHasBytesAvailable: ((String)->())?
init (address: CFString, port:UInt32) {
self.my_address = address
self.my_port = port
super.init()
}
public func connect() {
var readStream: Unmanaged<CFReadStream>?
var writeStream: Unmanaged<CFWriteStream>?
CFStreamCreatePairWithSocketToHost(nil, self.my_address, self.my_port, &readStream, &writeStream)
self.inputStream = readStream!.takeRetainedValue()
self.outputStream = writeStream!.takeRetainedValue()
self.inputStream.delegate = self
self.outputStream.delegate = self
self.inputStream.schedule(in: RunLoop.current, forMode: RunLoopMode.defaultRunLoopMode)
self.outputStream.schedule(in: RunLoop.current, forMode: RunLoopMode.defaultRunLoopMode)
self.inputStream.open()
self.outputStream.open()
}
public func stream(aStream: Stream, handleEvent eventCode: Stream.Event) {
// DispatchQueue.global(qos: .userInitiated).async { // do some task
print("stream")
if aStream === self.inputStream {
switch eventCode {
case Stream.Event.openCompleted:
print("open")
break
case Stream.Event.errorOccurred:
print("error")
break
case Stream.Event.hasBytesAvailable:
print("available!")
// var inputBuffer = Array<UInt8>(repeating: 0, count:2048)
// self.inputStream?.read(&inputBuffer, maxLength: 2048)
// self.changeWhenHasBytesAvailable?(String(bytes: inputBuffer, encoding: String.Encoding.utf8)!)
var buffer = [UInt8](repeating: 0, count: 512)
while (self.inputStream.hasBytesAvailable){
print("before read")
var len = self.inputStream.read(&buffer, maxLength: buffer.count)
print("after read")
if(len > 0){
// var output = NSString(bytes: &buffer, length: buffer.count, encoding: String.Encoding.utf8.rawValue)
var output = NSString(bytes: &buffer, length: len, encoding: String.Encoding.utf8.rawValue)
if (output != ""){
print("output: \(output!)")
}
} else {
print("empty string from stream")
}
print("while")
}
break
default:
print("default")
break
}
}
// }
}
public func sendMessage(message: String) -> Bool {
var sent: Bool = false
let buff = [UInt8](message.utf8)
let result = self.outputStream.write(buff, maxLength: buff.count)
if result != -1 {
sent = true
}
return sent
}
public func disconnect() {
self.inputStream.close()
self.outputStream.close()
// self.inputStream.remove(from: RunLoop.current, forMode: RunLoopMode.defaultRunLoopMode)
// self.outputStream.remove(from: RunLoop.current, forMode: RunLoopMode.defaultRunLoopMode)
}
}
update; send works! but not receiving data?
Don't use a while inside the StreamDelegate when you are reading:
while (inputStream.hasBytesAvailable){
var len = inputStream.read(&buffer, maxLength: buffer.count)
...
}
Just call read. If there are more bytes, your delegate will be called again.

Swift2, Call swift function in CFSocketCallBack - EXEC_BAD_ACCESS

I'm trying to write a socket server app for Mac OSX with Xcode:7.2.1 in Swift2.1.1. referring to CocoaEcho sample code.
But I cannot call a swift function in the socketCallBack function.
My code is here. I'm passing the self based on the answer at Swift 2 - UnsafeMutablePointer to object. And I think the part of the code is working ok.
class myServer: NSObject {
// sockets
private var socketipv4: CFSocket!
private var socketipv6: CFSocket!
// Connections
var connections = Set<SPFConnection>()
func start(address: String) -> Bool {
var sockCtxt = CFSocketContext(version: 0, info: nil, retain: nil, release: nil, copyDescription: nil)
sockCtxt.info = UnsafeMutablePointer(unsafeAddressOf(self))
// create socket with CFSocketCreate
socketipv4 = CFSocketCreate(
kCFAllocatorDefault,
PF_INET,
SOCK_STREAM,
IPPROTO_TCP,
kCFSocketAutomaticallyReenableAcceptCallBack,
socketCallBack,
&sockCtxt)
// ipv4
var sin = sockaddr_in() // = initStruct()
let server_addr_size = socklen_t(INET_ADDRSTRLEN)
sin.sin_len = UInt8(server_addr_size)
sin.sin_family = sa_family_t(AF_INET)
sin.sin_port = UInt16(9999).bigEndian
sin.sin_addr.s_addr = inet_addr(address)
let sinData = NSData(bytes: &sin, length: sizeof(sockaddr_in))
let ptr = UnsafePointer<UInt8>(sinData.bytes)
let sincfd = CFDataCreate(kCFAllocatorDefault, ptr, sizeof(sockaddr_in))
let ipv4SocketError: CFSocketError = CFSocketSetAddress(socketipv4, sincfd)
switch ipv4SocketError {
case .Success:
print("ipv4 Success")
default:
print("ipv4 error = \(ipv4SocketError.rawValue)")
return false
}
let socketSource = CFSocketCreateRunLoopSource(kCFAllocatorDefault, socketipv4, 0)
CFRunLoopAddSource(CFRunLoopGetCurrent(), socketSource, kCFRunLoopDefaultMode)
return true
}
// CFSocket call back
var socketCallBack : #convention(c)(CFSocket!, CFSocketCallBackType, CFData!, UnsafePointer<Void>, UnsafeMutablePointer<Void>) -> Void = {
(socketRef, callbackType, address, data, info) in
print("acceptConnection callback-ed") // \(socketRef), \(callbackType), \(address), \(data),\(info)")
var tempData: CFSocketNativeHandle = 0
var anNSData:NSData = NSData(bytes: data, length: sizeofValue(data))
anNSData.getBytes(&tempData, length: sizeof(CFSocketNativeHandle))
var tempAry = [UnsafeMutablePointer<Void>]()
tempAry.append(info)
if callbackType == CFSocketCallBackType.AcceptCallBack {
let server = unsafeBitCast(info, myServer.self)
// **** EXEC_BAD_ACCESS, code=2 ***** //
server.acceptConnection(tempData)
} else {
print("callbacktype = \(callbackType.rawValue)")
}
}
func acceptConnection(data: CFSocketNativeHandle) {
print("acceptConnection called")
var readStream: Unmanaged<CFReadStream>?
var writeStream: Unmanaged<CFWriteStream>?
CFStreamCreatePairWithSocket(kCFAllocatorDefault, data, &readStream, &writeStream)
if readStream != nil && writeStream != nil {
CFReadStreamSetProperty(readStream!.takeUnretainedValue(), kCFStreamPropertyShouldCloseNativeSocket, kCFBooleanTrue)
CFWriteStreamSetProperty(writeStream!.takeUnretainedValue(), kCFStreamPropertyShouldCloseNativeSocket, kCFBooleanTrue)
let connection = SPFConnection()
connection.inputStream = readStream!.takeRetainedValue()
connection.outputStream = writeStream!.takeRetainedValue()
if connection.open() {
connections.insert(connection)
}
}
}
}
I'm getting EXEC_BAD_ACCESS code=2 at the code server.acceptConnection(tempData).
Debugger shows same pointer for both info and server, which should mean info(self) is properly assigned to server.
But EXEC_BAD_ACCESS seems to mean self is no longer available.
I'm struggling to find a solution. If anyone could give me any advise,
it'd be very much appreciated.
Thanks in advance for your help.
I'll admit I don't know much about using these lower level C conventions, but since the callback function is part of your class, can't just say self.acceptConnection(tempData)?