asyncsocket的用法

更多參考文章 http://blog.csdn.net/zltianhen/article/details/6560322

http://www.cnblogs.com/bucengyongyou/archive/2012/10/28/2743523.html

CocoaAsyncSocket學習

下載地址:html

https://github.com/robbiehanson/CocoaAsyncSocket(必須使用arc項目)java

CocoaAsyncSocket支持tcp和udp。其中:git

  • AsyncSocket類是支持TCP的
  • AsyncUdpSocket是支持UDP的

AsyncSocket是封裝了CFSocket和CFSteam的TCP/IP socket網絡庫。它提供了異步操做,本地cocoa類的基於delegate的完整支持。主要有如下特性:github

  • 隊列的非阻塞的讀和寫,並且可選超時。你能夠調用它讀取和寫入,它會當完成後告知你
  • 自動的socket接收。若是你調用它接收鏈接,它將爲每一個鏈接啓動新的實例,固然,也能夠當即關閉這些鏈接
  • 委託(delegate)支持。錯誤、鏈接、接收、完整的讀取、完整的寫入、進度以及斷開鏈接,均可以經過委託模式調用
  • 基於run loop的,而不是線程的。雖然能夠在主線程或者工做線程中使用它,但你不須要這樣作。它異步的調用委託方法,使用NSRunLoop。委託方法包括socket的參數,可以讓你在多個實例中區分
  • 自包含在一個類中。你無需操做流或者socket,這個類幫你作了所有
  • 支持基於IPV4和IPV6的TCP流

AsyncUdpSocket是UDP/IP socket網絡庫,包裝自CFSocket。它的工做很像TCP版本,只不過是用於處理UDP的。它包括基於非阻塞隊列的發送接收操做,完整的委託支持,基於runloop,自包含的類,以及支持IPV4和IPV6。windows

 

如下內容是根據官方網站參考:服務器

http://code.google.com/p/cocoaasyncsocket/wiki/Reference_AsyncSocket網絡

編寫的示例。多線程

 

準備工做:如何在iOS項目中使用

可按照官網連接執行:dom

http://code.google.com/p/cocoaasyncsocket/wiki/iPhone異步

基本上是兩步:

  1. 將CocoaAsyncSocket項目中的.h和.m文件拖拽到本身項目的Classes目錄中
  2. 添加framework:CFNetwork

 

編寫簡單的TCP鏈接

編寫個簡單的TCP鏈接應用。HTTP其實就是創建在TCP協議上的。這裏就用向網站發起請求和得到響應來演示。

爲了形象說明,先手工模擬一下HTTP。這須要用到telnet工具,這是個命令行工具,若是在命令行裏敲:

C:\Users\Marshal Wu>telnet 
‘telnet’ 不是內部或外部命令,也不是可運行的程序 
或批處理文件。

 

說明你使用的是windows vista或者windows7,由於windows xp是默認安裝該軟件的。

我用的是Mac OSX,上面自帶這個工具。若是你出現上面的問題,可參照vista下使用telnet的作法安裝telnet。

而後,可使用這個工具發出socket信息,並接收socket返回信息。下面說一下步驟,如圖:

image

下面用CocoaAsyncSocket來實現。

首先是要實現相關的delegate:

#import <UIKit/UIKit.h>

#import "AsyncSocket.h"

@interface SocketDemosViewController : UIViewController<AsyncSocketDelegate>

 

而後,在實現代碼中:

- (void)onSocket:(AsyncSocket *)sock didConnectToHost:(NSString *)host port:(UInt16)port{ 
    NSLog(@"did connect to host"); 
}

