iOS-----使用CFNetwork實現TCP協議的通信


使用CFNetwork實現TCP協議的通信   

TCP/IP通信協議是一種可靠的網絡協議,它在通信的兩端各建立一個通信接口,從而在通信的兩端之間形成網絡虛擬鏈路.一旦建立了虛擬的網絡鏈路,兩端的程序就可以通過虛擬鏈路進行通信.CFNetwork對基於TCP協議的網絡通信提供了良好的封裝,CFNetwork使用CFSocket來代表兩端的通信接口,還可以通過CFStream讀/寫數據.

IP地址與端口號

IP地址用於唯一地標識網絡中的一個通信實體,這個通信實體既可以是一台主機,也可以是一台打印機,或者是路由器的某一個端口.在基於IP協議的網絡中傳輸的數據包,都必須使用IP地址來進行標識.

   IP地址是數字型的,是一個32位(32Bit)整數,通常把它分成4個8位的二進制數,每8為之間用圓點隔開,每個8位整數可以轉換成一個0-255的十進制整數,因此看到的IP地址常常是這樣的形式:198.162.8.10.

   NIC(Internet Network Information Center)統一負責全球Internet IP地址的規划、管理,而Inter NIC、APNIC、RIPE三大網絡信息中心具體負責美國及其他地區的IP地址分配。其中APNIC負責亞太地區的IP管理,我國申請IP地址也要通過APNIC,APNIC的總部設在日本東京大學。

  IP地址被分成了A、B、C、D、E五類,每個類別的網絡標識和主機標識各有規則。

  1. A類:10.0.0.0~10.255.255.255
  2. B類:172.16.0.0~172.31.255.255
  3. C類:192.168.0.0~192.168.255.255

IP地址用於唯一地標識網絡上的一個通信實體,但一個通信實體可以有多個通信程序同時提供網絡服務,此時還需要使用端口.

端口是一個16位的整數,用於標識數據交給那個通信程序處理.因此,端口就是應用程序與外界交流的出入口,它是一種抽象的軟件結構,包括一些數據結構和I/O(基本輸入/輸出)緩沖區.

不同的應用程序處理不同端口上的數據,同一台機器上不能有兩個程序使用同一個端口,端口號可以從0到65535,通常將它分為3類.

  1. 公認端口(Well Know Ports):從0到1023,它們緊密綁定(Binding)一些特定的服務.
  2. 注冊端口(Registered Ports):從1024到49151,它們松散地綁定一些服務。應用程序通常使用這個范圍內的端口。
  3. 動態和/或私有端口(Dynamic and/or Private Ports):從49152到65535,這些端口是應用程序使用的動態端口,應用程序一般不會主動使用這些端口。      

TCP協議基礎

IP協議是 Internet 上使用的一個關鍵協議,它是全稱是 Internet Protocol, 即Internet協議,通常簡稱 IP 協議.通過使用 IP協議,使 Internet 成為一個允許連接不同類型的計算機和不同操作系統的網絡.

要使兩台計算機之間彼此進行通信,必須使兩台計算機使用同一種”語言”,IP 協議只保證計算機能發送和接收分組數據. IP協議負責將消息從一個主機傳送到另一個主機,消息在傳送的過程中被分割成一個個小包.

安裝 IP 協議之后,可保證計算機之間發送和接收數據,但 IP協議還不能解決數據分組在傳輸過程中可能出現的問題.因此,若要解決可能出現的問題,連接上 Internet 的計算機還需要安裝 TCP 協議來提供可靠並且無差錯的通信服務.

TCP協議被稱作一種端對端協議.這是因為它為兩台計算機之間的連接起到了重要作用-----當一台計算機需要與另一台遠程計算機連接時, TCP 協議會讓它們建立一個連接:用於發送和接收數據的虛擬鏈路.

