下载地址:html
https://github.com/robbiehanson/CocoaAsyncSocket(必须使用arc项目)java
CocoaAsyncSocket支持tcp和udp。其中:git
AsyncSocket是封装了CFSocket和CFSteam的TCP/IP socket网络库。它提供了异步操做,本地cocoa类的基于delegate的完整支持。主要有如下特性:github
AsyncUdpSocket是UDP/IP socket网络库,包装自CFSocket。它的工做很像TCP版本,只不过是用于处理UDP的。它包括基于非阻塞队列的发送接收操做,完整的委托支持,基于runloop,自包含的类,以及支持IPV4和IPV6。windows
如下内容是根据官方网站参考:服务器
http://code.google.com/p/cocoaasyncsocket/wiki/Reference_AsyncSocket网络
编写的示例。多线程
可按照官网连接执行:dom
基本上是两步:
编写个简单的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");
}
这样就能够在日志中监控到关闭链接的信息。
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];
}
}
日志相似这样:
标红色的是两次取出的字节内容。
编写了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的以下方法:
在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数据报的示例。这须要有个服务器端能接收到内容。用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!
收到了数据报。
这里主要关注的是接收,一方面是需求上要求,另外一方面,碰到过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的操做。这里就很少说了。