{"id":1550,"date":"2010-01-04T10:30:40","date_gmt":"2010-01-04T09:30:40","guid":{"rendered":"http:\/\/www.devapp.it\/wordpress\/?p=1550"},"modified":"2010-01-04T11:33:35","modified_gmt":"2010-01-04T10:33:35","slug":"connettersi-ad-un-servizio-bonjour-nsnetservice","status":"publish","type":"post","link":"https:\/\/www.devapp.it\/wordpress\/connettersi-ad-un-servizio-bonjour-nsnetservice\/","title":{"rendered":"T#019 &#8211; Connettersi ad un servizio Bonjour (NSNetService)"},"content":{"rendered":"<p><img loading=\"lazy\" decoding=\"async\" class=\"alignleft\" src=\"http:\/\/images.apple.com\/support\/_images\/hero_bonjour.jpg\" alt=\"\" width=\"150\" height=\"150\" \/>In questo articolo voglio proporvi una piccola lezione sulla connessione fra due dispositivi attraverso il servizio bonjour.<br \/>\nPer semplificare questa pratica voglio proporvi una classe creata da me con la quale potrete facilmente connettervi ad un servizio (NSNetService) risolto.<\/p>\n<p>Per connettere o comunque far comunicare due dispositivi occorre creare un socket per ogni<br \/>\ndispositivo il quale ha il compito di aprire una porta dalla quale potr\u00e0 comunicare.<!--more--><\/p>\n<p>I due dispositivi comunicanti si distingueranno in &#8220;server&#8221; e &#8220;client&#8221; e le funzioni del socket differiranno in qualche parte.<\/p>\n<p>In qualunque caso la procedura \u00e8 molto semplice:<\/p>\n<p>SERVER (TCP)<\/p>\n<ul>\n<li> creare un socket;<\/li>\n<li> eseguire il bind (assegnazione dell&#8217;indirizzo e apertura della porta);<\/li>\n<li> mettere il socket in attesa di una connessione;<\/li>\n<li> accettare la connessione;<\/li>\n<li> mettere in ascolto il socket per eventuali dati in ingresso.<\/li>\n<\/ul>\n<p>CLIENT (TCP) (il client per connettersi al server deve conoscere l&#8217;indirizzo di quest&#8217;ultimo, bonjour e NSNetService servono a facilitare la scoperta dell&#8217;indirizzo)<\/p>\n<ul>\n<li> creare il socket (l&#8217;indirizzo e la porta verranno assegnati automaticamente appena il socket verr\u00e0 connesso);<\/li>\n<li> connettere il socket all&#8217;indirizzo del &#8220;server&#8221;(ottenuto con bonjour);<\/li>\n<li> mettere in ascolto il socket per eventuali dati in ingresso.<\/li>\n<\/ul>\n<p>Con l&#8217;object-c tutto \u00e8 reso molto pi\u00f9 semplice e certi passaggi vengono &#8220;saltati&#8221;. Comunque la classe da me creata fa le seguenti cose:<\/p>\n<ul>\n<li> prende un oggetto NSNetService gi\u00e0 risolto;<\/li>\n<li> crea un socket e lo connette al servizio;<\/li>\n<li> ottiene dal socket connesso gli oggetti NSInputStream e NSOutputStream che ci faciliteranno la trasmissione dati;<\/li>\n<li> invia i dati;<\/li>\n<li> passa al delegato i dati che riceve.<\/li>\n<\/ul>\n<p>Vi riporto di seguito l&#8217;header della mia classe<\/p>\n<pre lang=\"objc\" escaped=\"true\">\/\/\r\n\/\/  SocketStreamClient.h\r\n\/\/  FullControl\r\n\/\/\r\n\/\/  Created by Francesco Burelli on 12\/12\/09.\r\n\/\/  Copyright 2009 Francesco Burelli. All rights reserved.\r\n\/\/\r\n\r\n#import\r\n#import \r\n\r\n@class SocketStreamClient;\r\n\r\n@protocol SocketStreamClientDelegate\r\n@optional\r\n-(void)willConnectWithService:(NSNetService *)netService;\r\n-(void)didConnectWithService:(NSNetService *)netService;\r\n-(void)didNotConnectWithService:(NSNetService *)netService; \/\/ when disconnected\r\n-(void)didReceiveData:(NSData *)theData from:(NSNetService *)netService;\r\n-(void)didReceiveError:(NSError *)streamError;\r\n\r\n@end\r\n\r\n@interface SocketStreamClient : NSObject {\r\n\tid _delegate;\r\n\tNSNetService *_service;\r\n\tCFSocketRef socket;\r\n\r\n\tNSInputStream *_iStream;\r\n\tNSOutputStream *_oStream;\r\n\r\n\tBOOL input;\r\n\tBOOL output;\r\n\tBOOL _connected;\r\n\r\n\tNSMutableData *dataToSend;\r\n\r\n}\r\n\r\n@property(assign) id delegate;\r\n@property(readonly) NSNetService *service;\r\n@property(assign) NSInputStream *iStream;\r\n@property(assign) NSOutputStream *oStream;\r\n@property(readonly) BOOL connected;\r\n\r\n-(id)init;\r\n-(void)connectWithService:(NSNetService *)netService; \/\/ service must be just resolved !\r\n-(void)disconnect;\r\n-(NSInteger)sendData:(NSData *)theData;\r\n\r\n@end<\/pre>\n<p>Ora vi commento in ordine i vari metodi: partiamo dai metodi per il delegato.<\/p>\n<pre lang=\"objc\" escaped=\"true\">-(void)willConnectWithService:(NSNetService *)netService;\r\n-(void)didConnectWithService:(NSNetService *)netService;\r\n-(void)didNotConnectWithService:(NSNetService *)netService;<\/pre>\n<p>Informano che ci si sta connettendo al servizio indicato; connessione riuscita al servizio indicato; disconnesso dal servizio indicato.<\/p>\n<pre lang=\"objc\" escaped=\"true\">-(void)didReceiveData:(NSData *)theData from:(NSNetService *)netService;<\/pre>\n<p>Ricevuti dei dati dal servizio indicato.<\/p>\n<pre lang=\"objc\" escaped=\"true\">-(void)didReceiveError:(NSError *)streamError;<\/pre>\n<p>Questo metodo informa il delegato di eventuali errori. Non ho curato particolarmente questa parte: fondamentalmente ho creato due tipo di errori, quelli che riguardano il socket e quelli che riguardano il servizio bonjour. Lascio a voi il perfezionamento degli errori.<\/p>\n<p>Passiamo ora ai metodi della nostra classe.<\/p>\n<pre lang=\"objc\" escaped=\"true\">-(void)connectWithService:(NSNetService *)netService;<\/pre>\n<p>E&#8217; il metodo principale, connette il nostro oggetto ad un servizio, attenzione: il servizio deve essere gi\u00e0 stato risolto con il suo metodo &#8220;resolve&#8221; (vedi documentazione Apple).<br \/>\nQuesto metodo crea un socket, lo connette al servizio e ne ottiene gli oggetti NSInputStream e NSOutputStream, molto utili per il flusso dati.<\/p>\n<pre lang=\"objc\" escaped=\"true\">-(void)disconnect;<\/pre>\n<p>Bh\u00e9 non penso serva commentare questo metodo, semplicemente rilascia gli streams, chiude il socket e informa il delegato.<\/p>\n<p>Invece a quest&#8217;ultimo metodo vorrei dedicarci un po&#8217; di attenzione<\/p>\n<pre lang=\"objc\" escaped=\"true\">-(NSInteger)sendData:(NSData *)theData;<\/pre>\n<p>Testando questa classe ho notato che non \u00e8 sempre possibile inviare dati, soprattutto se di grandi dimensioni..<br \/>\nLa soluzione che ho trovato \u00e8 la seguente:<\/p>\n<p><em>Se non \u00e8 possibile inviare i dati, salvali e appena \u00e8 possibile inviare nuovi dati, inviali.<\/em><\/p>\n<p>Sembra banale, ma c&#8217;ho messo un po&#8217; a scriverlo in codice, ed ecco a voi<\/p>\n<pre lang=\"objc\" escaped=\"true\">-(NSInteger)sendData:(NSData *)theData {\r\n\tNSInteger bytesWrite;\r\n\r\n\tif ([_oStream hasSpaceAvailable]) {\r\n\t\tbytesWrite = [_oStream write:[theData bytes] maxLength:[theData length]];\r\n\t\tif (bytesWrite != [theData length]) {\r\n\t\t\t\/\/ if can't send all, store part of data and send when _oStream hasSpaceAvailable\r\n\t\t\tNSRange range = { bytesWrite, [theData length] - bytesWrite};\r\n\t\t\t[dataToSend appendData:[theData subdataWithRange:range]];\r\n\t\t}\r\n\t}\r\n\telse {\r\n\t\t\/\/ if can't send, store data and send when _oStream hasSpaceAvailable\r\n\t\t[dataToSend appendData:theData];\r\n\t}\r\n\treturn bytesWrite;\r\n}<\/pre>\n<pre lang=\"objc\" escaped=\"true\">case NSStreamEventHasSpaceAvailable: {\r\n\t\t\t\/\/ if any, send data remaining\r\n\t\t\tif (dataToSend &amp;&amp; [dataToSend length]) {\r\n\t\t\t\tNSInteger bytesWrite;\r\n\t\t\t\tbytesWrite = [_oStream write:[dataToSend bytes] maxLength:[dataToSend length]];\r\n\r\n\t\t\t\tif (bytesWrite == [dataToSend length]) {\r\n\t\t\t\t\t[dataToSend initWithLength:0];\r\n\t\t\t\t}\r\n\t\t\t\telse {\r\n\t\t\t\t\tNSRange range = { bytesWrite, [dataToSend length] - bytesWrite};\r\n\t\t\t\t\t[dataToSend initWithData:[dataToSend subdataWithRange:range]];\r\n\t\t\t\t}\r\n\r\n\t\t\t}\r\n\r\n\t\t\tbreak;<\/pre>\n<p>Le cose fondamentali penso di avervele dette, non resta che lasciarvi il link alla classe <a href=\"http:\/\/www.devapp.it\/wordpress\/wp-content\/uploads\/2010\/01\/SocketStreamClient.zip\">SocketStreamClient<\/a> e un esempio di utilizzo.<\/p>\n<pre lang=\"objc\" escaped=\"true\">\/\/ ...\r\n #import \"SocketStreamClient.h\"\r\n\r\n SocketStreamClient *_socketStreamClient = [[SocketStreamClient alloc] init];\r\n [_socketStreamClient setDelegate:self];\r\n\r\n [_socketStreamClient connectWithService:aService];\r\n\r\n \/\/ ...<\/pre>\n<p>Ricordo che la classe pu\u00f2 essere usata sia su applicazioni Mac che iPhone\/iPodTouch basta modificare<br \/>\n#import &lt;Foundation\/Foundation.h&gt; \/\/ per iphone<br \/>\n#import &lt;Cocoa\/Cocoa.h&gt; \/\/ per mac<\/p>\n<p>Ciao!<\/p>\n","protected":false},"excerpt":{"rendered":"<p>In questo articolo voglio proporvi una piccola lezione sulla connessione fra due dispositivi attraverso il servizio bonjour&#8230;.<\/p>\n","protected":false},"author":17,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[81,78,83,82,79,5,4,80],"class_list":["post-1550","post","type-post","status-publish","format-standard","hentry","category-tutorial-pratici","tag-bonjour","tag-classe","tag-connessione","tag-nsnetservice","tag-nsstream","tag-objective-c","tag-programmazione","tag-socket"],"acf":[],"aioseo_notices":[],"_links":{"self":[{"href":"https:\/\/www.devapp.it\/wordpress\/wp-json\/wp\/v2\/posts\/1550","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.devapp.it\/wordpress\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.devapp.it\/wordpress\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.devapp.it\/wordpress\/wp-json\/wp\/v2\/users\/17"}],"replies":[{"embeddable":true,"href":"https:\/\/www.devapp.it\/wordpress\/wp-json\/wp\/v2\/comments?post=1550"}],"version-history":[{"count":15,"href":"https:\/\/www.devapp.it\/wordpress\/wp-json\/wp\/v2\/posts\/1550\/revisions"}],"predecessor-version":[{"id":1566,"href":"https:\/\/www.devapp.it\/wordpress\/wp-json\/wp\/v2\/posts\/1550\/revisions\/1566"}],"wp:attachment":[{"href":"https:\/\/www.devapp.it\/wordpress\/wp-json\/wp\/v2\/media?parent=1550"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.devapp.it\/wordpress\/wp-json\/wp\/v2\/categories?post=1550"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.devapp.it\/wordpress\/wp-json\/wp\/v2\/tags?post=1550"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}