I am using the following code when trying to connect a GCDAsyncSocket but I don't know how to get it to expect SSL. Right now my code is saying that it is connected but going to disconnected seconds later. The socket never shows me as connecting.
func connect(){
bsocket = GCDAsyncSocket(delegate: self, delegateQueue: DispatchQueue.main)
bsocket.delegate = self
do {
try bsocket.connect(toHost: self.socketHost(), onPort: 443)
} catch let e {
NSLog("Error connecting socket: \(e)")
}
}
func socket(_ sock: GCDAsyncSocket, didRead data: Data, withTag tag: Int) {
let response = NSString(data: data, encoding: String.Encoding.utf8.rawValue)
print("Received Response: \(response)")
bsocket.readData(withTimeout: -1.0, tag: 0)
}
func disconnect(){
}
func socket(_ socket : GCDAsyncSocket, didConnectToHost host:String, port p:UInt16)
{
var settings = [AnyHashable: Any](minimumCapacity: 3)
settings[(kCFStreamSSLPeerName as String)] = self.socketHost()
// // Allow self-signed certificates
settings[(kCFStreamSSLPeerName as String)] = Int(true)
// // In fact, don't even validate the certificate chain
settings[(kCFStreamSSLValidatesCertificateChain as String)] = Int(false)
print("Connected to \(host) on port \(p).")
socket.readData(withTimeout: -1.0, tag: 0)
// sendRequest()
}
func socketDidDisconnect(_ sock: GCDAsyncSocket, withError err: Error?) {
NSLog("Socket Disconnected: \(err)")
}
func socket(_ sock:GCDAsyncSocket, didAcceptNewSocket newSocket:GCDAsyncSocket)
{
NSLog("New socket received: \(newSocket)")
}
You seem to be setting the value for the kCFStreamSSLPeerName key twice:
settings[(kCFStreamSSLPeerName as String)] = self.socketHost()
// // Allow self-signed certificates
settings[(kCFStreamSSLPeerName as String)] = Int(true)
Also, if you check out https://github.com/robbiehanson/CocoaAsyncSocket/blob/cae3732972501bbb39720be6213f30dc99d9f153/Source/GCD/GCDAsyncSocket.h
you'll see that kCFStreamSSLValidatesCertificateChain is now an unavailable key and will throw an exception.
Related
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.
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
I have problem with receiving messages on specific port. Sending is working fine.
The code looks like that :
import Foundation
import CocoaAsyncSocket
class InSocket: NSObject, GCDAsyncUdpSocketDelegate {
//let IP = "192.168.1.196"
let PORT:UInt16 = 14000
var isocket:GCDAsyncUdpSocket!
override init(){
super.init()
setupConnection()
}
func setupConnection(){
isocket = GCDAsyncUdpSocket(delegate: self, delegateQueue: DispatchQueue.main)
do{
try isocket.bind(toPort:PORT)
try isocket.beginReceiving()
} catch {print("ErrorReceive")}
}
func udpSocket(sock: GCDAsyncUdpSocket!, didReceiveData data: NSData, fromAddress address: NSData!, withFilterContext filterContext: AnyObject!) {
let str = NSString(data: data as Data, encoding: String.Encoding.ascii.rawValue)
print(str)
}
}
I see in network statistics that application is receiving packets:
Packets
But I don't see anything in a console.
Some ideas ?
Add before delegate function: func udpSocket(....) that attribute
#objc(udpSocket:didReceiveData:fromAddress:withFilterContext:)
XCode 8 will issue a warning, but do not pay attention, UDP packets will arrive.
I am working on trying develop a means of discovering Logitech Harmony Hub devices on my local network, from an iOS app. The concept is inspired by this NODE.JS project, which seems to send out a UDP broadcast to the 255.255.255.255 address, and then procures the Logitech's IP address (which is all I'm after). When testing the NODE.JS project on my home network from my Mac, it successfully finds the Logitech Harmony Hub.
I am using CocoaASyncSocket, and must admit, my understanding of how UDP broadcast/discovery works may be askew here. Here's what I'm doing;
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.bindToPort(port)
} catch {
print(error)
}
do {
try socket.enableBroadcast(true)
} catch {
print(error)
}
do {
try socket.beginReceiving()
} 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)
}
}
When I compile this, the only response I get is the message I just sent out;
didSendDataWithTag
From ::ffff:192.168.1.101
_logitech-reverse-bonjour._tcp.local.
61991
From 192.168.1.101
_logitech-reverse-bonjour._tcp.local.
61991
I fear that I have a conceptual understanding issue with the broadcast here, and am sincerely hoping that someone may be able to point me to a resource or help to understand why I'm not getting any response from the device in my code.
Thanks!
From the looks of the code it seems that you have only implemented half of the solution. The way it works is:
A broadcast message is sent to port 5224. This message includes the string logitech-reverse-bonjour._tcp.local. plus the port number that the Harmony should connect back to - in your case you have hardcoded 61991.
Presumably the Harmony receives this packet, recognises the message and then initiates a connection back to the device that sent the broadcast on the nominated port (61991 in this case).
Since your app is not listening on this port you don't get any response. This is implemented in the responseCollector.js file in the node.js project
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