How to resolve an address with MonoTouch.Foundation.NSNetService? - mono

I am currently developing an iPad application. I want this to communicate with a Windows c# application using WCF, and discovery over BonJour.
I have already made the windows/c# part, using Mono.ZeroConf library, and tested that it works as supposed on Windows.
Now when I try to get my MonoTouch application discover the Windows application, all goes fine until I need to resolve the address. I simply can't figure out what is wrong.
I have the following code in my iPad app.
NSNetServiceBrowser browser = new NSNetServiceBrowser();
browser.FoundService += delegate(object sender, NSNetServiceEventArgs e){
e.Service.AddressResolved += delegate(object service, EventArgs e1){
//No matter how I treat the Bytes here it resolves to me as 8.135.207.208
//When it was supposed to resolve to some 172.x.x.x ip.
long address = ((NSNetService)service).Addresses[0].Bytes.ToInt64();
};
e.Service.Resolve();
};
browser.SearchForServices("_myService._tcp", "local");
So my question is, how am I supposed to treat the NSData objects in the Addresses array to resolve them correct.
I have tried searching, but can't seem to figure out.
All the other properties of the resolved service is correct, like name, port, etc.
I hope some of you have been here, and can provide me an answer.
Best regards
/Anders

The data in the Addresses you receive is NSData in an NSArray. The description for the delegate itself says it is a collection of sockaddr objects (plain C struct). So definitely not a Int64 as is, but a pointer to sockaddr object.
The way it is done in MT is a bit different then. You received the delegate to the NSNetService, and now you have to wait for the service to resolve the address, this is done by another resolving.
I usually store the NSNetService returned by the NSNetServiceBrowser delegate and then wait until the address is resolved, once this is done, it is then when I use the service as it is already initialized and I do not care about the address itself.

Related

ESP-IDF wifi event loop keeps receiving SYSTEM_EVENT_STA_WPS_ER_PIN even after code rollback

