Friday, February 19, 2010

Bonjour

Group 4: iOrder for restaurants. After evaluating the project risk in class, we came up with the following potential risk: Server Client: Can we use the iPod touch to connect to a server? how hard is it? UI Feasibility: Can we create a user interface that is fast enough to place orders? The biggest risk for our group project is that we need to have a server and client. After doing our risk evaluation in class we started to work on trying to tackle this issue. Prof of Concept: I worked on trying to get the iPod touch to communicate with a server. I took some server client code that I used from a previous school project and tried to port it over to the Ipod. This was not as difficult as I expected. The iPhone SDK natively supports c, c++, objective c/c++. The client code I had was written in c++. I was able to copy it into the project in Xcode and have it compile without any modifications. Using the code from objective-c was more difficult. I had to rename the extension of the file that was using the class from *.m to *.mm. After this I could call the c++ code without any problems. I was actually impressed at how objective-c++ can keep track of both objective-c and c++. I had overloaded operators in my clientSocket class and they worked just fine. I was able to connect both the iPod touch and a linux server and send unencrypted data back and forward. I saw a potential problem setting up the server address in the client though. We really don't want people to have to enter the ip address of the server and port number in order to connect to the server. If for some reason the server goes down and comes back with a different address, having to go trough and change many iPod touches to reflect these changes would be a hassle. This got me thinking: iTunes automatically shares music libraries across a network. It does some magic and automatically the libraries of anyone on a network are shared with everyone else. Can our group do this? I looked around the Internet and found out how this magic is done. Apple uses a zeroconf protocol called bonjour. Bonjour locates devices such as printers, other computers, and the services that those devices offer on a local network using multicast Domain Name System service records (source). I was able to broadcast my server on a local network using avahi. Avahi is an open source implementation of bonjour (source). I was able to do it with the following steps in ubuntu: - run my server using port 5555. - create file in /etc/avahi/services/iorder.service with following contents
<?xml version="1.0" standalone='no'?>
<service-group>
  <name>iOrder Server</name>
  <service>
    <type>_http._tcp</type>
    <port>5555</port>
  </service>
</service-group>
avahi-deamon will now take care of broadcasting the service. To browse for services using bonjour apple provides the class NSNetServiceBrowser. I had a hard time getting this to work. I am not familiar with objective-c so writing code was very slow. The biggest problem I had was that after discovering a service(by name) it needs to be resolved to get the ip address and port number. When a service is found, the method
(void)netServiceBrowser: ...didFindService ( NSNetService* ) service moreComing: ( BOOL )more
is called and a request needs to be done to resolve the address:
[service resolveWithTimeout:5.0]
but for some reason it can't be called on the service that is being passed by the didFindService method. It needs to be a new service. It took a very long time to figure this out. I spent hours looking at my code and examples trying to figure out why it was not working. I was able to get networking/bonjour working on the prof of concept app. It automagically finds the server and connects to it. It does take a few min to resolve the address and port but these can probably be saved until they are no longer valid. In conclusion, having a server client is not a huge risk, although we will need to be careful moving forward.

4 comments:

José Augusto Costa Martins Jr. said...

Thanks for explanation, but just to be clear all you need to do is to create another nsNetService object with initWithDomain:type:name: then set his delegate (generally as self), and call resolveWithTimeout.
At this point you had a valid hostName and port?????

Can you do this inside the didFindService delegate method?

nonameentername said...

Jose,
You need to create a new NSNetService, set the delegate and then resolveWithTimeout. You can do this inside the didFindService method.

You will also need to implement the method
- (void)netServiceDidResolveAddress:(NSNetService *)service

If the address is resolved, this method gets called and NSNetService service will contain the ip address and port information.

# said...

I want to price this page within my blog. It might? And you et a forex account for Twitter?

Michael Hansip said...

Great post with really helpful info.