iOS swift socket communication: release outstream - ios

I followed this tutorial to make the most simple socket communication just to exchange string messages. However, when I write to the outstream, it seems to be never released until I kill the app - then the server receives everything in one chunk. I searched for solutions and found similar questions and problems, without solution. Here is my code:
class Peer: NSObject {
weak var delegate: PeerDelegate?
var inputStream: InputStream!
var outputStream: OutputStream!
var username = ""
let maxReadLength = 4096
func setupNetworkCommunication() {
var readStream: Unmanaged<CFReadStream>?
var writeStream: Unmanaged<CFWriteStream>?
CFStreamCreatePairWithSocketToHost(kCFAllocatorDefault,"192.168.43.1" as CFString,6000,&readStream,&writeStream)
inputStream = readStream!.takeRetainedValue()
outputStream = writeStream!.takeRetainedValue()
inputStream.schedule(in: .current, forMode: .common)
outputStream.schedule(in: .current, forMode: .common)
inputStream.open()
outputStream.open()
inputStream.delegate = self
}
func sendText(text: String) {
print("Sending", text)
let data = text.data(using: .utf8)!
data.withUnsafeBytes {
guard let pointer = $0.baseAddress?.assumingMemoryBound(to: UInt8.self) else {
print("Error sending text")
return
}
outputStream.write(pointer, maxLength: data.count)
print("DONE")
}
}
}
As you may notice, there is also a delegate that monitors the stream and responds to incoming data. It bothers me that I cannot simply send strings forth and back. It could be on a server side problem, that it waits for more, but if I run it with Android, it just works without extras. I tried to finalize, release and close the stream to produce something similar to an app kill, without success. There seems to be no flush command as in Java. Any ideas where the problem lies?

Okay, the solution seems to be ending the string with a line-breaker \n

Related

How to connect to websocket in swift?

I am trying to create a connection to web socket with a link ws://link.net:8888/ws?token= to open communication with the server.
I found out that I could use Stream but I don't know how to add access_token to the end.
Stream.getStreamsToHost(withName: host, port: port, inputStream: &inputStream, outputStream: &outputStream)
if inputStream != nil && outputStream != nil {
// Set delegate
inputStream!.delegate = self
outputStream!.delegate = self
// Schedule
inputStream!.schedule(in: .main, forMode: RunLoopMode.defaultRunLoopMode)
outputStream!.schedule(in: .main, forMode: RunLoopMode.defaultRunLoopMode)
print("Start open()")
// Open!
inputStream!.open()
outputStream!.open()
}
And for some reason even if I try to connect to link without access key, I am unsuccessful. So does anyone have a way how to open connection to the socket with access_key added I would be very grateful?
I also tried https://github.com/swiftsocket/SwiftSocket but it also doesn't have an option to add anything on end, the only thing I will try now it is https://github.com/socketio/socket.io-client-swift.
EDIT1:
So after a comment from Dan Karbayev I checked out Starscream with code
let url = "ws://link.net:5000/ws?token=token"
let socket = WebSocket(url: URL(string: url)!)
socket.onConnect = {
print("websocket is connected")
}
//websocketDidDisconnect
socket.onDisconnect = { (error: Error?) in
print("websocket is disconnected: \
(error?.localizedDescription)")
}
//websocketDidReceiveMessage
socket.onText = { (text: String) in
print("got some text: \(text)")
}
//websocketDidReceiveData
socket.onData = { (data: Data) in
print("got some data: \(data.count)")
}
//you could do onPong as well.
socket.connect()
But for some odd reason none of socket methods don't wake up.
EDIT 2:
At the end I ended using https://github.com/tidwall/SwiftWebSocket.
It worked right away.
But thanks to all for help.

Non-responsive stream delegate in Swift