I have been working on a project using the ESP32 with the ESP-IDF that will check it's NVS memory for wifi credentials before starting the network stack. If it has said credentials, it will connect to the wifi network in STA mode, if it lacks them, it will launch as it's own AP to allow the user to send it the credentials over HTTP.
After manually putting my test credentials into NVS, I started working on the AP code. Once all the AP code and logic was complete, I manually wiped the flash memory with esptool to force the board to launch in that mode. Doing so worked fine, and I was able to send it the updated credentials over HTTP.
At this point, the board attempted to connect as STA upon reset, however, the SYSTEM_EVENT_STA_WPS_ER_PIN event kept being caught by the wifi event loop. The board has since only experienced this event and has been completely unable to connect to wifi since. To make matters stranger, even after rolling back to a previous version with git, the problem still persists.
main.c
void app_main() {
// Start NVS
initNVS();
// Init Wifi Controller
initWifiController();
// Get Credentials to send to wifi
Creds creds = getCreds();
// Start wifi in STA mode with gathered creds
beginWifi(creds);
initializePins();
initializeTimers();
}
wifiController.c
void initWifiController(){
// * NVS must be initialized before wifi work can be done
// Handle when connected to the network
connectionSemaphore = xSemaphoreCreateBinary();
// Begin network stack
ESP_ERROR_CHECK(esp_netif_init());
// Create event loop for handling callbacks
ESP_ERROR_CHECK(esp_event_loop_create_default());
}
void beginWifi(Creds creds){
if(creds.status == ESP_OK){
ESP_LOGI(TAG, "Connection credentials have been found, connecting to network");
connectSTA(creds);
}
else if(creds.status == ESP_ERR_NVS_NOT_FOUND){
ESP_LOGW(TAG, "Missing credentials, starting as AP");
connectAP();
}
else{
ESP_LOGE(TAG, "ESP failed with error %s, not starting wifi", esp_err_to_name(creds.status));
}
void connectSTA(Creds creds){
ESP_LOGI(TAG, "Attempting to connect to wifi with following creds: %s | %s", creds.ssid, creds.pass);
// Set netif to sta
esp_netif_create_default_wifi_sta();
// Prepare and initialize wifi_init_config_t
wifi_init_config_t wifi_init_config = WIFI_INIT_CONFIG_DEFAULT();
ESP_ERROR_CHECK(esp_wifi_init(&wifi_init_config));
// Register tracked events for event_handler
ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID, event_handler, NULL));
ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, event_handler, NULL));
// TODO: Check if this can be used to avoid havng to use NVS manually
esp_wifi_set_storage(WIFI_STORAGE_RAM);
// Config struct for wifi details
wifi_config_t wifi_config = {};
// Copy casted info into wifi_config
// * https://www.esp32.com/viewtopic.php?f=13&t=14611
// * See above link for details on this
strcpy((char *)wifi_config.sta.ssid, creds.ssid);
strcpy((char *)wifi_config.sta.password, creds.pass);
esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_config);
ESP_ERROR_CHECK(esp_wifi_start());
// ? Is this required to avoid a memory leak?
free(creds.pass);
free(creds.ssid);
}
void connectAP(){
// ? How important is it that these be called in this order?
ESP_LOGI(TAG, "Starting in AP Mode");
esp_netif_create_default_wifi_ap();
// TODO: maybe move this creation to initWifiController to avoid making it twice
wifi_init_config_t wifi_init_config = WIFI_INIT_CONFIG_DEFAULT();
ESP_ERROR_CHECK(esp_wifi_init(&wifi_init_config));
// TODO: Consider moving this to init Wifi Controller to avoid running it twice as well
ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &event_handler, NULL));
// Configuration for AP
wifi_config_t wifi_config = {
.ap = {
.ssid = "Grow System",
.password = "Password",
.ssid_len = strlen("Grow System"),
.max_connection = 4,
.authmode = WIFI_AUTH_WPA_WPA2_PSK
}
};
// TODO: Enable password support on AP configuration
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_AP));
ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_AP, &wifi_config));
ESP_ERROR_CHECK(esp_wifi_start());
ESP_LOGI(TAG, "Wifi connectAP finished");
registerEndPoints();
}
The code essentially checks NVS for the credentials, if it finds them both it returns a struct containing both of them as well as ESP_OK. If one of them was not found, the struct instead contains ESP_ERR_NVS_NOT_FOUND. wifiController.c then receives this creds struct and calls beginWifi() which then either calls connectSTA() or connectAP() based on the status of the struct. When the credentials are present, connectSTA() is called, but for unknown reasons the event loop consistently only receives the SYSTEM_EVENT_STA_WPS_ER_PIN event. As I mentioned earlier, even after rolling my code back to a version that did not have the connectAP() function, this behavior persists.
Because of this, I have a hunch that the issue may be related to when I wiped the flash manually, as opposed to the code.
The header file contains the following line in it's typedef to define this event.
SYSTEM_EVENT_STA_WPS_ER_PIN, /*!< ESP32 station wps pin code in enrollee mode */
I do not know what that means, as I have not intentionally included anything regarding wps in this project.
So far my research hasn't returned anything incredibly useful, so if anyone has any ideas what SYSTEM_EVENT_STA_WPS_ER_PIN is or what could be causing my issue, I would be very appreciative. If you think that anymore detail would be useful to solve this issue, please let me know and I will be more than happy to provide it.
Useful to solve the problem.
I'm in the proces of learning to use the ESP32 wifi, read your message checked, the ESP-idf and esp-32 technical manual not much info there I found this URI
https://www.wi-fi.org/downloads-public/Wi-Fi_Protected_Setup_Best_Practices_v2.0.2.pdf/8188
Kind regards
Update: check the sdkconfig file in your projectmap against the one in the example map for wifi config settings.
I have been digging into this issue for several days now and I have not found the root cause but have managed to discovery a workaround and some details about the issue. the SYSTEM_EVENT_STA_WPS_ER_PIN event that kept arising actually is caused by the chip trying to use WPS Enrolle Pin mode to connect to the router. Supposedly this generates an eight digit long pin that can be put into your router which should allow it to connect. I really do not know why it was doing this, but I believe it may have had something to do with how I had AP mode set up. My primary confusion now is why rolling back the code did not fix it.
For the "workaround" I found that flashing Espressif's example STA wifi connection code managed to solve it. The chip properly connected to my network once it was flashed, and I was able to reflash my own code without any issues arriving. This seems extremely strange to me, so part of me thinks that maybe the chip just couldn't connect to my network for some reason and an edge case in the code caused it to go into enrollee mode.
Here is a link to the code that I used to "fix" the issue: https://github.com/espressif/esp-idf/blob/master/examples/wifi/getting_started/station/main/station_example_main.c
I will mark this answer as correct and continue looking for the root problem. In the event that someone else know what actually could have caused this, feel free to post and I will update the correct answer. In the event that I find what the root problem is, I will update this answer as well.
EDIT: After continuing to dig, I believe that the problem was actually do to a multitude of errors in my code. Particularly, I was calling esp_netif_create_default_wifi_sta() and then not setting the WI-FI mode. I needed to add esp_wifi_set_mode(WIFI_MODE_STA), which was absent in my program. I believe setting the network stack to sta without changing the wifi mode was what caused my issue.