TCP 協議負責收集這些信息包,並將其按適當的次序放好傳送,在接收端收到后再將其正確地還原. TCP協議保證了數據包在傳送中准備無誤. TCP 協議使用重發機制:當一個通信實體發送一個消息給另一個通信實體后,需要收到另一個通信實體的確認信息,如果沒有收到另一個通信實體的確認信息,則會再次重發剛才發送的信息.

通過這種重發機制, TCP協議向應用程序提供可靠的通信連接,使它能夠自動適應網上的各種變化.即使在 Internet 暫時出現堵塞的情況下, TCP也能夠保證通信的可靠性.

使用 CFSocket 實現 TCP服務器端

使用CFSocket建立服務器的步驟如下.

1

創建一個監聽Socket Accept(Socket連接)的CFSocket,並為kCFSocketAcceptCallBack事件綁定回調函數.

2

調用CFSocketSetAddress()函數,將服務器端的CFSocket綁定到本地IP地址和端口

3

將CFSocket作為source添加到指定線程的CFRunLoop上,並運行該線程的CFRunLoop,從而保證該CFSocket能持續不斷地接受來自客戶端的連接.

代碼片段

  1 #import <sys/socket.h>
  2 
  3 #import <arpa/inet.h>
  4 
  5 #import<Foundation/Foundation.h>
  6 
  7 // 讀取數據的回調函數
  8 
  9 void readStream(CFReadStreamRef  iStream, CFStreamEventType eventType, void *clientCallBackInfo)
 10 
 11 {
 12 
 13   UInt 8 buff[2048];
 14 
 15   CFIndex hasRead = CFReadStreamRead(iStream, buff, 2048);
 16 
 17   if(hasRead > 0)
 18 
 19   {
 20 
 21 // 強制只處理hasRead前面的數據
 22 
 23 buff[hasRead]=’\0’;
 24 
 25 printf(“接收到數據: %s\n”, buff);
 26 
 27 }
 28 
 29 }
 30 
 31 // 有客戶端連接進來的回調函數
 32 
 33 void TCPServerAcceptCallBack(CFSocketRef  socket, CFSocketCallBackType type, CFDataRef  address, const void *data, void *info )
 34 
 35 {
 36 
 37   // 如果有客戶端Socket連接進來
 38 
 39   if(kCFSocketAcceptCallBack == type)
 40 
 41   {
 42 
 43 // 獲取本地Socket的Handle
 44 
 45 CFSocketNativeHandle  nativeSocketHandle = *(CFSocketNativeHandle*)data;
 46 
 47 uint8_t name[SOCk_MAXADDRLEN];
 48 
 49 socklen_t  nameLen = sizeof(name);
 50 
 51 // 獲取對方Socket信息,還有getsocketname()函數則用於獲取本程序所在Socket信息
 52 
 53 if(getpeername(nativeSocketHandle, (struct sockaddr *)name, &nameLen) != 0)
 54 
 55 {
 56 
 57   NSLog(@”error”);
 58 
 59   exit(1);
 60 
 61 }
 62 
 63 // 獲取連接信息
 64 
 65 struct sockeaddr_in * addr_in = (struct socketaddr_in*) name;
 66 
 67 NSLog(@”%s: %d 連接進來了”,  inet_ntoa(addr_in->sin_addr)
 68 
 69 ,  addr_in->sin_port);
 70 
 71 CFReadStreamRef  iStream;
 72 
 73 CFWriteStreamRef  oStream;
 74 
 75 // 創建一組可讀/寫的CFStream
 76 
 77 CFStreamCreatePairWithSocket(kCFAllocatorDefault , nativeSocketHandle, &iStream, &oStream);
 78 
 79 if(iStream && oStream)
 80 
 81 {
 82 
 83    // 打開輸入流和輸入流
 84 
 85    CFReadStreamOpen(iStream);
 86 
 87    CFWriteStreamOpen(oStream);
 88 
 89    CFStreamClientContext streamContext = {0, NULL, NULL, NULL};
 90 
 91    if(!CFReadStreamSetClient(iStream, kCFStreamEventHasBytesAvailable,
 92 
 93 readStream/*回調函數,當有可讀的數據時調用*/, &streamContext))
 94 
 95 {
 96 
 97 exit(1);
 98 
 99 }
100 
101 CFReadStreamScheduleWithRunLoop(iStream,  CFRunLoopGetCurrent(),
102 
103 kC FRunLoopCommonModes);
104 
105 const char* str = “您好,您收到Mac服務器的新年祝福!\n”;
106 
107 // 向客戶端輸出數據
108 
109 CFWriteStreamWrite(oStream, (UInt8 *)str, strlen(str) + 1);
110 
111 }
112 
113 }
114 
115 }
116 
117 int main(int argc, char * argv[])
118 
119 {
120 
121   @autoreleasepool{
122 
123    // 創建Socket,指定TCPServerAcceptCallBack
124 
125    // 作為kCFSocketAcceptCallBack事件的監聽函數
126 
127    CFSocketRef _socket = CFSocketCreate(kCFAllocatorDefault
128 
129 , PF_INEF // 指定協議族,如果該參數為0或者負數,則默認為PF_INEF
130 
131 // 指定Socket類型,如果協議族為PF_INEF,且該參數為0或負數
132 
133   // 則它會默認為SOCK_STREAM,如果要使用UDP協議,則該參數指定為SOCK_DGRAM
134 
135 , SOCK_STREAM
136 
137  // 指定通信協議。如果前一個參數為SOCK_STREAM,則默認使用TCP協議
138 
139  // 如果前一個參數為SOCK_DGRAM,則默認使用UDP協議
140 
141 ,IPPROTO_TCP
142 
143 // 該參數指定下一個回調函數所監聽的事件類型
144 
145   ,kCFSocketAcceptCallBack
146 
147   ,TCPServerAcceptCallBack // 回調函數
148 
149   ,NULL);
150 
151  if(_socket == NULL)
152 
153  {
154 
155    NSLog(@”創建Socket失敗!”);
156 
157    return 0;
158 
159 }
160 
161 int optval = 1;
162 
163 // 設置允許重用本地地址和端口
164 
165 setsockopt(CFSocketGetNative(_socket), SOL_SOCKET,  SO_REUSEADDR,
166 
167 (void *)&optval, sizeof(optval));
168 
169 // 定義sockaddr_in類型的變量,該變量將作為CFSocket的地址
170 
171 struct sockaddr_in addr4;
172 
173 memset(&addr4, 0 , sizeof(addr4));
174 
175 addr4.sin_len = sizeof(addr4);
176 
177 addr4.sin_family = AF_INEF;
178 
179 //  設置該服務器監聽本機任意可用的IP地址
180 
181 // addr4.sin_addr.s_addr =htonl(INADDR_ANY);
182 
183 // 設置服務器監聽地址
184 
185 addr4.sin_addr.s_addr = inet_addr(“192.168.1.100”);
186 
187 // 設置服務器監聽端口
188 
189 addr4.sin_port = htons(30000);
190 
191 // 將IPv4的地址轉換成CFDataRef
192 
193 CFDataRef address = CFDataCreate(kCFAllocatorDefault
194 
195  , (UInt8 *)&addr4, sizeof(addr4));
196 
197 //  將CFSocket綁定到指定IP地址
198 
199 if(CFSocketSetAddress(_socket, address) != kCFSocketSuccess)
200 
201 {
202 
203    NSLog(@”地址綁定失敗!”);
204 
205    // 如果_socket不為NULL,則釋放_socket
206 
207   if(_socket)
208 
209   {
210 
211     CFRelease(_socket);
212 
213     exit(1);
214 
215 }
216 
217 _socket = NULL;
218 
219 }
220 
221   NSLog(@”---啟動循環監聽客戶端連接-----”);
222 
223   // 獲取當前線程的CFRunLoop
224 
225 CFRunLoopRef  cfRunLoop = CFRunLoopGetCurrent();
226 
227 // 將_socket包裝成CFRunLoopSource
228 
229 CFRunLoopSourceRef source = CFSocketCreateRunLoopSource(
230 
231 kCFAllocatorDefault , _socket, 0);
232 
233 // 為CFRunLoop對象添加source
234 
235 CFRunLoopAddSource(cfRunLoop,  source,  kCFRunLoopCommonModes);
236 
237 CFRelease(source);
238 
239 //  運行當前線程的CFRunLoop
240 
241 CFRunLoopRun();
242 
243 }
244 
245   return 0;
246 
247 }

 