So I was playing around with sockets in Swift and trying to connect the app with my server. I was having the app connect to the IP address of the server and used netcat on the server for testing. During execution, the console output from the app showed it had successfully connected to the server. However, the stream delegate does not seem to be responsive. When I typed into netcat, the app console did not print anything. I have searched for quite a while and found that my implementation is pretty similar to others. Perhaps I am missing something here that I do not see.
Any thought to this problem would be greatly appreciated!
Code is attached below:
import UIKit
class ViewController: UIViewController, StreamDelegate {
let addr:String = "52.34.56.78"
let port:Int = 1234
var inputStream: InputStream?
var outputStream: OutputStream?
override func viewDidLoad() {
super.viewDidLoad()
self.connect(host: addr, port: port)
}
func connect(host: String, port: Int) {
Stream.getStreamsToHost(withName: host, port: port, inputStream: &inputStream, outputStream: &outputStream)
if inputStream != nil && outputStream != nil {
inputStream!.delegate = self
outputStream!.delegate = self
inputStream!.schedule(in: RunLoop.main, forMode: RunLoopMode.defaultRunLoopMode)
outputStream!.schedule(in: RunLoop.main, forMode: RunLoopMode.defaultRunLoopMode)
inputStream!.open()
outputStream!.open()
print("successfully connected")
}
else {
print("connection unsuccessful")
}
}
func stream(aStream: Stream, handleEvent eventCode: Stream.Event) {
if aStream === inputStream {
switch eventCode {
case Stream.Event.errorOccurred:
print("input: ErrorOccurred: \(aStream.streamError?.localizedDescription)")
break
case Stream.Event.openCompleted:
print("input: OpenCompleted")
break
case Stream.Event.hasBytesAvailable:
print("input: HasBytesAvailable")
break
default:
break
}
}
else {
print("unknown stuff happened")
}
}
}
So after a lot of trials and errors, I finally realized the stream() function did not work just because the signature of this function is incorrect/obsolete.
Here is what I was using:
func stream(aStream: Stream, handleEvent eventCode: Stream.Event)
But really it should be:
func stream(_ aStream: Stream, handle eventCode: Stream.Event)
This is likely a syntax conversion from previous Swift version to Swift 3. The XCode compiler usually detects obsolete functions/syntax, but sadly did not catch this one.
Hopefully my answer could help out those who are still suffering from this problem.

Swift 3 inputstream and outputstream connecting but no stream