Using AFIncrementalStore with a WebSockets Application

I'm attempting to adopt AFIncrementalStore for a Mac app that talks to App.Net. Unlike the example applications that come with the framework, I'm using the streaming APIs, with a websocket connection. For this I was using SocketRocket. These parts are working fine: I'm able to set up a request connection to ADN and get a connection ID back. It's this connection ID I supply to the later requests to ADN APIs.
My problem is that the Core Data stack is initialized and firing before I get my first connection ID back from ADN. I'm not sure how to handle this situation.
Currently, I have this code in my app delegate:
self.socketConnection = [[MUNConnectionManager alloc] init];
self.socketConnection.delegate = self;
My connection manager implements a delegate that calls back to the app delegate when the connection ID has been received:
# pragma mark MUNConnectionManager delegate method
- (void)didReceiveConnectionId:(NSString*)connectionId
{
self.connectionId = connectionId;
}
So once this connection ID is received, that's when I'd like to boot AFIncrementalStore into action. But this is perhaps a full second or so after launch, and my AFIncrementalStore client is already crapping out because it doesn't have that connection ID.
Any suggestions appreciated!
I think I may have found the answer to this. In my XIB I have an array controller with the "prepares content" checkbox on. That would have triggered the data store and loaded up all the Core Data stack. When I uncheck that box it doesn't load, and my ADN delegate is free to pull the ID.
So if anyone else runs into this, the answer is the CD stack doesn't load until you try to hit it.

NSNetServiceBrowser did NOT find published service