說明

   上面程序中的main()函數作為程序的入口,程序從該函數開始執行.main()函數的第1段紅色字代碼創建了一個CFSocket對象,並指定了該CFSocket使用TCP協議,基於流經行輸入/輸出.

sockaddr_in類型的結構體變量,該結構體變量將會作為CFSocket綁定的監聽地址,因此程序為socketaddr_in類型的結構體變量指定了IP地址和端口,然后程序中的紅色字代碼調用了CFSocketSetAddress()函數將指定CFSocket綁定到指定的IP地址和端口.

main()函數的最后一段紅色體代碼將該CFSocket作為source添加到主線程的CFRunLoop上,並運行主線程的CFRunLoop,從而保證該CFSocket能持續不斷地接受來自客戶端的連接.

 

  該程序的另一個重點是TCPServerAcceptCallBack回調函數---當CFSocket接受來自客戶端的連接后,該函數將會被調用.該函數主要做了如下4件事情.

1

調用getpeername()函數獲取遠程Socket的相關信息,從而在控制台打印出來連接進來的客戶端IP地址和端口.

2

調用CFStreamCreatePairWithSocket()函數通過CFSocket獲取CFReadStreamRef、CFWriterStreamRef,接下來程序即可通過這兩個流進行讀/寫網絡數據。