I have an issue with connecting to a Java socket with StreamDelegate in Swift 3. I am currently in the process of rewriting a big Objective C project where this piece of code runs like a charm, but I can't seem to be able to get it to work in Swift 3. The problem I am having is that the stream function never runs. So "Stream!" is never printed out. The Stream.Status after self.outputStream.open() is called is 1 (opening). The code runs without any problems and "Opening streams on thread:" gets called.
The socket I am trying to connect to is a Java socket.
Note: I have experienced that the debugger has printed out a message saying there is no handler attached, but I do not know it is related.
I have found a couple of similar posts, but not on this exact problem. If anyone has any ideas, I would be happy to hear them out! Thanks to anyone who tries to help.
import Foundation
#objc class SocketConnector : NSObject, StreamDelegate {
var inputStream : InputStream!
var outputStream : OutputStream!
var lock : NSRecursiveLock
override init () {
lock = NSRecursiveLock.init()
}
func connect (host : String, port : Int) -> Bool {
lock.lock()
var readStream : Unmanaged<CFReadStream>?
var writeStream : Unmanaged<CFWriteStream>?
CFStreamCreatePairWithSocketToHost(nil, host as CFString!, UInt32(port), &readStream, &writeStream)
inputStream = readStream!.takeRetainedValue()
outputStream = writeStream!.takeRetainedValue()
self.inputStream.delegate = self
self.outputStream.delegate = self
self.inputStream.schedule(in: RunLoop.current, forMode: .defaultRunLoopMode)
self.outputStream.schedule(in: RunLoop.current, forMode: .defaultRunLoopMode)
self.inputStream.open()
self.outputStream.open()
print("Opening streams on thread: %#", Thread.current)
lock.unlock()
return true
}
func stream (aStream : Stream, handleEvent eventCode : Stream.Event) {
print("Stream!")
switch eventCode {
case Stream.Event.hasBytesAvailable:
print("Stream has bytes:");
break;
case Stream.Event.errorOccurred:
print("Stream error occurred: %#",aStream.streamError?.localizedDescription)
case Stream.Event.openCompleted:
print("Stream has bytes:")
break
case Stream.Event.endEncountered:
print("Stream ended")
self.closeStreams()
break
default:
break
}
}
Your implement is not compatible with stream method of StreamDelegate.
You must change to this:
func stream(_ aStream: Stream, handle eventCode: Stream.Event)
instead of
func stream (aStream : Stream, handleEvent eventCode : Stream.Event

NSTimer never call the selector function (Swift)

keep calm, most probably this question is not a duplicate.
I have already tried all solutions in stack overflow, but nothing worked for me.
I have a client thread that send and receive a "keepalive" string (a ping) continually from another host. If it doesn't receive the "keepalive" within the KeepAliveMax, it closes the streams and say good bye (in theory, but this yet work in progress).
Now my problem is that I used a NSTimer to call the updateKeepAlive function, but it is never invoked..I don't understand why :(
I have tried also to set the NSTimer RunLoop manually, but it not works
The follow is the part of code where the selector function should be initialized and called every second:
public class Client: NSObject, NSStreamDelegate {
var serverAddress: CFString
let serverPort: UInt32 = 50000
private var inputStream: NSInputStream!
private var outputStream: NSOutputStream!
private var connecting:Bool
private var byteRead:Int
private var byteWrite:Int
private let keepAliveMax:Double = 5000
private var keepAliveTime:Double
private var connected:Bool
var timer: NSTimer?
init(ip:String) {
serverAddress = ip
connecting = false
connected = false
byteRead = 0
byteWrite = 0
keepAliveTime = 0
super.init()
let thread = NSThread(target:self, selector:#selector(connect), object:nil)
thread.start()
print("thread is started and now I can continue with other tasks..")
}
func connect() {
connecting = true
while connecting {
print("connecting...")
var readStream: Unmanaged<CFReadStream>?
var writeStream: Unmanaged<CFWriteStream>?
CFStreamCreatePairWithSocketToHost(nil, self.serverAddress, self.serverPort, &readStream, &writeStream)
// Documentation suggests readStream and writeStream can be assumed to
// be non-nil. If you believe otherwise, you can test if either is nil
// and implement whatever error-handling you wish.
self.inputStream = readStream!.takeRetainedValue()
self.outputStream = writeStream!.takeRetainedValue()
self.inputStream.delegate = self
self.outputStream.delegate = self
self.inputStream.scheduleInRunLoop(NSRunLoop.currentRunLoop(), forMode: NSDefaultRunLoopMode)
self.outputStream.scheduleInRunLoop(NSRunLoop.currentRunLoop(), forMode: NSDefaultRunLoopMode)
self.inputStream.open()
self.outputStream.open()
// send handshake
byteWrite = writeLine("handshake")
print("written: \(byteWrite) for handshake")
// wait to receive handshake
print("waintig for handshake...")
if readLine() == "handshake" {
connected = true
print("Client: connection estabilished correctly")
// close the waiting popup and start with SendBox...
// in progress...
// send keepAlive
byteWrite = writeLine("keepalive")
print("written: \(byteWrite) for keepalive")
//======================== THIS NOT WORK PROPERLY ============================================================
timer = NSTimer.scheduledTimerWithTimeInterval(1, target: self, selector: #selector(Client.updateKeepAlive), userInfo: nil, repeats: true)
/*
timer = NSTimer(timeInterval: 1, target: self, selector: #selector(Client.updateKeepAlive), userInfo: nil, repeats: true)
NSRunLoop.currentRunLoop().addTimer(timer!, forMode: NSRunLoopCommonModes)
*/
//============================================================================================================
keepAliveTime = NSDate().timeIntervalSince1970 * 1000
print("Client: Timer started")
while self.inputStream.streamStatus != NSStreamStatus.Closed ||
self.outputStream.streamStatus != NSStreamStatus.Closed
{
print("Client: under the while of keepAlive");
if readLine() == "keepalive"
{
keepAliveTime = NSDate().timeIntervalSince1970 * 1000
writeLine("keepalive")
print("Client: keepalive received");
}
else
{
print("Client: not keepalive: ");
// close streams...... work in progress
break
}
sleep(1)
}
}
else{
print("wrong handshake")
}
print("closing streams..")
connecting = false
self.inputStream.close()
self.outputStream.close()
self.timer?.invalidate()
}
}
And the follow is the updateKeepAlive function:
func updateKeepAlive(){
print("in updateKeepalive function") // <---- NEVER PRINTED
/* in progress .....
........./*
}
In a new thread begining, there is no input sources or timers are attached to the run loop. So the run loop while not be active. You can call run function after call scheduledTimerWithTimeInterval.
timer = NSTimer.scheduledTimerWithTimeInterval(1, target: self, selector: #selector(TimerTest.updateKeepAlive), userInfo: nil, repeats: true)
NSRunLoop.currentRunLoop().run()

CFStreamCreatePairWithSocketToHost crashes unexpectedly with Swift

Finally time to ask my first question here!
Up front: Xcode 7.1.1, OS 10.11.2, iOS 9.0.2 (on physical device)
I'm writing a small app that communicates with my Raspberry Pi. I've got some working code that's written in Obj-C (for iOS 7) borrowed from a tutorial, and it all works fine for me in Obj-C (connects and behaves as expected with the Pi). The issue lies with rewriting it for Swift/iOS 9 (which is the goal).
The good bit:
func initNetworkCommunication() {
var readStream: Unmanaged<CFReadStreamRef>?
var writeStream: Unmanaged<CFWriteStreamRef>?
CFStreamCreatePairWithSocketToHost(nil, "192.168.1.22", 777, &readStream, &writeStream)
inputStream = readStream?.takeRetainedValue() as! NSInputStream
outputStream = writeStream?.takeRetainedValue() as! NSOutputStream
inputStream.scheduleInRunLoop(NSRunLoop.currentRunLoop(), forMode: NSDefaultRunLoopMode)
outputStream.scheduleInRunLoop(NSRunLoop.currentRunLoop(), forMode: NSDefaultRunLoopMode)
inputStream.open()
outputStream.open()
}
I believe the issue to lie in the above as this is the last method call in the stack, however the application crashes quietly with little information:
Any help would be much appreciated!
Please feel free to ask for more information.
p.s. I understand the formatting on this site is rather "strict", anything I missed, overdid, etc, please let me know :)
I've solved it.
Firstly:
inputStream = readStream?.takeRetainedValue() as! NSInputStream
outputStream = writeStream?.takeRetainedValue() as! NSOutputStream
Should be:
inputStream = readStream!.takeRetainedValue()
outputStream = writeStream!.takeRetainedValue()
Secondly, I abstracted the connection out of the ViewController (where I had this method initially) to a new class called Connection.
Here's Connection:
import UIKit
class Connection: NSObject, NSStreamDelegate {
var inputStream: NSInputStream!
var outputStream: NSOutputStream!
func connect() {
var readStream: Unmanaged<CFReadStream>?
var writeStream: Unmanaged<CFWriteStream>?
CFStreamCreatePairWithSocketToHost(nil, "192.168.1.22", 777, &readStream, &writeStream)
self.inputStream = readStream!.takeRetainedValue()
self.outputStream = writeStream!.takeRetainedValue()
self.inputStream.delegate = self
self.outputStream.delegate = self
self.inputStream.scheduleInRunLoop(NSRunLoop.currentRunLoop(), forMode: NSDefaultRunLoopMode)
self.outputStream.scheduleInRunLoop(NSRunLoop.currentRunLoop(), forMode: NSDefaultRunLoopMode)
self.inputStream.open()
self.outputStream.open()
}
}
Often I find starting to type these helps me out :p

Resources