使用NSStream来实现Socket

使用NSStream来实现Socket,第1张

概述http://www.cocoachina.com/bbs/read.php?tid=6146#   这个类使用了Singleton,因此永远只有一个实例。没有实例时会自动生成实例,可以在程序中的任何位置调用它。 一般来说,只要跟服务器建立一次连接即可,产生一对stream,分别是outStream和inStream,所有的数据都通过它们不断地发送和接收。 stream的end意味着连接中断,如果 http://www.cocoachina.com/bbs/read.php?tid=6146#  

这个类使用了Singleton,因此永远只有一个实例。没有实例时会自动生成实例,可以在程序中的任何位置调用它。
一般来说,只要跟服务器建立一次连接即可,产生一对stream,分别是outStream和inStream,所有的数据都通过它们不断地发送和接收。
stream的end意味着连接中断,如果还需要访问服务器的话,得重新连接stream。(也就是重新实例化一下我这个类)
每次发送和接受的数据包大小需要自己控制,而不是等stream来告诉你这个数据包有多大,因为stream不会告诉你……
控制方法之一:通过添加一个特殊的后缀来判断,比如“<EOF>”,每次读到这个组合就认为数据读完。但是问题很明显,这个只能用于string。
控制方法之二:通过添加一个4字节的前缀来判断长度。这4个byte的byte[]数组,是当前数据包的长度信息,根据这个信息来读取一定长度的数据。
每次数据收完后,我用了一个取巧的方法来把数据返还给调用stream的函数……这个部分需要改进。

代码
SynthesizeSingleton.h,实现singleton的类
?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 // //  SynthesizeSingleton.h //  CocoaWithlove // //  Created by Matt gallagher on 20/10/08. //  copyright 2009 Matt gallagher. All rights reserved. // //  Permission is given to use this source code file without charge in any //  project,commercial or otherwise,entirely at your risk,with the condition //  that any redistribution (in part or whole) of source code must retain //  this copyright and permission notice. Attribution in compiled projects is //  appreciated but not required. //    #define SYNTHESIZE_SINGLetoN_FOR_CLASS(classname) \   \ static classname *shared##classname = nil ; \ \ + (classname *)shared##classname \ { \      @synchronized ( self ) \      { \          if (shared##classname == ) \          { \              shared##classname = [[ alloc] init]; \ } \ } \       \ return shared##classname; \ } \ \ + ( ID )allocWithZone:( NSZone *)zone \ { \ ) \ { \ ) \ { \ shared##classname = [ super allocWithZone:zone]; \              shared##classname; \ } \ } \ \ return ; \ } \ \ - ( )copyWithZone:( *)zone \ { \ ; \ } \ \ )retain \ { \ ; \ } \ \ - ( NSUInteger )retainCount \ { \ NSUIntegerMax ; \ } \ \ voID )release \ { \ } \ \ )autorelease \ { \ ; \ }


Stream.h
24 #import <foundation foundation.h="">  #import <cfnetwork cfnetwork.h=""> #import <systemconfiguration systemconfiguration.h=""> #import <netinet in.h=""> #import <arpa inet.h="">   @interface Stream : NSObject { NSinputStream    *inStream; NSOutputStream    *outStream; NSMutableData    *dataBuffer;            BOol            _hasEstablished; ID                _currentObject; int                _numCondition;       _isFirstFourBytes; uint            remainingToRead; }   + (Stream *)sharedStream; -( )requestData:( Nsstring *)requestString whoRequest:( )currentObject condition:( int )numCondition; )manageData:( NSData *)receivedData; @end </arpa></netinet></systemconfiguration></cfnetwork></foundation>


