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

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

CocoaAsyncSocket学习

下载地址:

https://github.com/robbiehanson/CocoaAsyncSocket(必须使用arc项目)

CocoaAsyncSocket支持tcp和udp。其中:

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

AsyncSocket是封装了CFSocket和CFSteam的TCP/IP socket网络库。它提供了异步操作,本地cocoa类的基于delegate的完整支持。主要有以下特性:

  • 队列的非阻塞的读和写,而且可选超时。你可以调用它读取和写入,它会当完成后告知你
  • 自动的socket接收。如果你调用它接收连接,它将为每个连接启动新的实例,当然,也可以立即关闭这些连接
  • 委托(delegate)支持。错误、连接、接收、完整的读取、完整的写入、进度以及断开连接,都可以通过委托模式调用
  • 基于run loop的,而不是线程的。虽然可以在主线程或者工作线程中使用它,但你不需要这样做。它异步的调用委托方法,使用NSRunLoop。委托方法包括socket的参数,可让你在多个实例中区分
  • 自包含在一个类中。你无需操作流或者socket,这个类帮你做了全部
  • 支持基于IPV4和IPV6的TCP流

AsyncUdpSocket是UDP/IP socket网络库,包装自CFSocket。它的工作很像TCP版本,只不过是用于处理UDP的。它包括基于非阻塞队列的发送接收操作,完整的委托支持,基于runloop,自包含的类,以及支持IPV4和IPV6。

以下内容是根据官方网站参考:

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

编写的示例。

准备工作:如何在iOS项目中使用

可按照官网链接执行:

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返回信息。下面说一下步骤,如图:

下面用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访问看到的是这样的结尾:

因此,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]; 
    } 
}

日志类似这样:

标红色的是两次取出的字节内容。

编写服务器端Socket

编写了Echo示例,说明最简单的服务器端Socket写法。Echo就是回声,通过telnet发送什么,服务器端就返回什么。类似这样:

服务器端,需要监听客户端的连接。等待客户端发来信息。代码是这样的:

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.");

执行后,在接收端成功输出如下内容:

下面,写个接收端的代码:

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的操作。这里就不多说了。

asyncsocket的用法的更多相关文章

  1. 关于AsyncSocket

               写篇博客,在我项目中用到了一个很重要的第三方---AsyncSocket,写下我对AsyncSocket使用心得.我的项目中是APP对硬件直接交互,APP对硬件发指令的时候不需要 ...

  2. iOS  UDP 广播 AsyncSocket 用法

    因为业务需要,需要用广播发送一个字段,在iOS开发中,用到了AsynSocket. 1.定义一个属性,负责发送和接受数据 #define YX_Local_Host @"255.255.25 ...

  3. EditText 基本用法

    title: EditText 基本用法 tags: EditText,编辑框,输入框 --- EditText介绍: EditText 在开发中也是经常用到的控件,也是一个比较必要的组件,可以说它是 ...

  4. jquery插件的用法之cookie 插件

    一.使用cookie 插件 插件官方网站下载地址:http://plugins.jquery.com/cookie/ cookie 插件的用法比较简单,直接粘贴下面代码示例: //生成一个cookie ...

  5. Java中的Socket的用法

                                   Java中的Socket的用法 Java中的Socket分为普通的Socket和NioSocket. 普通Socket的用法 Java中的 ...

  6. [转载]C#中MessageBox.Show用法以及VB.NET中MsgBox用法

    一.C#中MessageBox.Show用法 MessageBox.Show (String) 显示具有指定文本的消息框. 由 .NET Compact Framework 支持. MessageBo ...

  7. python enumerate 用法

    A new built-in function, enumerate() , will make certain loops a bit clearer. enumerate(thing) , whe ...

  8. [转载]Jquery中$.get(),$.post(),$.ajax(),$.getJSON()的用法总结

    本文对Jquery中$.get(),$.post(),$.ajax(),$.getJSON()的用法进行了详细的总结,需要的朋友可以参考下,希望对大家有所帮助. 详细解读Jquery各Ajax函数: ...

  9. 【JavaScript】innerHTML、innerText和outerHTML的用法区别

    用法: <div id="test">   <span style="color:red">test1</span> tes ...

随机推荐

  1. Combination Sum leetcode java

    题目: Given a set of candidate numbers (C) and a target number (T), find all unique combinations in C ...

  2. DNS预解析dns-prefetch提升页面载入速度优化前端性能

    当浏览器请求一个URL的时候,通过firebug我们可以发现大概有以下几个过程:阻挡.域名解析.建立连接.发送请求.等待响应.接收数据.后面四个跟用户的网络情况和你的服务器处理速度有关,本文重点说说前 ...

  3. sqrt函数的实现

    原文:http://blog.csdn.net/legend050709/article/details/39394381 sqrt算法实现: (一)int sqrt1(int n);求取整数x的平方 ...

  4. Windows 环境 cygwin 安装 SSH

    本文内容 安装环境 安装 cygwin 安装 SSH 服务 启动 sshd 服务 SSH 免密码登录 验证 SSH 是否已安装成功 验证 SSH 是否可以免密码登录本机 安装环境 Windows 20 ...

  5. hdu 1728 逃离迷宫 bfs记转向

    题链:http://acm.hdu.edu.cn/showproblem.php?pid=1728 逃离迷宫 Time Limit: 1000/1000 MS (Java/Others)    Mem ...

  6. android studio中的常用快捷键

    1.Ctrl+Alt+Space 这个类似Eclipse中的Alt+/,实现智能提示功能的 2.Ctrl+Y 删除当前行,Eclipse中是Ctrl+D,伤不起,每次都习惯性的按Ctrl+D,不删,反 ...

  7. 牛客网-《剑指offer》-从尾到头打印链表

    题目:http://www.nowcoder.com/practice/d0267f7f55b3412ba93bd35cfa8e8035 C++ /** * struct ListNode { * i ...

  8. 實戰ESXi 5設置MPIO 打造IP-SAN負載平衡容錯

    http://www.netadmin.com.tw/article_content.aspx?sn=1305100002 測試MPIO負載平衡及容錯移轉機制VMware vSphere ESXi主機 ...

  9. WebSocket【转】

    1.什么是WebSocket WebSocket 是一种自然的全双工.双向.单套接字连接.使用WebSocket,你的HTTP 请求变成打开WebSocket 连接(WebSocket 或者WebSo ...

  10. Codeforces Round #310 (Div. 1) B. Case of Fugitive(set二分)

    B. Case of Fugitive time limit per test 3 seconds memory limit per test 256 megabytes input standard ...