How to connect to websocket in swift? - ios

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.

Related

iOS swift socket communication: release outstream

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

Multipeer Connectivity how to get the name of the advertising peer?

iOS12 Swift 5.x
I using Multipeer Connectivity. So far it works well, if I connect my devices in the correct order.
Using boiler plate code, tracked down the problem to here.
func setupStream() {
do {
outputStream = try session.startStream(withName: "chat", toPeer: session.connectedPeers.first!)
for debug in session.connectedPeers {
print("Peer connected \(debug.displayName)")
}
} catch {
print("unable to open stream")
}
if let outputStream = outputStream {
outputStream.delegate = self
outputStream.schedule(in: RunLoop.main, forMode:RunLoop.Mode.default)
outputStream.open()
}
}
Now if I start the advertiser first, and then the browsers... it works well, very well. But if I start the browsers BEFORE the advertiser, they seem to see each other... even if I am not advertising from them and the service from them and connect to the wrong client.
How to say connect to the peer adverting the service and not anybody and everybody you find...
iOS 12, Swift 5
Found a solution which I post here, a nice simple one. When I advertised the service I do so with some discovery info. The key here, the discover variable.
Block looks this.
class ColorService : NSObject {
lazy var session : MCSession = {
let session = MCSession(peer: self.myPeerId, securityIdentity: nil, encryptionPreference: .required)
session.delegate = self
return session
}()
var delegate : ColorServiceDelegate?
// Service type must be a unique string, at most 15 characters long
// and can contain only ASCII lowercase letters, numbers and hyphens.
private let ColorServiceType = "example-color"
private let myPeerId = MCPeerID(displayName: UIDevice.current.name)
private let serviceAdvertiser : MCNearbyServiceAdvertiser
override init() {
let discover:[String:String] = ["prime":myPeerId.displayName]
self.serviceAdvertiser = MCNearbyServiceAdvertiser(peer: myPeerId, discoveryInfo: discover, serviceType: ColorServiceType)
super.init()
self.serviceAdvertiser.delegate = self
self.serviceAdvertiser.startAdvertisingPeer()
}
deinit {
self.serviceAdvertiser.stopAdvertisingPeer()
}
func stopAdvertising() {
self.serviceAdvertiser.stopAdvertisingPeer()
}
On the other side, when I am looking for the service, I only invite peers who have the discovering set, obviously using said info as the peer to invite.
extension ColorSearch : MCNearbyServiceBrowserDelegate {
func browser(_ browser: MCNearbyServiceBrowser, didNotStartBrowsingForPeers error: Error) {
NSLog("%#", "didNotStartBrowsingForPeers: \(error)")
}
func browser(_ browser: MCNearbyServiceBrowser, foundPeer peerID: MCPeerID, withDiscoveryInfo info: [String : String]?) {
NSLog("%#", "foundPeer: \(peerID)")
NSLog("%#", "invitePeer: \(peerID)")
NSLog("%#", "discoverInfo: \(info)")
primePeer = info!["prime"]
if primePeer != nil {
if peerID.displayName == primePeer {
browser.invitePeer(peerID, to: self.session, withContext: nil, timeout: 10)
}
}
}
func browser(_ browser: MCNearbyServiceBrowser, lostPeer peerID: MCPeerID) {
NSLog("%#", "lostPeer: \(peerID)")
}
func disconnect() {
self.session.disconnect()
}
And I don't get a matrix of connections, I get a single master and a bunch of slaves. Maybe not what multipeerconnectivity was intended, but a good solution never the less.

How to write a socket server in iOS

I want to communicate two iOS devices in local area network via socket.
Right now, I am able to communicate with web socket by writing a client side code in iOS, but i want to know there is any way to communicate two iOS devises via socket, so one iOS device work as server and other iOS device work as a client.
For Client side i am using below code:
// MARK:- communication initilaize Methode.
func initNetworkCommunication() {
//Initilize network communication.
host = "192.168.2.15" //Server IP
port = 8788 //Port for communication
Stream.getStreamsToHost(withName: host!, port: port!, inputStream: &inputstream, outputStream: &outputstream)
if ((inputstream != nil) && (outputstream != nil)) {
// connection not failed.
print("Connection to \(host!):\(port!)")
inputstream?.delegate = self
outputstream?.delegate = self
inputstream?.schedule(in: RunLoop.current, forMode: RunLoopMode.defaultRunLoopMode)
outputstream?.schedule(in: RunLoop.current, forMode: RunLoopMode.defaultRunLoopMode)
inputstream?.open()
outputstream?.open()
}
}
And for handling a connection, implement a delegate of StreamDelegate as:
// MARK:- StreamDelegate Methods.
func stream(_ aStream: Stream, handle eventCode: Stream.Event) {
switch eventCode {
case Stream.Event.openCompleted:
print("Connected")
//Make connection connect.
outputstream_Status = true
break
case Stream.Event.hasBytesAvailable:
break
case Stream.Event.hasSpaceAvailable:
break
case Stream.Event.endEncountered:
print("Connection closed by the server.")
//Make connection disconnect.
outputstream_Status = false
//De-Initialize the Stream.
closeNetworkCommunication()
break
case Stream.Event.errorOccurred:
print("Can not connect to the host!")
//Make connection disconnect.
outputstream_Status = false
break
default:
break
}
}
}

CocoaAsyncSocket sent UDP but no response

infomation: I have a device on my local network, it is an UDP server and when I send "hello" to it I will get a "how are you" response, but I don't know the IP, so I have to send "hello" to each IP of my local network to find the device.
problem: when I send "hello" to my device,s IP directly, I get the response, but I send "hello" to each IP, I get no response most of the time( sometime I get a response)
some of my code:
//first create a instance of GCDAsyncUdpSocket
udpSocket = GCDAsyncUdpSocket(delegate: self, delegateQueue: DispatchQueue.global())
do{
log.debug("bind port")
try self.udpSocket?.enableReusePort(true)
try self.udpSocket?.bind(toPort: 0)
udpBindedToPort = true
try self.udpSocket?.beginReceiving()
}catch {
log.error("error")
udpBindedToPort = false
self.delegate?.findDeviceTimeout(0)
return
}
// this does not work
Async.background({[weak self] () in
for i in 1...255 {
let targetIP = ip_sub + ".\(i)"
self?.send(Define.scanDeviceMessage, host: targetIP, tag: Define.scanDeviceTag)
}
})
//this works
Async.background({[weak self] () in
let targetIP = "192.168.0.101"
self?.send(Define.scanDeviceMessage, host: targetIP, tag: Define.scanDeviceTag)
})
//the delegate
#objc func udpSocket(_ sock: GCDAsyncUdpSocket, didReceive data: Data, fromAddress address: Data, withFilterContext filterContext: Any?
) {
print("UDP接收到数据 : \(String(data:data, encoding: String.Encoding.utf8))")
}
thanks for your help, this drives me crazy, some times it works, but sometime not, and now it never works anymore