Stream.m
? 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 @H_502_947@ 183 184 185 186 187 188 189 190 191 @H_404_965@ 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 @H_301_1027@ 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 @H_502_1063@ 241 242 243 244
#import "Stream.h" #import "SynthesizeSingleton.h"   @implementation Stream   SYNTHESIZE_SINGLetoN_FOR_CLASS(Stream);   )startClIEnt { _hasEstablished = NO ; CFReadStreamRef        readStream = NulL ; CFWriteStreamRef    writeStream = ; Nsstring            *server = /*你的服务器地址,比如我公司服务器地址[url]www.javista.com[/url]*/ ;      //这里没有用Nsstream的getStreamsToHost,是因为真机编译时有黄色提示说不存在这个函数。 //虽然真机能用,但我担心上传到APP Store时会被reject,所以就用了更底层的CFStreamCreatePairWithSocketToHost。 //其实一点都不难,一样用的~ CFStreamCreatePairWithSocketToHost(kcfAllocatorDefault,                                         (CFStringRef)server, 1234, //服务器接收数据的端口 &readStream, &writeStream);             (readStream && writeStream) { inStream = ( NSinputStream *)readStream; outStream = ( NSOutputStream *)writeStream; } else {          //Error Control } }   )closeStreams{ [[PromptVIEw sharedPromptVIEw] dismisspromptVIEw]; [inStream close]; [outStream close]; [inStream removeFromrunLoop:[ NSRunLoop currentRunLoop] forMode: NSDefaultRunLoopMode ]; [outStream removeFromrunLoop:[ ]; [inStream setDelegate: ]; [outStream setDelegate: ]; [inStream release]; [outStream release]; inStream = ; outStream = ; }   )openStreams{ [inStream retain]; [outStream retain]; [inStream setProperty: NsstreamSocketSecurityLevelSSLv3 forKey: NsstreamSocketSecurityLevelKey ]; [outStream setProperty: ]; //不需要SSL的话,下面这行可以去掉。 CFWriteStreamSetProperty((CFWriteStreamRef)outStream,kcfStreamPropertySSLSettings,[ NSMutableDictionary dictionaryWithObjectsAndKeys:( )kcfBooleanFalse,kcfStreamSSLValIDatesCertificateChain,kcfBooleanFalse,kcfStreamSSlisServer, ]); ]; ]; [inStream scheduleInRunLoop:[ ]; [outStream scheduleInRunLoop:[ ]; [inStream open]; [outStream open]; }   )stream:( Nsstream *)aStream handleEvent:( NsstreamEvent )eventCode { switch (eventCode) { case NsstreamEventHasBytesAvailable : { @H_701_1419@ (_isFirstFourBytes) //读取前4个字节,算出数据包大小 {                  uint8_t bufferLen[4];                  ([inStream read:bufferLen maxLength:4] == 4) {                      remainingToRead = ((bufferLen[0]<<24)&0xff000000)+((bufferLen[1]<<16)&0xff0000)+((bufferLen[2]<<8)&0xff00)+(bufferLen[3] & 0xff); _isFirstFourBytes = ; } else { [ closeStreams];                      //Error Control } } else //根据数据包大小读取数据 {                  actuallyRead; uint8_t buffer[32768]; //32KB的缓冲区,缓冲区太小的话会明显影响真机上的通信速度 (!dataBuffer) { dataBuffer = [[ NSMutableData alloc] init]; }                   actuallyRead = [inStream read:buffer maxLength: sizeof (buffer)]; (actuallyRead == -1){ closeStreams]; //Error Control } else (actuallyRead == 0){ //Do something if you want else { [dataBuffer appendBytes:buffer length:actuallyRead]; remainingToRead -= actuallyRead; }                   (remainingToRead == 0) { YES ; manageData:dataBuffer]; //数据接收完毕,把数据送回调用sream的函数 [dataBuffer release]; dataBuffer = ; } } break ; } NsstreamEventEndEncountered : //连接断开或结束 { closeStreams]; ; } NsstreamEventErrorOccurred //无法连接或断开连接 { ([[aStream streamError] code]) //确定code不是0……有时候正常使用时会跳出code为0的错误,但其实一点问题都没有,可以继续使用,很奇怪…… { closeStreams]; ; } } NsstreamEventopenCompleted : { ; ; } NsstreamEventHasspaceAvailable : { ; } NsstreamEventNone : default : ; } }   //判断是否能连接到服务器。这个函数用来判断网络是否连通还好,要真的判断服务器上对应的端口是否可以连接,不是很好用来着…… BOol )isServerAvailable{ *addressstring = /*你的服务器地址,比如我公司地址[url]www.javista.com[/url]*/ ; (!addressstring) { ; }   SCNetworkReachabilityRef defaultRouteReachability = SCNetworkReachabilityCreateWithname(kcfAllocatorDefault,[addressstring UTF8String]); SCNetworkReachabilityFlags flags;       dIDRetrIEveFlags = SCNetworkReachabilityGetFlags(defaultRouteReachability,&flags); CFRelease(defaultRouteReachability);       (!dIDRetrIEveFlags) { ; }       isReachable = flags & kSCNetworkFlagsReachable; needsConnection = flags & kSCNetworkFlagsConnectionrequired; (isReachable && !needsConnection) ? : ; }   )numCondition { (![ isServerAvailable]) //如果无法连通到服务器 { //Error Control } else { (inStream == || outStream == ) { [[Stream sharedStream] startClIEnt]; [[Stream sharedStream] openStreams]; ; }           (inStream != && outStream != ) { _currentObject = currentObject; //记下是谁调用了requestData(记下了它的指针) _numCondition = numCondition; //参数,以便有时候需要区分同一个类里发来的不同请求 (_hasEstablished) { *requestData = [requestString dataUsingEnCoding: NSUTF8StringEnCoding ]; dataLength = [requestData length];                                    //创建前4个字节用来表示数据包长度 uint8_t len[4]; for ( i = 0;i<4;i++) { len[i] = (Byte)(dataLength>>8*(3-i)&0xff); } [/i]                //将这4个字节添加到数据的开头 *dataToSend = [ dataWithBytes:len length:4]; [dataToSend appendData:requestData];                   remainingToWrite = dataLength+ 4; * marker = ( *)[dataToSend bytes]; actuallyWritten;                   while ([outStream hasspaceAvailable]) {                      (remainingToWrite > 0) {                          actuallyWritten = 0;                                                    (remainingToWrite < 32768)                              actuallyWritten = [outStream write:marker maxLength:remainingToWrite]; //不足32KB数据时发送剩余部分 else actuallyWritten = [outStream write:marker maxLength:32768]; //每次32KB数据                               ((actuallyWritten == -1) || (actuallyWritten == 0)) { closeStreams];                              //Error control } else { remainingToWrite -= actuallyWritten; marker += actuallyWritten;                      } } else { ; } } } else { //Error Control } } } }   *)receivedData{ [_currentObject getData:receivedData condition:_numCondition]; //执行_currentObject指针所指向的类里的getData函数,并把收到的数据传递过去 }   )dealloc { dealloc]; }   @end


用的时候,在调用stream的类的头文件里#import这个Stream.h,并添加一个函数叫- (voID)getData:(NSData *)receivedData condition:(int)numCondition;
发送时:
? 1
[[Stream SharedStream] requestData:@ "login" /*需要发送的命令*/ whoRequest: self /*把自己的指针传递过去*/ condition:0 /*用以区分不同功能的请求*/ ];

接收完毕后Stream会调用这个类里的getData函数,这个函数写法如下:
13 )getData:( *)receivedData condition:( )numCondition{ (numCondition) { case 0:              //Do something ; 1: //Do something different ; : } }
总结

以上是内存溢出为你收集整理的使用NSStream来实现Socket全部内容,希望文章能够帮你解决使用NSStream来实现Socket所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/web/1089120.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-27
下一篇 2022-05-27

发表评论

登录后才能评论

评论列表(0条)

保存