On an iPhone (the server), I've tried to publish a service and my code ran into the NSNetService object's delegate method:
-(void)netServiceDidPublish:(NSNetService *)sender
So I believe that my service #"_chatty._tcp." has published successfully. Then on another iPhone (the client), I use NSNetServiceBrowser to find my service, but it did NOT run into the delegate method:
-(void)netServiceBrowser:(NSNetServiceBrowser *)netServiceBrowser didFindService:(NSNetService *)netService moreComing:(BOOL)moreServicesComing
I found some questions related to my case on this site, most of the answer remind to check the delegate object whether is out of scope or not. I'm sure my delegate work well because it ran into another delegate method like:
-(void)netServiceBrowserWillSearch:(NSNetServiceBrowser *)aNetServiceBrowser
Can anybody help me find out the reason?
Here are some parts of my code:
I init the service like that:
#define MY_PROTOCOL #"_chatty._tcp."
self.myService = [[NSNetService alloc]
initWithDomain:#"" type:MY_PROTOCOL
name:#"thaith" port:self.port];
The port is initialized with a given listeningSocket in the Browser class:
NSNetServiceBrowser* finder = [[NSNetServiceBrowser alloc] init];
//I also retain the finder.
finder.delegate = self;
[finder searchForServicesOfType:MY_PROTOCOL inDomain:#""];
After having come across the same problem and giving up for a month. I've just come back to it and solved it:
Even though the sample code in the docs seems to imply otherwise, don't use a local variable for the NSNetServiceBrowser. As soon as it goes out of scope it gets garbage collected. Make finder an instance variable or property so its sticks around. I didn't spot this straight away as the netServiceBrowserWillSearch: delegate was getting called so I assumed everything was ok...
Possible Solutions
Check both WiFi identifiers are same
Check both are in same WiFi network
Check the NSNetServiceBrowser delegate assigned as same class
At last download sample Apple.Developer Witap Application , install in two devices , test and confirm it working.
Instead of downloading bonjour browser, I suggest using the terminal command:
dns-sd -B _chatty._tcp local.
For me, it shows that the server side is working fine.
Currently, I can find the service when my application starts, my only issue is that once I stop the server, I get the "removed" event but running it again, I cant discover it anymore. I know the problem is on my client side, thanks to dns-sd - B
I would narrow the scope and try to find the problem place. First, find out whether the service is published correctly. Use Bonjour Browser application (you can find it in the Internet) on a computer within the same local network where you publish the service. I hope you publish and browse in the same local net. If the Bonjour Browser can see your service then you know it is published correctly. Then work on the browser side to connect to it.

How to fix naive server implementation with CFSocket to allow multiple connections

I have been studying the bonjour/NSStream sample code from lecture #17 of Stanford's CS193p course (iOS programming) on iTunes U from the winter of 2010. The example code is available here.
In a nut shell, the sample code creates a socket and binds to port 0 so that it will be given a free port. It then publishes a service with that port using NSNetService (bonjour). An NSNetServiceBrowser is also started when the app starts up. Available services are placed in a UITableView. When a cell is selected, the corresponding service is resolved, an NSOutputStream is created, and data can be sent.
This is a naive implementation because connections are rejected if a connection already exists. My question is, what is the proper way to handle multiple connections? Once multiple clients are connected to the server, how does the server distinguish between them? i.e. How can data be sent specifically to one client and not the others?
- (void) _acceptConnection:(int)fd
{
int junk;
// If we already have a connection, reject this new one. This is one of the
// big simplifying assumptions in this code. A real server should handle
// multiple simultaneous connections.
if ( self.isReceiving ) {
junk = close(fd);
assert(junk == 0);
} else {
[self _startReceive:fd];
}
}
// Called by CFSocket when someone connects to our listening socket.
// This implementation just bounces the request up to Objective-C.
static void AcceptCallback(CFSocketRef s,
CFSocketCallBackType type,
CFDataRef address,
const void *data,
void *info)
{
ReceiveServer * obj;
assert(type == kCFSocketAcceptCallBack);
assert(data != NULL);
obj = (ReceiveServer *) info;
assert(obj != nil);
assert(s == obj->_listeningSocket);
[obj _acceptConnection:*(int *)data];
}
I'm not specifically familiar with that course or sample code, but: separate out the code for handling a connection into a different class from the code which accepts new connections. So, in the posted code, you'd move the -startReceive: method and everything which it calls to another class.
Then, each time a connection is accepted, create an instance of that other class. That instance will be responsible for processing all of the communication on that connection. It will be given the information about the connection (mainly the fd) during initialization. The main controller object of the server could hold those instances in an array.
Where things go from here will depend on what your server actually does. At the very least, your connection objects will need to inform the main controller object when they are closed, so the main controller can remove them from the array. You can use notifications or the delegate pattern for that.

Help with sending text thou TCP/IP or LAN

Hey all i am trying to send a text message from a virtual machine (VMWARE) to my local machine so that i can hit a button in the VM and have it do something on the local.
Is there anyway to send a text through an IP, TCP/IP, LAN using VB6 or VB.net? I was looking at the net send to send something but it doesn't seem to work for me (as well as it seems to pop up a dialog box for every text you send). I've already tried this out:
http://www.codeproject.com/KB/vb/CfSimpleSendComp.aspx
But it doesn't seem to work at all on my computer? I've tried both the IP of the machine and also the computer's name. Maybe .NET Remoting in VB?
Could anyone let me know if there are other ways to do what i would like to do?
Thanks!
David
You also can consider to use Eneter Messaging Framework.
It is lightweight and very easy to use.
I am sorry, I am not familiar with VB syntax, but in C# the whole implementation
is here: (You can copy paste the code into your project, include Eneter.Messaging.Framework.dll and change the IP to yours.)
The server listening to string messages.
using System;
using Eneter.Messaging.EndPoints.StringMessages;
using Eneter.Messaging.MessagingSystems.MessagingSystemBase;
using Eneter.Messaging.MessagingSystems.TcpMessagingSystem;
namespace StringReceiver
{
class Program
{
static void Main(string[] args)
{
// Create Tcp based messaging.
IMessagingSystemFactory aTcpMessaging = new TcpMessagingSystemFactory();
IInputChannel anInputChannel = aTcpMessaging.CreateInputChannel("127.0.0.1:7091");
// Create string message receiver
// Note: it is possible to receiver typed messages too.
IStringMessagesFactory aStringMessagesFactory = new StringMessagesFactory();
IStringMessageReceiver aStringMessageReceiver = aStringMessagesFactory.CreateStringMessageReceiver();
aStringMessageReceiver.MessageReceived += StringMessageReceived;
// Attach the input channel to the string message receiver
// and start listening.
Console.WriteLine("String sercer is listening.");
aStringMessageReceiver.AttachInputChannel(anInputChannel);
}
// Processing messages.
static void StringMessageReceived(object sender, StringMessageEventArgs e)
{
Console.WriteLine("Received message: " + e.Message);
}
}
}
The client sending the string messages:
using Eneter.Messaging.EndPoints.StringMessages;
using Eneter.Messaging.MessagingSystems.MessagingSystemBase;
using Eneter.Messaging.MessagingSystems.TcpMessagingSystem;
namespace StringMessageSender
{
class Program
{
static void Main(string[] args)
{
// Create Tcp based messaging.
IMessagingSystemFactory aTcpMessaging = new TcpMessagingSystemFactory();
IOutputChannel anOutputChannel = aTcpMessaging.CreateOutputChannel("127.0.0.1:7091");
// Create string message receiver
// Note: it is possible to receiver typed messages too.
IStringMessagesFactory aStringMessagesFactory = new StringMessagesFactory();
IStringMessageSender aStringMessageSender = aStringMessagesFactory.CreateStringMessageSender();
// Attach the output channel to the string message sender
// so that we can send messages via Tcp to desired Ip address.
aStringMessageSender.AttachOutputChannel(anOutputChannel);
// Send message.
aStringMessageSender.SendMessage("Hello world.");
}
}
}
Eneter Messaging Framework can be downloaded at www.eneter.net.
If you would like to get more technical info: www.eneter.net/OnlineHelp/EneterMessagingFramework/Index.html
More examples: eneter.blogspot.com
You need a client and a server, one of them at each end. These could communicate over TCPIP (TCP or UDP), Microsoft Networking (named Pipes, Mailslots), or whatever you have. Some options depend on what OS the two machines are running, but since you mentioned NET SEND we can probably assume some flavor of Windows.
The Messenger Service is gone in later versions of Windows NT (Vista, Windows 7) so it's not the best option. There are other Mailslots messengers though as well as lots of UDP messengers.
The real question is what action you want to perform "at the push of a button." Heck, you could easily run Telnet if that gave you what you were after.
Don't forget you may have to open firewalls for any of these to work though.
.Net Remoting isn't available in VB, you must mean VB.Net. That's a remote object invokation technology though and probably not what you want.
You'd probably get to a result more quickly picking one development tool set and using whatever it offers for TCP or UDP sockets.
There are several ways. The best way depends on how you define 'text message'.
If you literally just need to hit a button and run a command on a remote machine, I would run PsExec in Process object. Cheesy but effective. Something like:
Using p as new Process()
p.StartInfo.FileName = "c:\path\to\PsExec.exe"
p.StartInfo.Arguments = "\\RemoteComputerName RemoteCommand.exe"
p.Start()
End Using
If you need bi-directional communication with a custom protocol, I would use WCF or the TCPListener and TCPClient classes to create your own socket server and client.
I would stay away from remoting.