3

再次使用主線程的CFRunLoop監聽來自客戶端的數據。

4

向客戶端寫入一段字符串。

 

使用CFSocket實現TCP客戶端

創建TCP客戶端同樣通過CFSocket完成。使用CFSocket創建Socket客戶端的步驟如下。

1

創建一個不監聽任何事件或監聽Connection的CFSocket。如果要監聽Connection,則需要為kCFSocketConnectCallBack事件綁定回調函數.

2

調用CFSocketConnectToAddress()函數,將客戶端的CFSocket連接到指定IP地址和端口的服務器上.

3

得到客戶端CFSocket之后,既可直接使用CFSocket對應的CFSocketNativeHandle進行讀/寫,也可通過CFSocket獲取CFReadStreamRef、CFWriteStreamRef后進行讀/寫。

代碼片段

ViewController.m

#import <sys/socket.h>

#import <netinet/in.h>

#import <arpa/inet.h>

#import <ViewController.h>

 

@implementation ViewController

CFSocketRef  _socket;

BOOL isOnline;

- (void)viewDidLoad

{

   [super viewDidLoad];

   // 創建Socket,無須回調函數

   _socket = CFSocketCreate(kCFAllocatorDefault,

PF_INEF // 指定協議族,如果該參數為0或者負數,則默認為PF_INET

// 指定Socket類型,如果協議族為PF_INEF,且該參數為0或負數

// 則它會默認為SOCK_STREAM,如果要使用UDP協議,則該參數指定為SOCK_DGRAM

, SOCK_STREAM

// 指定通信協議.如果前一個參數為SOCK_STREAM,則默認使用TCP協議

// 如果前一個參數SOCK_DGRAM,則默認使用UDP協議

, IPPROTO_TCP

// 該參數指定下一個回調函數所監聽的事件類型

,  kCFSocketNoCallBack

,  nil

,  NULL);

if(_socket != nil)

{

  // 定義sockaddr_in類型的變量,該變量將作為CFSocket的地址

  struct  sockaddr_in  addr4;

  memset(&addr4, 0, sizeof(addr4));

  addr4.sin_len = sizeof(addr4);

  addr4.sin_family = AF_INET;

  // 設置連接遠程服務器的地址

  addr4.sin_addr.s_addr = inet_addr(“192.168.1.88”);

  // 設置連接遠程服務器的監聽端口

  addr4.sin_port = htons(30000);

  // 將IPv4的地址轉換為CFDataRef

  CFDateRef addres = CFDataCreate(kCFAllocatorDefault

,  (UInt8 *)&addr4, sizeof(addr4));

// 連接遠程服務器的Socket,並返回連接結果

CFSocketError  result = CFSocketConnectionToAddress(_socket

,  address // 指定遠程服務器的IP地址和端口

,  5 // 指定連接超時時長, 如果該參數為負數, 則把連接操作放在后台進行

//  當_socket消息類型為kCFSocketConnectCallBack時

//  將會在連接成功或失敗的時候在后台觸發回調函數

);

// 如果連接遠程服務器成功

if(result == kCFSocketSuccess)

{

  isOnline = YES;

// 啟動新線程來讀取服務器響應的數據

[NSThread detachNewThreadSelector:@selector(readStream)

toTarget:self withObject:nil];

}

}

}

