I'm new to SocketRocket and what I want to do is to scan the network 192.168.1.x and when it finds the server connect to it.
this is my code:
NSString *seg=#"ws://192.168.1.";
NSString *puerto=#":5000";
self.socketReady = NO;
for (int i=0; i<255; i++) {
NSMutableString *ip =[[NSMutableString alloc]init];
[ip appendString:seg];
[ip appendString:[#(i) stringValue]];
[ip appendString:puerto];
self.serverSocket = [[SRWebSocket alloc] initWithURL:[[NSURL alloc] initWithString:ip]];
self.serverSocket.delegate = self;
[self.serverSocket open];
}
I know that [self.serverSocket open] can only be done once, but I don't know how to scan and then connect to the server.
[self.serverSocket open] is an asynchronous call - that is, it will immediately return, with your delegate method webSocketDidOpen: being called when/if the socket successfully connects or didFailWithError: if it doesn't.
You could change you loop so that it used a local variable rather than your property -
NSString *seg=#"ws://192.168.1.";
int puerto=5000;
for (int i=0; i<255; i++) {
NSString *ip =[NSString stringWithFormat:#"%#%d:%d",seg,i,puerto];
SRWebSocket *newSocket = [[SRWebSocket alloc] initWithURL:[[NSURL alloc] initWithString:ip]];
newSocket.delegate = self;
[newSocket open];
}
and then in the delegate method you would need to store the socket that connected successfully -
- (void)webSocketDidOpen:(SRWebSocket *)webSocket
{
self.serverSocket=webSocket;
//TO DO start processing data on the socket...
}
BUT this is a pretty ugly way of locating and connecting to a service:
It 'spams' the local network with multiple connection requests
It wastes a considerable amount of resources on your local device
It doesn't handle the case where there are multiple services/servers running on your port on different devices
What if the local network isn't 192.168.1.x ?
You should investigate other options for identifying your servers address such as Bonjour or even a simple application preference to set the server address
Related
I’m working on an app that uses MPC. Sometimes it's working, A and B client connects like a charm but sometimes connection fails, I get the weird error from MCNearbyServiceBrowser.
First of all, I initialize advertiser, browser, and session on both A and B devices.
_peerID = [[MCPeerID alloc] initWithDisplayName:uniqueId];
session = [[MCSession alloc] initWithPeer:_peerID securityIdentity:nil encryptionPreference:MCEncryptionNone];
session.delegate = self;
NSDictionary *dict = #{#“uniqueId” : uniqueId};
_advertiser = [[MCNearbyServiceAdvertiser alloc] initWithPeer:_peerID discoveryInfo:dict serviceType:#“my-app”];
_advertiser.delegate = self;
_browser = [[MCNearbyServiceBrowser alloc] initWithPeer:_peerID serviceType:#“my-app”];
_browser.delegate = self;
[_advertiser startAdvertisingPeer];
[_browser startBrowsingForPeers];
A and B have a unique ID for deciding what device should invite the other, and what device should accept the invitation (it's necessary to prevent A and B inviting each other at the same time). After they found each other, found peer MCNearbyServiceBrowser delegate called. A device has less uniqueId, and it sends invitation request.
-(void)browser:(MCNearbyServiceBrowser *)browser foundPeer:(MCPeerID *)peerID withDiscoveryInfo:(NSDictionary<NSString *,NSString *> *)info {
if (![[session connectedPeers] containsObject:peerID]) {
NSInteger targetUniqueId = [[peerID displayName] integerValue];
NSInteger myUniqueId = [uniqueId integerValue];
if(myUniqueId<targetUniqueId){
NSLog(#“invitation sent”);
[browser invitePeer:peerID toSession:session withContext:nil timeout:inviteTimeout];
}
}
}
Accepting invitation (this called on B device):
-(void)advertiser:(MCNearbyServiceAdvertiser *)advertiser didReceiveInvitationFromPeer:(MCPeerID *)peerID withContext:(NSData *)context invitationHandler:(void (^)(BOOL, MCSession * _Nonnull))invitationHandler {
NSInteger targetUniqueId = [[peerID displayName] integerValue];
NSInteger myUniqueId = [uniqueId integerValue];
if(myUniqueId>targetUniqueId){
NSLog(#“accepting invitation”);
invitationHandler(YES, session);
}
}
Also implemented certificate handler like this (some post complaining about it, when not implemented it can cause connection problems w/o using security identity too):
-(void)session:(MCSession *)session didReceiveCertificate:(NSArray *)certificate fromPeer:(MCPeerID *)peerID certificateHandler:(void (^)(BOOL))certificateHandler {
certificateHandler(YES);
}
I logged both devices, then:
device A: invitation sent
device B: accepting invitation
device A: [MCNearbyServiceBrowser] Received an invitation response from [3362,090D4987], but we never sent it an invitation. Aborting!
Few secs after, when not connected I stop browsing peers, then start browsing again. After finding peer called I make same connection try again, re-invite peer on device B, what's accepting the invitation. The result can be the same or the connection state switches to Connected. These are the 2 options. Sometimes devices can connect in the first try or in less than 3 tries, but sometimes after many tries. Last time they could connect after about 40 abort message, it took about 15 mins when connection got established.
What I am doing wrong, why device A don't know anything about his own invitation?
An MCPeerID has a hashvalue member. You can compare them directly.
Two MCPeerID objects created using the same display name will not have the same hash value. This is to prevent name collisions.
If you want to recognize and be recognized by previously connected peers you must save and restore the actual MCPeerID objects.
Paste the following code into a playground and run it to see what I mean.
import MultipeerConnectivity
let hostName = "TestPlaygroundHostName"
let firstPeerID = MCPeerID(displayName: hostName)
let secondPeerID = MCPeerID(displayName: hostName)
firstPeerID.hashValue == secondPeerID.hashValue
I'm working on a local network based game. By this I mean that you can play it on different iphone on local network wifi or bluetooth. I did the implementation with NSStream with a tcp connection to transfer data. I have one server who listens the network :
service = [[NSNetService alloc] initWithDomain:#"local."// 4
type:CreateNSString(serviceType)
name:CreateNSString(serviceName)
port:80];
if(service)
{
service.includesPeerToPeer = YES;
[service setDelegate:delegatePublisherObject];// 5
[service publishWithOptions:NSNetServiceListenForConnections];
And clients who search for game sessions around :
serviceBrowser = [[NSNetServiceBrowser alloc] init];
serviceBrowser.includesPeerToPeer = YES;
[serviceBrowser setDelegate:delegateBrowserObject];
[serviceBrowser searchForServicesOfType: CreateNSString(service) inDomain:#"local"];
But i have an issue with the data transfer between them. What i did from now, is :
i queued the data i want to send. If i can write it directly in the output stream i do
dataWriteQueue insertObject:data atIndex:0];
if (flag_canSendDirectly)
[self _sendData];
...
- (void)_sendData {
NSData *data = [dataWriteQueue lastObject];
flag_canSendDirectly = NO;
if (data == nil)
{
flag_canSendDirectly = YES;
return;
}
bufferToSend = (uint8_t *)[data bytes];
NSInteger bytesWritten;
bytesWritten = [_outputStream write:bufferToSend maxLength:dataBufferLimit - currentDataOffset];
if the callback NSStreamEventHasSpaceAvailable is called i send the last pieces of data queued.
case NSStreamEventHasSpaceAvailable: {
if (stream == _outputStream)
{
//read a new chunk of data
[self _sendData];
}
} break;
I have stopped the browsing of devices when i transfer data between peers.
The transfer is done well. But my issue here is that the transfer is pretty fast in bluetooth. But slower in wifi. I don't understand this, it doesn't seems logical. Do you have any clues about this issue ? The browsing is shut down when the peers are connected.
I am making an app to send UDP packets in order to switch on a LED bulb. I have been able to perform all the actions when I am connecting to the Ad-hoc created by the Wifi bridge.
Now, I want to configure the Wifi bridge so that it can connect to my main router. I have the AT command set to perform this procedure but somehow I am not able to receive the response form the Wifi bridge for the commands which I am sending to it.
The procedure is as follows:-
Step 1 : Send UDP message to the LAN broadcast IP address of "10.10.100.255" and port of 48899 => "Link_Wi-Fi"
All Wifi bridges on the LAN will respond with their details. Response is "10.10.100.254, ACCF232483E8"
Step 2 : (optional for changing settings on the wifi bridge): Then send "+ok" to the LimitlessLED Wifi Bridge. Send UDP message to the response IP address returned from step 1 "10.10.100.254" => "+ok"
Step 3 : (optional for changing settings on the wifi bridge): After that you may send AT commands (ending with \r\n) to the module.
The code for sending the UDP packets is as follows
-(void)configureWifi{
counter++;
NSString *host = #"10.10.100.255";
if ([host length] == 0)
{
[self logError:#"Address required"];
return;
}
int port = 48899; //[portField.text intValue];
if (port <= 0 || port > 65535)
{
[self logError:#"Valid port required"];
return;
}
NSString *msg = #"Link_Wi-Fi";
NSData *data = [msg dataUsingEncoding:NSUTF8StringEncoding];
NSLog(#"the message sent is %#", data);
[udpSocket sendData:data toHost:host port:port withTimeout:-1 tag:tag];
}
Now in order to setup the socket and to receive the data I am using these two delegate methods:
- (void)setupSocket
{
// Setup our socket.
// The socket will invoke our delegate methods using the usual delegate paradigm.
// However, it will invoke the delegate methods on a specified GCD delegate dispatch queue.
//
// Now we can configure the delegate dispatch queues however we want.
// We could simply use the main dispatc queue, so the delegate methods are invoked on the main thread.
// Or we could use a dedicated dispatch queue, which could be helpful if we were doing a lot of processing.
//
// The best approach for your application will depend upon convenience, requirements and performance.
//
// For this simple example, we're just going to use the main thread.
udpSocket = [[GCDAsyncUdpSocket alloc] initWithDelegate:self delegateQueue:dispatch_get_main_queue()];
NSError *error = nil;
if (![udpSocket bindToPort:0 error:&error])
{
[self logError:FORMAT(#"Error binding: %#", error)];
return;
}
if (![udpSocket beginReceiving:&error])
{
[self logError:FORMAT(#"Error receiving: %#", error)];
return;
}
[self logInfo:#"Ready"];
}
and to Receive data this is the method which is note getting called after sending the UDP packets. This is the delegate method of the GCDAsyncUdpSocket class which I have used in my project in order to send and receive the UDP packets.
- (void)udpSocket:(GCDAsyncUdpSocket *)sock didReceiveData:(NSData *)data
fromAddress:(NSData *)address
withFilterContext:(id)filterContext
{
NSString *msg = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
if (msg)
{
[self logMessage:FORMAT(#"RECV: %#", msg)];
}
else
{
NSString *host = nil;
uint16_t port = 0;
[GCDAsyncUdpSocket getHost:&host port:&port fromAddress:address];
[self logInfo:FORMAT(#"RECV: Unknown message from: %#:%hu", host, port)];
}
}
Once I am able to receive the response I will be able to send the next AT commands in order to configure the Bridge.
Thanks. Any help will be appreciated.
Here are the troubleshooting steps I recommend that you use :
1- I'm assuming you are using ARC so make sure that your udpSocket variable has a strong reference throughout the asynchronous communication. If it is being freed, then that could explain the absence of a callback.
2- Make sure the communication is really happening the way you think it is. Use a software such as Wireshark to capture the packets being exchanged on the network. This should allow you to confirm that your packets do get sent upon calling sendData: and it will also allow you to confirm whether or not you are getting a reply back.
3- Make sure you are using the GCDAsyncUdpSocket properly. Considering you want to broadcast a message, you shouldn't be calling bindToPort:error: in your setupSocket method. Instead you should be calling enableBroadcast:error:. Considering you also want to receive packets after broadcasting, you should use the connectToHost:onPort:error: method to change the state of the socket to allow for bidirectional communication. After that is done, you can replace your usage of sendData:toHost:port:withTimeout:tag: by sendData:withTimeout:tag:. Finally, you can call beginReceiving: so that the delegate gets called for any incoming packets.
4- If this still doesn't get you through it, I recommend that you read throughly the documentation of the GCDAsyncUdpSocket which is very well documented.
You can trouble shoot the problem using Wireshark or any network capture tool.
We use to work in similar kind of project where we used Wireshark extensively.
If packet has reached device(Z-Wave ) it will send out some sort of Ack.
this will help to make sure packets are getting out.
I'm developing an application which is connecting to another device and in order to connect to it my app has to find the IP of it. I'm doing it through UDP socket. I'm sending a message to the server application in the other device and then the server app sends me another message but after I receive it I only use the IP address. Then I want to get it from this .m file to another .m file which is going to make the TCP connection between the two devices. Here's the code I use:
NSString *SearchCommand = #"AT+S";
static int receivedByteCount = 0;
BOOL connectedThroughUDP;
- (void) activate {
connectedThroughUDP = NO;
AsyncUdpSocket *udpSocket = [[AsyncUdpSocket alloc] initWithDelegate:self];
[udpSocket bindToPort:1234 error:nil ];
[udpSocket enableBroadcast:YES error:nil];
NSData* data=[SearchCommand dataUsingEncoding:NSUTF8StringEncoding];
if ([udpSocket sendData:data toHost:#"255.255.255.255" port:30100 withTimeout:3 tag:0])
{
//2 second timeout. onUdpSocket methods will provide results
[udpSocket receiveWithTimeout:2 tag:0];
NSLog(#"Connected.");
}
}
- (BOOL)onUdpSocket:(AsyncUdpSocket *)sock didReceiveData:(NSData *)data withTag:(long)tag fromHost:(NSString *)host port:(UInt16)port{
NSString *receiveddata = [[NSString alloc] initWithData:data encoding:NSASCIIStringEncoding];
addressOfServer = host;
NSLog(#"addressOfServer = %#", addressOfServer);
connectedThroughUDP = YES;
return YES;
}
- (NSString *) getHost {
return addressOfServer;
}
- (BOOL) isItConnected {
return connectedThroughUDP;
}
addressOfServer is global variable.
In the Log when I want NSLog(#"addressOfServer = %#", addressOfServer); I receive the IP address which I want but then when I want to access it from the getHost method I receive (null).
I know it will be something very simple but it caused me a real headache in the past 3 hours so I would be very thankful if you can help me!
Are you calling these 3 lines right after each other like so:
UDPConnection *udpConnection = [[UDPConnection alloc] init];
[udpConnection activate];
host = [udpConnection getHost];
If so your issue is that [udpConnection activate]; is going to take some time to connect and figure out the address. You are calling [udpConnection getHost]; too soon.
You will need to setup a delegate or completion block that will be triggered when didReceiveData is fired
In order to read a new display name of a peer I need to kill and renew the GKSession. Setting it to nil and initiate it anew does not work. In the code below, the NSLog in the for-loop to show the available peers is not called (there's no error message):
-(IBAction) btnRefresh:(id) sender {
self.currentSession = nil;
self.currentSession = [[GKSession alloc] initWithSessionID:#"anything" displayName:name sessionMode:GKSessionModePeer];
self.currentSession.delegate = self;
self.currentSession.available = YES;
self.currentSession.disconnectTimeout = 0;
[self.currentSession setDataReceiveHandler:self withContext:nil];
peerListAvailable = [[NSMutableArray alloc] initWithArray:[currentSession peersWithConnectionState:GKPeerStateAvailable]];
for (NSString *peer in peerListAvailable) {
NSLog(#"found available peer; checking name and ID... %#, %#",[currentSession displayNameForPeer:peer], peer);
}
What is wrong with setting the currentSession to nil and initiate it anew?
Maybe you know of another way to renew a GKSession?
Thanks very much in advance.
The following methods illustrate GKSession setup and teardown:
- (void)setupSession
{
gkSession = [[GKSession alloc] initWithSessionID:nil displayName:nil sessionMode:GKSessionModePeer];
gkSession.delegate = self;
gkSession.disconnectTimeout = 5;
gkSession.available = YES;
}
- (void)teardownSession
{
gkSession.available = NO;
[gkSession disconnectFromAllPeers];
}
If you're interested in delving deeper, take a look at GKSessionP2P, a demo app that illustrates the ad-hoc networking features of GKSession. The app both advertises itself on the local network and automatically connects to available peers, establishing a peer-to-peer network.