Implementing A UDP "Listener" In Swift?

I've been trying to somewhat reverse engineer a project to discover Logitech Harmony Hub devices on my network, and posted this question to see if someone could help me understand UDP broadcast. The answer explained that I've implementing the send portion of the UDP broadcast, but I've not implemented anything to "listen" for responses. And that's where I'm struggling. Here's my send code;
import UIKit
import CocoaAsyncSocket
class ViewController: UIViewController, GCDAsyncUdpSocketDelegate {
var address = "255.255.255.255"
var port:UInt16 = 5224
var socket:GCDAsyncUdpSocket!
var socketReceive:GCDAsyncUdpSocket!
var error : NSError?
override func viewDidLoad() {
super.viewDidLoad()
let message = "_logitech-reverse-bonjour._tcp.local.\n61991".dataUsingEncoding(NSUTF8StringEncoding)
socket = GCDAsyncUdpSocket(delegate: self, delegateQueue: dispatch_get_main_queue())
socket.sendData(message, toHost: address, port: port, withTimeout: 1000, tag: 0)
do {
try socket.enableBroadcast(true)
} catch {
print(error)
}
}
func udpSocket(sock: GCDAsyncUdpSocket!, didConnectToAddress address: NSData!) {
print("didConnectToAddress");
}
func udpSocket(sock: GCDAsyncUdpSocket!, didNotConnect error: NSError!) {
print("didNotConnect \(error)")
}
func udpSocket(sock: GCDAsyncUdpSocket!, didSendDataWithTag tag: Int) {
print("didSendDataWithTag")
}
func udpSocket(sock: GCDAsyncUdpSocket!, didNotSendDataWithTag tag: Int, dueToError error: NSError!) {
print("didNotSendDataWithTag")
}
func udpSocket(sock: GCDAsyncUdpSocket!, didReceiveData data: NSData!, fromAddress address: NSData!, withFilterContext filterContext: AnyObject!) {
var host: NSString?
var port1: UInt16 = 0
GCDAsyncUdpSocket.getHost(&host, port: &port1, fromAddress: address)
print("From \(host!)")
let gotdata: NSString = NSString(data: data!, encoding: NSUTF8StringEncoding)!
print(gotdata)
}
}
I see I have the code to handle the response (in didReceiveData), but I'm unsure what I need to implement to get the listening going;
Do I need to "bind" to the listener port (in this case, 61991)?
Do I need to "join the multicast group"? And if so, at what address? I tried doing so at "255.255.255.255", which creates a setSocketOpt() error when I build.
I know I need to call beginReceiving(), and can I do all this on socket, or do I need to instantiate a separate socket for the listening?
Edit: Resolved
The below answer absolutely helped me to solve the problem. It seemed I wasn't getting a response because I had not fully implemented a means of handling the incoming response.
Per the code provided in the answer below, I added the following;
// Setup the other socket (used to handle the response from the Harmony hub)
otherSocket = GCDAsyncSocket(delegate: self, delegateQueue: dispatch_get_main_queue())
do {
// Accept connections on port 61991
try otherSocket.acceptOnPort(61991)
} catch {
// Handle any errors here
print(error)
}
I also set this controller to be a GCDAsyncSocketDelegate, which seemed to do the trick. I was able to read the response in didReadData.
The following code changes enabled me to receive UDP packets that I sent from my Mac using netcat, but my Harmony hub didn't seem to send anything, so I am not sure if the data that is being sent is correct.
override func viewDidLoad() {
super.viewDidLoad()
let message = "_logitech-reverse-bonjour._tcp.local.\n61991".dataUsingEncoding(NSUTF8StringEncoding)
socket = GCDAsyncUdpSocket(delegate: self, delegateQueue: dispatch_get_main_queue())
do {
try self.socket.bindToPort(61991)
try self.socket.beginReceiving()
try socket.enableBroadcast(true)
socket.sendData(message, toHost: address, port: port, withTimeout: 1000, tag: 0)
} catch {
print(error)
}
}
From the command line you can test receiving using the command
echo -n "hello" | nc -4u -w1 x.x.x.x 61991

Resources