// 讀取接收的數據

- (void)readStream

{

   char buffer[2048];

   int hasRead;

   // 與本機關聯的Socket如果已經失效,則返回-1:INVALID_SOCKET

   while ((hasRead = recv(CFSocketGetNative(_socket)

, buffer, sizeof(buffer), 0)))

{

   NSLog(@”%@”,[[NSString alloc] initWithBytes:buffer

length:hasRead encoding:NSUTF8StringEncoding]);

}

}

-  (IBAction)clicked:(id)sender

{

  if(isOnline)

  {

NSString* stringTosend = @”來自iOS客戶端的問候”;

const char* data = [stringTosend UTF8String];

send(CFSocketGetNative(_socket), data, strlen(data) +1, 1);

}

else

{

  NSLog(@”暫未連接服務器”);

}

}

@end

 

說明

   上面程序中的第1段紅色字代碼創建了一個CFSocket, 該CFSocket同樣適用了TCP協議,並且是基於SOCK_STREAM流的Socket,然后程序創建了一個struct  sockaddr_in 結構體變量,該結構體變量代表遠程服務器的地址.

   上面程序中的第2段紅色字代碼調用了CFSocketConnectToAddress()函數將CFSocket連接到遠程服務器地址---如果連城成功,就可得到一個進行網絡讀/寫的CFSocket.剩下的事情是程序以readStream作為新線程的執行體,啟動了一個新線程,其中readStream方法中的紅色字代碼調用了recv()函數從指定CFSocket讀取數據.而clicked:方法則用於向服務器發送數據,該方法中的紅色字代碼調用了send()函數向CFSocket發送數據.

使用CocoaAsyncSocket實現TCP客戶端

   CocoaAsyncSocket封裝了CFNetwork底層的CFSocket和CFStream,並提供了異步操作,從而可簡化Socket網絡編程.CocoaAsyncSocket不僅支持TCP協議的網絡編程,也支持UDP協議的網絡編程.CocoaAsyncSocket是CFSocket的絕佳替代者.

CocoaAsyncSocket主要有以下特性

1

非阻塞方式的讀和寫,而且可設置超時時長.

2

自動的Socket接收。如果調用它接受連接,它將為每個連接啟動新的實例,當然也可以立即關閉這些連接。

3

委托(delegate)支持。錯誤、連接、接收、完整的讀取、完整的寫入、進度以及斷開連接,都可通過委托模式調用。

4

所有操作都封裝在一個類中,無須操作Socket或流,該類封裝了所有操作。

下載和安裝CocoaAsyncSocket的步驟如下

