(六十四)iOS的socket实现(C+OC混合实现)

对于微博、微信朋友圈之类的网络通信,使用JSON完全可以满足需求,但是如果要制作网络游戏,就需要建立一个持久连接,这时候就要考虑使用socket。

在iOS上实现socket大体有两种方法,一是借助自带的输入输出流和C语言socket相结合,二是利用第三方类库CocoaAsyncSocket,本文将介绍前者,在下一篇文章中介绍基于第三方类库的实现方法。

要调试socket,首先应该有一个简易的socket服务端,下面是用python写的简单服务端,功能是发送iam:name相当于登录操作,服务器会返回name has joined,发送msg:content,服务器会判断当前登录者,然后返回name:msg。

下面是server.py的源码

from twisted.internet.protocol import Protocol, Factoryfrom twisted.internet import reactorclass IphoneChat(Protocol):def connectionMade(self):#self.transport.write("""connected""")self.factory.clients.append(self)print "clients are ", self.factory.clientsdef connectionLost(self, reason):self.factory.clients.remove(self)def dataReceived(self, data):#print "data is ", dataa = data.split(':')if len(a) > 1:command = a[0]content = a[1]msg = ""if command == "iam":self.name = contentmsg = self.name + " has joined"elif command == "msg":msg = self.name + ": " + contentprint msgfor c in self.factory.clients:c.message(msg)def message(self, message):self.transport.write(message + '\n')factory = Factory()factory.protocol = IphoneChatfactory.clients = []reactor.listenTCP(12345, factory)print "Iphone Chat server started"reactor.run()使用方法也很简单,只需要打开终端,输入python server.py即可运行服务端,运行成功后会在终端打印Iphone Chat server started.

下面介绍如何实现iOS客户端:

从上面的源码可以看到,服务端的端口号是12345,假设是在本地使用,则按如下方法进行连接:

// 建立连接NSString *host = @"127.0.0.1";int port = 12345;// 注意C字符串和OC字符串桥接转换// 定义输入输出流CFReadStreamRef read_s;CFWriteStreamRef write_s;CFStreamCreatePairWithSocketToHost(NULL, (__bridge CFStringRef)host, port, &read_s, &write_s);注意CF开头的是C语言的结构体和函数,为了和OC连接,我们需要定义OC的输入输出流:NSInputStream *_input_s;NSOutputStream *_output_s;然后进行桥接转换:不要忘记设置代理,OC的输入输出流有数据时通过代理来通知。 // 使用代理来通知连接建立是否成功,把C语言的输入输出流转化成OC对象,使用桥接转换。_input_s = (__bridge NSInputStream *)(read_s);_output_s = (__bridge NSOutputStream *)(write_s);_input_s.delegate = _output_s.delegate = self;Tip:流的代理名称为NSStreamDelegate。

之后不要忘记将流加入到主运行循环,并且打开流:

// 把输入输出流添加到主运行循环,否则代理可能不工作[_input_s scheduleInRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];[_output_s scheduleInRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];// 打开输入输出流[_input_s open];[_output_s open];在连接结束时时,不要忘记把流从主循环上移除,判断连接状态通过NSStream的代理方法:- (void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)eventCode{switch (eventCode) {case NSStreamEventEndEncountered:{ // 连接结束NSLog(@"关闭输入输出流");[_input_s close];[_output_s close];[_input_s removeFromRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];[_output_s removeFromRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];break;}case NSStreamEventErrorOccurred: // 连接出错break;case NSStreamEventHasBytesAvailable: // 有字节可读[self readData];break;case NSStreamEventNone: // 无事件break;case NSStreamEventOpenCompleted: // 连接打开完成NSLog(@"打开完成");break;case NSStreamEventHasSpaceAvailable: // 可以发放字节break;default:break;}}建立连接的完整代码为: // 建立连接NSString *host = @"127.0.0.1";int port = 12345;// 注意C字符串和OC字符串桥接转换// 定义输入输出流CFReadStreamRef read_s;CFWriteStreamRef write_s;CFStreamCreatePairWithSocketToHost(NULL, (__bridge CFStringRef)host, port, &read_s, &write_s);// 使用代理来通知连接建立是否成功,把C语言的输入输出流转化成OC对象,使用桥接转换。_input_s = (__bridge NSInputStream *)(read_s);_output_s = (__bridge NSOutputStream *)(write_s);_input_s.delegate = _output_s.delegate = self;// 把输入输出流添加到主运行循环,否则代理可能不工作[_input_s scheduleInRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];[_output_s scheduleInRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];// 打开输入输出流[_input_s open];[_output_s open];

想要发送一条消息,只需要通过OC的输出流即可,,先把NSString转化为data,然后调用流的write方法,注意这里需要传两个参数,一个是缓冲区大小,一个是最大长度:data.bytes返回的是一个很大的值,而data.length返回的是实际长度,因此我们用data.bytes作为缓冲区的大小,data.length作为实际长度。

// 发送 iam:name 表示name登录NSString *loginStr = @"iam:soulghost";// 把string转成NSDataNSData *data = [loginStr dataUsingEncoding:NSUTF8StringEncoding];[_output_s write:data.bytes maxLength:data.length];一起吃早餐,午餐,晚餐。或许吃得不好,

(六十四)iOS的socket实现(C+OC混合实现)

相关文章:

你感兴趣的文章:

标签云: