更多参考文章 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. iOS开发-UISwipeGestureRecognizer滑动手势

    滑动手势也算是iOS中交互中很重要的一部分,上下左右滑动,UISwipeGestureRecognizer可以很轻松的解决这个问题,没什么难度直接看代码吧: UISwipeGestureRecogni ...

  2. AS 注解处理器 APT Processor MD

    Markdown版本笔记 我的GitHub首页 我的博客 我的微信 我的邮箱 MyAndroidBlogs baiqiantao baiqiantao bqt20094 baiqiantao@sina ...

  3. mybatis 乐观锁和逻辑删除

    本篇介绍easymybatis如配置乐观锁和逻辑删除. 乐观锁 easymybatis提供的乐观锁使用方式跟JPA一样,使用@Version注解来实现.即:数据库增加一个int或long类型字段ver ...

  4. jquery中对 iframe的操作

    我们先看一下 JQUERY中的对像 contents() 的帮助文件 contents() 概述 查找匹配元素内部所有的子节点(包括文本节点).如果元素是一个iframe,则查找文档内容 示例 描述: ...

  5. Backbone.js 的 View 中定义事件

    使用 Backbone 的 View 时,可以象传统 jQuery 那样定义事件,$("selector").click(function(){...}).幸运的是 Backbon ...

  6. Kafka:ZK+Kafka+Spark Streaming集群环境搭建(十七)Elasticsearch-6.2.2集群安装,组件安装

    1.集群安装es ES内部索引原理: <时间序列数据库的秘密(1)—— 介绍> <时间序列数据库的秘密 (2)——索引> <时间序列数据库的秘密(3)——加载和分布式计算 ...

  7. ps叠加模式笔记

    1.叠加模式:Overlay混色模式会让图层白色的部分去加亮底色,图层深色的部分去加暗底色 2.常见按钮:1)主体渐变:2)投影:3)内阴影:颜色减淡,按钮颜色,距离1,大小3:4)外发光,正片叠底,

  8. cmake 学习笔记(三) (转)

    接前面的 Cmake学习笔记(一) 与 Cmake学习笔记(二) 继续学习 cmake 的使用. 学习一下cmake的 finder. finder是神马东西? 当编译一个需要使用第三方库的软件时,我 ...

  9. C#正则表达式 - 精通版

    1.正则所需要的命名空间是 using System.Text.RegularExpressions 2.创建Regex对象 new Regex(string pattern,RegexOptions ...

  10. JS获取当前/指定URL参数

    方法: 首先通过 document.location 获得当前访问网页的网址, 其次用 split 方法通过“?”把网址分为两部分. 如果网址中有参数(arrObj.length > 1) 再用 ...