1.CocoaAsyncSocket的官方網站是https://github.com/robbiehanson/CocoaAsyncSocket,登錄該站點,單擊頁面中間的release鏈接.

2.瀏覽器將會打開一個新的列表頁面,該列表頁面中列出了CocoaAsyncSocket的所有發布版本,建議下載最新版的CocoaAsyncSocket.

3.下載完成將可以得到一個CocoaAsyncSocket.zip文件,解壓該壓縮包,將可以看到如下文件結構.

        RunLoop:該目錄下包含了AsyncSocket\AsyncUDPSocket兩個類的源文件和Xcode目錄.其中AsyncSocket就是基於TCP協議的CocoaAsyncSocket實現,AsyncUDPSocket就是基於UDP協議的AsyncUDPSocket實現。而Xcode目錄下則包含了使用CocoaAsyncSocket開發服務器端與客戶端的示例項目。

        GCD:該目錄下的內容與RunLoop目錄下的內容基本相似,只是類名變成了GCDAsyncSocket、GCDAsyncUDPSocket,這是因為該目錄下的CocoaAsyncSocket是基於GCD的實現。

        Vendor:其他相關類。

        其他雜項文件。

        需要為項目增加CFNetwork.framework框架。

添加CocoaAsyncSocket支持之后,使用AsyncSocket開發TCP客戶端的步驟如下

  1. 創建一個AsyncSocket對象,創建該對象時需要指定該AsyncSocket的delegate,該delegate必須實現AsyncSocketDelegate協議,該delegate對象負責處理AsyncSocket在網絡通信過程中的各種事件。
  2. 調用AsyncSocket對象的connectToHost:onPort:error:方法控制AsyncSocket對象連接指定IP地址、指定端口的服務器程序,該方法的最后一個參數用於接收連接錯誤。
  3. 為AsyncSocket的delegate對象(實現AsyncSocketDelegate協議的對象)實現特定的方法,該delegate負責處理AsyncSocket在網絡通信過程中的各種事件。
  4. 如果需要發送數據,則調用AsyncSocket的writeData:withTimeout:tag:方法;如果需要讀取數據,則調用AsyncSocket的readDataWithTimeout:tag:方法。當數據發送完成或數據讀取完成時,都會激發AsyncSocket的delegate對象的特定方法。

