I am writing a very, very simple TCP socket app that sends and receives data from a server; my app uses delegates; most of the code I found from online but none of the event handlers for the streams are working; any ideas on why the following method is never being called?
UPDATE: this question has been answered here:
Non-responsive stream delegate in Swift
However, the problem that I am facing is how do I flush the output stream after the buffer is full? I would like send data in real time to my server, but I am getting a "broken pipe" from stream output error
/**
NSStream Delegate Method where we handle errors, read and write data from input and output streams
:param: stream NStream that called delegate method
:param: eventCode Event Code
*/
final func stream(stream: Stream, handleEvent eventCode: Stream.Event) {
switch eventCode {
case Stream.Event.endEncountered:
endEncountered(stream: stream)
case Stream.Event.errorOccurred:
print("[SCKT]: ErrorOccurred: \(stream.streamError?.localizedDescription)")
case Stream.Event.openCompleted:
print("open completed")
openCompleted(stream: stream)
case Stream.Event.hasBytesAvailable:
handleIncommingStream(stream: stream)
case Stream.Event.hasSpaceAvailable:
print("space available")
writeToStream()
default:
print("default!")
}
}
UIViewController
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 ip_address = "000.000.00.0" as CFString
private var port: Int = 0000
private var control_socket: Socket
// private var control_socket: Socket = Socket()
private var toast_label: UILabel = UILabel()
// let dataProcessingQueue = DispatchQueue.main
// convenience init() {
// self.control_socket = Socket()
// }
// init(coder aDecoder: NSCoder!) {
// super.init(coder: aDecoder)
// }
// init(imageURL: NSURL?) {
// self.control_socket = Socket()
// self.control_socket.open(host: self.ip_address as String!, port: self.port)
// super.init(nibName: nil, bundle: nil)
// }
// required init?(coder aDecoder: NSCoder) {
// fatalError("init(coder:) has not been implemented")
// }
required init(coder aDecoder: NSCoder) {
print("here!")
self.control_socket = Socket()
// self.control_socket.open(host: self.ip_address as String!, port: self.port)
print("here(2)!")
super.init(coder: aDecoder)!
}
override func viewDidLoad() {
super.viewDidLoad()
self.control_socket.open(host: self.ip_address as String!, port: self.port)
createJoystick()
createContours()
createViewsButton()
recvLidarData()
}
private func requestIsComplete() -> Bool {
// This function should find out if all expected data was received and return 'true' if it did.
return true
}
private func processData(data: Data) {
// This function should do something with the received data
}
private func recvLidarData() {
let concurrentQueue = DispatchQueue(label: "queuename", attributes: .concurrent)
concurrentQueue.async {
// recieve the data here!
}
}
private func createToast() {
self.toast_label = UILabel(frame: CGRect(x: self.view.frame.size.width/2 - 150, y: self.view.frame.size.height-100, width: 300, height: 35))
self.toast_label.backgroundColor = UIColor.black
self.toast_label.textColor = UIColor.white
self.toast_label.textAlignment = NSTextAlignment.center;
self.view.addSubview(self.toast_label)
self.toast_label.text = "Could not connect to server"
self.toast_label.alpha = 1.0
self.toast_label.layer.cornerRadius = 10;
self.toast_label.clipsToBounds = true
UIView.animate(withDuration: 4.0, delay: 0.1, options: UIViewAnimationOptions.curveEaseOut, animations: {
self.toast_label.alpha = 0.0
})
}
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
self.joystick.ip_address = self.ip_address
self.joystick.port = self.port
var packet = ""
DispatchQueue.global(qos: .userInitiated).async { // do some task
// self.control_socket = Socket()
// self.control_socket.open(host: self.ip_address as String!, port: self.port)
self.joystick.trackingHandler = { (data) -> () in
var 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)
power = power/1.2
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 { // turn left
packet = "\(-1) \(1) \(power) \(power)"
}
}
print("packet: \(packet)")
self.control_socket.send(message: packet)
print("sent")
}
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 createViewsButton() {
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) {
}
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) {
}
}
Socket class
import UIKit
import Foundation
@objc protocol SocketStreamDelegate{
func socketDidConnect(stream:Stream)
@objc optional func socketDidDisconnet(stream:Stream, message:String)
@objc optional func socketDidReceiveMessage(stream:Stream, message:String)
@objc optional func socketDidEndConnection()
}
class Socket: NSObject, StreamDelegate {
var delegate:SocketStreamDelegate?
private let bufferSize = 1024
private var _host:String?
private var _port:Int?
private var _messagesQueue:Array<String> = [String]()
private var _streamHasSpace:Bool = false
private var inputStream: InputStream?
private var outputStream: OutputStream?
var isClosed = false
var isOpen = false
var host:String?{
get{
return self._host
}
}
var port:Int?{
get{
return self._port
}
}
deinit{
if let inputStr = self.inputStream{
inputStr.close()
inputStr.remove(from: .main, forMode: RunLoopMode.defaultRunLoopMode)
}
if let outputStr = self.outputStream{
outputStr.close()
outputStr.remove(from: .main, forMode: RunLoopMode.defaultRunLoopMode)
}
}
/**
Opens streaming for both reading and writing, error will be thrown if you try to send a message and streaming hasn't been opened
:param: host String with host portion
:param: port Port
*/
final func open(host:String!, port:Int!){
self._host = host
self._port = port
var inStreamUnmanaged:Unmanaged<CFReadStream>?
var outStreamUnmanaged:Unmanaged<CFWriteStream>?
CFStreamCreatePairWithSocketToHost(nil, host as CFString!, UInt32(port), &inStreamUnmanaged, &outStreamUnmanaged)
inputStream = inStreamUnmanaged?.takeRetainedValue()
outputStream = outStreamUnmanaged?.takeRetainedValue()
if inputStream != nil && outputStream != nil {
inputStream!.delegate = self
outputStream!.delegate = self
// var myloop = RunLoop.current
// inputStream!.schedule(in: myloop, forMode: RunLoopMode.defaultRunLoopMode)
// outputStream!.schedule(in: myloop, forMode: RunLoopMode.defaultRunLoopMode)
inputStream!.schedule(in: .main, forMode: RunLoopMode.defaultRunLoopMode)
outputStream!.schedule(in: .main, forMode: RunLoopMode.defaultRunLoopMode)
print("[SCKT]: Open Stream")
self._messagesQueue = Array()
inputStream!.open()
outputStream!.open()
// myloop.run()
print("Run")
} else {
print("[SCKT]: Failed Getting Streams")
}
}
final func close(){
if let inputStr = self.inputStream {
inputStr.delegate = nil
inputStr.close()
inputStr.remove(from: .main, forMode: RunLoopMode.defaultRunLoopMode)
}
if let outputStr = self.outputStream {
outputStr.delegate = nil
outputStr.close()
outputStr.remove(from: .main, forMode: RunLoopMode.defaultRunLoopMode)
}
isClosed = true
}
/**
NSStream Delegate Method where we handle errors, read and write data from input and output streams
:param: stream NStream that called delegate method
:param: eventCode Event Code
*/
final func stream(stream: Stream, handleEvent eventCode: Stream.Event) {
switch eventCode {
case Stream.Event.endEncountered:
endEncountered(stream: stream)
case Stream.Event.errorOccurred:
print("[SCKT]: ErrorOccurred: \(stream.streamError?.localizedDescription)")
case Stream.Event.openCompleted:
print("open completed")
openCompleted(stream: stream)
case Stream.Event.hasBytesAvailable:
handleIncommingStream(stream: stream)
case Stream.Event.hasSpaceAvailable:
print("space available")
writeToStream()
default:
print("default!")
}
}
final func endEncountered(stream: Stream) {
}
final func openCompleted(stream: Stream){
if(self.inputStream!.streamStatus == .open && self.outputStream!.streamStatus == .open) {
let justAOneTimeThing: () = {
self.isOpen = true
self.delegate!.socketDidConnect(stream: stream)
}()
}
}
/**
Reads bytes asynchronously from incomming stream and calls delegate method socketDidReceiveMessage
:param: stream An NSInputStream
*/
final func handleIncommingStream(stream: Stream) {
if stream is InputStream {
var buffer = [UInt8](repeating: 0, count: bufferSize)
DispatchQueue.global(qos: .userInitiated).async {
let len = self.inputStream?.read(&buffer, maxLength: buffer.count)
if len! >= 0 {
if let output = NSString(bytes: &buffer, length: len!, encoding: String.Encoding.utf8.rawValue) {
self.delegate?.socketDidReceiveMessage!(stream: stream, message: output as String)
}
} else {
// Handle error
}
}
} else {
print("[SCKT]: \(#function) : Incorrect stream received")
}
}
/**
If messages exist in _messagesQueue it will remove and it and send it, if there is an error
it will return the message to the queue
*/
final func writeToStream() {
if _messagesQueue.count > 0 && self.outputStream!.hasSpaceAvailable {
DispatchQueue.global(qos: .userInitiated).async {
let message = self._messagesQueue.removeLast()
let buff = [UInt8](message.utf8)
if self.outputStream!.write(buff, maxLength: buff.count) == -1 {
self._messagesQueue.append(message)
}
}
}
}
final func send(message:String){
_messagesQueue.insert(message, at: 0)
writeToStream()
}
}
Maybe your
stream
function prototype is incorrect. Your prototype isTry this:
in your Socket class