- (void)onSocket:(AsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag{ 
    NSLog(@"did read data"); 
    NSString* message = [[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding] autorelease]; 
    NSLog(@"message is: \n%@",message); 
}

 

AsyncSocketDelegate中的方法都是可選的。我實現了對創建鏈接後以及讀取數據的監聽。

這些監聽須要建立和使用AsyncSocket實例時才能被用到。下面就是這部分代碼:

- (void)viewDidLoad { 
    [super viewDidLoad]; 
    
    AsyncSocket *socket=[[AsyncSocket alloc] initWithDelegate:self]; 
    [socket connectToHost:@"www.baidu.com" onPort:80 error:nil]; 
    
    [socket readDataWithTimeout:3 tag:1]; 
    [socket writeData:[@"GET / HTTP/1.1\n\n" dataUsingEncoding:NSUTF8StringEncoding] withTimeout:3 tag:1];

 

我把這部分代碼直接寫到controller的viewDidLoad中了。

執行的日誌以下:

2011-07-19 17:17:46.545 SocketDemos[27120:207] did connect to host 
2011-07-19 17:17:46.620 SocketDemos[27120:207] did read data 
2011-07-19 17:17:46.621 SocketDemos[27120:207] message is: 
HTTP/1.1 200 OK 
Date: Tue, 19 Jul 2011 09:17:46 GMT 
Server: BWS/1.0 
Content-Length: 7691 
Content-Type: text/html;charset=gb2312 
Cache-Control: private 
Expires: Tue, 19 Jul 2011 09:17:46 GMT 
Set-Cookie: BAIDUID=9389BA38262D7997D220A564154CCA87:FG=1; expires=Tue, 19-Jul-41 09:17:46 GMT; path=/; domain=.baidu.com 
P3P: CP=" OTI DSP COR IVA OUR IND COM " 
Connection: Keep-Alive

這裏的HTTP響應被截斷了,由於咱們不是要編寫真的接收HTTP響應的功能,所以這個缺陷能夠忽略。

原本HTTP請求應該是由服務器端來關閉,好比使用telent訪問看到的是這樣的結尾:

image

所以,HTTP響應沒有徹底接收下來,服務器端未斷掉鏈接。能夠在客戶端關閉鏈接,這樣:

[socket readDataWithTimeout:3 tag:1]; 
[socket writeData:[@"GET / HTTP/1.1\n\n" dataUsingEncoding:NSUTF8StringEncoding] withTimeout:3 tag:1]; 
[socket disconnect]; 

另外,能夠實現delegate中的這個方法:

- (void)onSocketDidDisconnect:(AsyncSocket *)sock{ 
    NSLog(@"socket did disconnect"); 
}

 

這樣就能夠在日誌中監控到關閉鏈接的信息。

 

TCP鏈接讀取指定長度的數據

socket鏈接,常常碰到這樣的需求,讀取固定長度的字節。這能夠經過下面的示例實現。

仍是基於HTTP鏈接作演示。好比取2次,每次50字節。而後中止socket。

能夠這樣寫:

- (void)viewDidLoad { 
    [super viewDidLoad]; 
    
    socket=[[AsyncSocket alloc] initWithDelegate:self]; 
    [socket connectToHost:@"www.baidu.com" onPort:80 error:nil]; 
    
    data=[[NSMutableData dataWithLength:50] retain]; 
    
    [socket readDataToLength:50 withTimeout:5 tag:1]; 
    [socket readDataToLength:50 withTimeout:5 tag:2]; 
    [socket writeData:[@"GET / HTTP/1.1\n\n" dataUsingEncoding:NSUTF8StringEncoding] withTimeout:3 tag:1];

 

在delegate中,主要是這個方法起做用:

- (void)onSocket:(AsyncSocket *)sock didReadData:(NSData *)_data withTag:(long)tag{ 
    NSLog(@"did read data"); 
    NSString* message = [[[NSString alloc] initWithData:_data encoding:NSUTF8StringEncoding] autorelease]; 
    NSLog(@"message is: \n%@",message); 
    
    if (tag==2) { 
        [socket disconnect]; 
    } 
}

日誌相似這樣:

image

標紅色的是兩次取出的字節內容。

 

編寫服務器端Socket

編寫了Echo示例,說明最簡單的服務器端Socket寫法。Echo就是回聲,經過telnet發送什麼,服務器端就返回什麼。相似這樣:

image

服務器端,須要監聽客戶端的鏈接。等待客戶端發來信息。代碼是這樣的:

socket=[[AsyncSocket alloc] initWithDelegate:self]; 
NSError *err = nil;

if ([socket acceptOnPort:4322 error:&err]) { 
    NSLog(@"accept ok."); 
}else { 
    NSLog(@"accept failed."); 
}

if (err) { 
    NSLog(@"error: %@",err); 
}

 

這一步若是成功,應該只有一個日誌信息:

2011-07-20 12:27:03.228 SocketDemos[611:707] accept ok.

這時若是有客戶端與之創建鏈接,好比經過telnet。會依次調用AsyncSocket 的delegate的以下方法:

  • onSocket:didAcceptNewSocket: AsyncSocket建立了新的Socket用於處理和客戶端的請求,若是這個新socket實例你不打算保留(retain),那麼將拒絕和該客戶端鏈接
  • onSocket:wantsRunLoopForNewSocket:,提供線程的runloop實例給AsyncSocket,後者將使用這個runloop執行socket通信的操做
  • onSocketWillConnect:,將要創建鏈接,這時能夠作一些準備工做,若是須要的話
  • onSocket:didConnectToHost:port:,這個方法是創建鏈接後執行的,通常會在這裏調用寫入或者讀取socket的操做

在Echo示例中,不打算執行多線程,也不想支持多客戶端鏈接,並且服務器端和客戶端將創建長鏈接。直至客戶端斷開鏈接,服務器端才釋放相應的socket。

代碼以下:

- (void)onSocket:(AsyncSocket *)sock didAcceptNewSocket:(AsyncSocket *)newSocket{ 
    if (!acceptSocket) { 
        acceptSocket=[newSocket retain]; 
        NSLog(@"did accept new socket"); 
    } 
}

- (NSRunLoop *)onSocket:(AsyncSocket *)sock wantsRunLoopForNewSocket:(AsyncSocket *)newSocket{ 
    NSLog(@"wants runloop for new socket."); 
    return [NSRunLoop currentRunLoop]; 
}

- (BOOL)onSocketWillConnect:(AsyncSocket *)sock{ 
    NSLog(@"will connect"); 
    return YES; 
}

- (void)onSocket:(AsyncSocket *)sock didConnectToHost:(NSString *)host port:(UInt16)port{ 
    NSLog(@"did connect to host"); 
    [acceptSocket readDataWithTimeout:-1 tag:1]; 
}

- (void)onSocket:(AsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag{ 
    NSLog(@"did read data"); 
    NSString* message = [[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding] autorelease]; 
    NSLog(@"message is: \n%@",message); 
    [acceptSocket writeData:data withTimeout:2 tag:1]; 
}

- (void)onSocket:(AsyncSocket *)sock didWriteDataWithTag:(long)tag{ 
    NSLog(@"message did write"); 
    [acceptSocket readDataWithTimeout:-1 tag:1]; 
}

- (void)onSocket:(AsyncSocket *)sock willDisconnectWithError:(NSError *)err{ 
    NSLog(@"onSocket:%p willDisconnectWithError:%@", sock, err); 
}

- (void)onSocketDidDisconnect:(AsyncSocket *)sock{ 
    NSLog(@"socket did disconnect"); 
    [acceptSocket release]; 
    acceptSocket=nil; 
}

 

這裏timeout設置爲-1,這樣就能夠保持長鏈接狀態。

 

編寫簡單的UDP應用

首先,編寫發送UDP數據報的示例。這須要有個服務器端能接收到內容。用Java寫了個簡單的接收端:

public static void main(String[] args) throws IOException { 
    InetSocketAddress address = new InetSocketAddress("0.0.0.0", 5555); 
    DatagramSocket datagramSocket=new DatagramSocket(address); 
    
    System.out.println("start udp server"); 
    
    byte[] buffer=new byte[1024]; 
    
    for(;;){ 
        DatagramPacket datagramPacket=new DatagramPacket(buffer, buffer.length); 
        datagramSocket.receive(datagramPacket); 
        System.out.println("receive data:"+new String(datagramPacket.getData(),0,datagramPacket.getLength())); 
    } 
}

 

下面寫發送的代碼:

AsyncUdpSocket *socket=[[AsyncUdpSocket alloc]initWithDelegate:self];

NSData *data=[@"Hello from iPhone" dataUsingEncoding:NSUTF8StringEncoding]; 
[socket sendData:data toHost:@"192.168.0.165" port:5555 withTimeout:-1 tag:1]; 
NSLog(@"send upd complete.");

 

執行後,在接收端成功輸出以下內容:

image

下面,寫個接收端的代碼:

AsyncUdpSocket *socket=[[AsyncUdpSocket alloc] initWithDelegate:self];

NSError *error = nil; 
[socket bindToPort:5555 error:&error];

if (error) { 
    NSLog(@"error: %@",error); 
}

[socket receiveWithTimeout:-1 tag:1]; 
NSLog(@"start udp server");

另外,至少寫這個delegate方法:

- (BOOL)onUdpSocket:(AsyncUdpSocket *)sock 
     didReceiveData:(NSData *)data 
            withTag:(long)tag 
           fromHost:(NSString *)host 
               port:(UInt16)port{ 
    NSLog(@"received data: %@",[[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding] autorelease]); 
    return YES; 
}

 

發送端,仍是用java寫個測試代碼:

public static void main(String[] args) throws IOException { 
    DatagramSocket datagramSocket = new DatagramSocket(); 
    byte[] buffer = "Hello!".getBytes(); 
    DatagramPacket datagramPacket = new DatagramPacket(buffer, 
            buffer.length, new InetSocketAddress("192.168.0.144", 5555)); 
    datagramSocket.send(datagramPacket); 
}

 

在iPhone日誌中:

2011-07-20 15:23:33.571 SocketDemos[795:707] start udp server 
2011-07-20 15:23:47.395 SocketDemos[795:707] received data: Hello!

收到了數據報。

 

使用UDP發送和接收組播

這裏主要關注的是接收,一方面是需求上要求,另外一方面,碰到過Android Wifi獲取組播問題,擔憂iOS也有相似的機制。後來測試發現沒有那麼麻煩(打開組播鎖)。

爲了測試,仍是用java編寫了個發送UDP廣播的簡單代碼:

public static void main(String[] args) throws IOException { 
    int port=3333; 
    MulticastSocket socket=new MulticastSocket(port); 
    InetAddress address=InetAddress.getByName("239.0.0.1"); 
    socket.joinGroup(address); 
    byte[] data="Hello everyone.".getBytes(); 
    DatagramPacket datagramPacket=new DatagramPacket(data,data.length,address,port); 
    socket.send(datagramPacket); 
    System.out.println("send ok.");

 

編寫的iOS代碼:

AsyncUdpSocket *socket=[[AsyncUdpSocket alloc] initWithDelegate:self];

NSError *error = nil; 
[socket bindToPort:3333 error:&error]; 
[socket enableBroadcast:YES error:&error]; 
[socket joinMulticastGroup:@"239.0.0.1" error:&error];

if (error) { 
    NSLog(@"error: %@",error); 
}

[socket receiveWithTimeout:-1 tag:1]; 
NSLog(@"start udp server");

 

delegate和上面接收普通UDP如出一轍:

- (BOOL)onUdpSocket:(AsyncUdpSocket *)sock 
     didReceiveData:(NSData *)data 
            withTag:(long)tag 
           fromHost:(NSString *)host 
               port:(UInt16)port{ 
    NSLog(@"received data: %@",[[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding] autorelease]); 
    return YES; 
}

 

測試獲得的日誌:

2011-07-20 16:14:30.338 SocketDemos[860:707] start udp server 
2011-07-20 16:14:42.829 SocketDemos[860:707] received data: Hello everyone.

說明是收到了。

發送組播和前面的UDP發送相似,只是多了要作join group的操做。這裏就很少說了。

相關文章
相關標籤/搜索