代 碼 片 段

  1 ViewController.h
  2 
  3 #import <UIKit/UIKit.h>
  4 
  5 #import “AsyncSocket.h”
  6 
  7 @interface ViewController : UIViewController<AsyncSocketDelegate>
  8 
  9 @property (strong, nonatomic) IBOutlet UITextView *showView;
 10 
 11 @property (strong, nonatomic) IBOutlet UITextFiled *inputField;
 12 
 13 - (IBAction)finishEdit:(id)sender;
 14 
 15 - (IBAction)send:(id)sender;
 16 
 17 @end
 18 
 19 ViewController.m
 20 
 21 @implementation ViewController
 22 
 23 NSString* myName;
 24 
 25 AsyncSocket* socket;
 26 
 27 BOOL iSOnline;
 28 
 29 - (void)viewDidLoad
 30 
 31 {
 32 
 33   [super viewDidLoad];
 34 
 35   // 創建一個UIAlertView提醒用戶輸入名字
 36 
 37   UIAlertView* alert = [[UIAlertView alloc] initWithTitle:@”名字”
 38 
 39 message:@”請輸入您的名字”  delegate:self
 40 
 41 cancelButtonTitle:@”確定” otherButtonTitles: nil ];
 42 
 43 // 設置該UIAlertView為UIAlertViewStylePlainTextInput風格
 44 
 45 alert.alertViewStyle = UIAlertViewStylePlainTextInput;
 46 
 47 [alert show];
 48 
 49 }
 50 
 51 - (IBAction)finishEdit:(id)sender
 52 
 53 {
 54 
 55    [sender resignFirstResponder];
 56 
 57 }
 58 
 59 - (IBAction)send:(id)sender
 60 
 61 {
 62 
 63   if(isOnline)
 64 
 65   {
 66 
 67 // 定義要發送的字符串內容
 68 
 69 NSString* stringTosend = [NSString stringWithFormat:@”%@說: %@”, myName, self.inputField.text];
 70 
 71 self.inputField.text = nil;
 72 
 73 NSData *data = [stringTosend dataUsingEncoding:NSUTF8StringEncoding];
 74 
 75 // 調用writeData:withTimeout:tag:方法發送數據
 76 
 77 [socket  writeData:data  withTimeout: - 1  tag:0];
 78 
 79   }
 80 
 81   else
 82 
 83   {
 84 
 85     NSLog(@”暫未連接服務器”);
 86 
 87 }
 88 
 89 }
 90 
 91 // AsyncSocketDelegate中定義的方法,當成功連接到服務器時激發該方法
 92 
 93 - (void)onSocket:(AsyncSocket *)sock didConnectToHost:(NSString *)host port:(UInt16)port
 94 
 95 {
 96 
 97   isOnline = YES;
 98 
 99   // 調用readDataWithTimeout: tag: 方法讀取數據
100 
101  [sock readDataWithTimeout:-1 tag:0];
102 
103 }
104 
105 // AsyncSocketDelegate中定義的方法,當讀取數據完成時激發該方法
106 
107 - (void)onSocket:(AsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag
108 
109 {
110 
111   // 獲取讀到的內容
112 
113   NSData* strData = [data subDataWithRange:NSMakeRange(0, [data length])];
114 
115   NSString* content = [[NSString alloc] initWithData:strData encoding:NSUTF8StringEncoding];
116 
117   if(content)
118 
119   {
120 
121 // 使用showView控件顯示從網絡讀取的內容
122 
123 self.showView.text = [NSString stringWithFormat:@”%@\n%@”,
124 
125 content , self.showView.text];
126 
127 }
128 
129 // 再次調用readDataWithTimeout:tag:方法讀取數據
130 
131 [sock readDataWithTimeout:-1 tag:0];
132 
133 }
134 
135 - (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
136 
137 {
138 
139   //  獲取UIAlertView控件上 文本框內的字符串,並將字符串賦值給myName變量
140 
141   myName = [alertView textFieldAtIndex:0].text;
142 
143   socket = [[AsyncSocket alloc] initWithDelegate:self];
144 
145   NSError* error = nil;
146 
147   int port = 30000;
148 
149   NSString* host = @”192.168.1.88”;
150 
151   // 調用connectToHost:onPort:error:方法連接指定IP地址、指定端口的服務器
152 
153   [socket connectToHost:host onPort:port withTimeout:2 error:&error];
154 
155   if(error)
156 
157   {
158 
159     NSLog(@”連接出現錯誤:%@”,  error);
160 
161 }
162 
163 }
164 
165 @end

 

  上面程序中的第1行紅色字代碼創建了一個AsyncSocket,並指定該視圖控制器本身作為它的delegate對象,這意味着該視圖控制器本身需要實現AsyncSocketDelegate協議,並實現該協議中特定的方法.程序中的第2行紅色字代碼調用了AsyncSocket的connectToHost:onPort:error: 方法來連接指定IP地址、指定端口的服務器程序。

   處理AsyncSocket網絡連接及網絡通信過程中的事件,該視圖控制器(作為AsyncSocket的delegate)實現了onSocket:didConnectToHost:port:方法------當AsyncSocket成功連接指定服務器時激發該方法,該方法中的程序調用了AsyncSocket的readDataWithTimeout: tag:方法讀取網絡數據。當AsyncSocket成功讀取網絡數據之后,系統會自動調用視圖控制器(作為AsyncSocket的delegate)的onSocket:didReadData:方法------這就實現了通過AsyncSocket讀取網絡數據。


注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



 
粤ICP备14056181号  © 2014-2020 ITdaan.com