使用NSStream来实现Socket.docx
- 文档编号:11677574
- 上传时间:2023-03-30
- 格式:DOCX
- 页数:51
- 大小:45.58KB
使用NSStream来实现Socket.docx
《使用NSStream来实现Socket.docx》由会员分享,可在线阅读,更多相关《使用NSStream来实现Socket.docx(51页珍藏版)》请在冰豆网上搜索。
使用NSStream来实现Socket
使用NSStream来实现Socket
这玩意儿已经折腾我小半年了,因为没有socket开发方面的经验,跌跌撞撞遇到了不少麻烦。
以下是目前应用在我程序中的Stream类,真机真网络使用正常,3G和wifi都可以用。
只是回调部分写的比较外行……应该还有更好的回调方式。
以下代码除了SynthesizeSingleton.h外,都是从我自己的代码里一行一行挑出来的,没有测试,可能会有一些错误。
但关键部分都在了,应该问题不大。
先说一下理论。
这个类使用了Singleton,因此永远只有一个实例。
没有实例时会自动生成实例,可以在程序中的任何位置调用它。
一般来说,只要跟服务器建立一次连接即可,产生一对stream,分别是outStream和inStream,所有的数据都通过它们不断地发送和接收。
stream的end意味着连接中断,如果还需要访问服务器的话,得重新连接stream。
(也就是重新实例化一下我这个类)
每次发送和接受的数据包大小需要自己控制,而不是等stream来告诉你这个数据包有多大,因为stream不会告诉你……
控制方法之一:
通过添加一个特殊的后缀来判断,比如“
但是问题很明显,这个只能用于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
//
// CreatedbyMattGallagheron20/10/08.
// Copyright2009MattGallagher.Allrightsreserved.
//
// Permissionisgiventousethissourcecodefilewithoutchargeinany
// project,commercialorotherwise,entirelyatyourrisk,withthecondition
// thatanyredistribution(inpartorwhole)ofsourcecodemustretain
// thiscopyrightandpermissionnotice.Attributionincompiledprojectsis
// appreciatedbutnotrequired.
//
#defineSYNTHESIZE_SINGLETON_FOR_CLASS(classname)\
\
staticclassname*shared##classname=nil;\
\
+(classname*)shared##classname\
{\
@synchronized(self)\
{\
if(shared##classname==nil)\
{\
shared##classname=[[selfalloc]init];\
}\
}\
\
returnshared##classname;\
}\
\
+(id)allocWithZone:
(NSZone*)zone\
{\
@synchronized(self)\
{\
if(shared##classname==nil)\
{\
shared##classname=[superallocWithZone:
zone];\
returnshared##classname;\
}\
}\
\
returnnil;\
}\
\
-(id)copyWithZone:
(NSZone*)zone\
{\
returnself;\
}\
\
-(id)retain\
{\
returnself;\
}\
\
-(NSUInteger)retainCount\
{\
returnNSUIntegerMax;\
}\
\
-(void)release\
{\
}\
\
-(id)autorelease\
{\
returnself;\
}
Stream.h
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#import
#import
#import
#import
#import
@interfaceStream:
NSObject{
NSInputStream *inStream;
NSOutputStream *outStream;
NSMutableData *dataBuffer;
BOOL _hasEstablished;
id _currentObject;
int _numCondition;
BOOL _isFirstFourBytes;
uint remainingToRead;
}
+(Stream*)sharedStream;
-(void)requestData:
(NSString*)requestStringwhoRequest:
(id)currentObjectcondition:
(int)numCondition;
-(void)manageData:
(NSData*)receivedData;
@end
Stream.m
?
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
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
183
184
185
186
187
188
189
190
191
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
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
#import"Stream.h"
#import"SynthesizeSingleton.h"
@implementationStream
SYNTHESIZE_SINGLETON_FOR_CLASS(Stream);
-(void)startClient
{
_hasEstablished=NO;
CFReadStreamRef readStream=NULL;
CFWriteStreamRef writeStream=NULL;
NSString *server=/*你的服务器地址,比如我公司服务器地址[url][/url]*/;
//这里没有用NSStream的getStreamsToHost,是因为真机编译时有黄色提示说不存在这个函数。
//虽然真机能用,但我担心上传到APPStore时会被reject,所以就用了更底层的CFStreamCreatePairWithSocketToHost。
//其实一点都不难,一样用的~
CFStreamCreatePairWithSocketToHost(kCFAllocatorDefault,
(CFStringRef)server,
1234,//服务器接收数据的端口
&readStream,
&writeStream);
if(readStream&&writeStream)
{
inStream=(NSInputStream*)readStream;
outStream=(NSOutputStream*)writeStream;
}
else
{
//ErrorControl
}
}
-(void)closeStreams{
[[PromptViewsharedPromptView]dismissPromptView];
[inStreamclose];
[outStreamclose];
[inStreamremoveFromRunLoop:
[NSRunLoopcurrentRunLoop]forMode:
NSDefaultRunLoopMode];
[outStreamremoveFromRunLoop:
[NSRunLoopcurrentRunLoop]forMode:
NSDefaultRunLoopMode];
[inStreamsetDelegate:
nil];
[outStreamsetDelegate:
nil];
[inStreamrelease];
[outStreamrelease];
inStream=nil;
outStream=nil;
}
-(void)openStreams{
[inStreamretain];
[outStreamretain];
[inStreamsetProperty:
NSStreamSocketSecurityLevelSSLv3forKey:
NSStreamSocketSecurityLevelKey];
[outStreamsetProperty:
NSStreamSocketSecurityLevelSSLv3forKey:
NSStreamSocketSecurityLevelKey];
//不需要SSL的话,下面这行可以去掉。
CFWriteStreamSetProperty((CFWriteStreamRef)outStream,kCFStreamPropertySSLSettings,[NSMutableDictionarydictionaryWithObjectsAndKeys:
(id)kCFBooleanFalse,kCFStreamSSLValidatesCertificateChain,kCFBooleanFalse,kCFStreamSSLIsServer,nil]);
[inStreamsetDelegate:
self];
[outStreamsetDelegate:
self];
[inStreamscheduleInRunLoop:
[NSRunLoopcurrentRunLoop]forMode:
NSDefaultRunLoopMode];
[outStreamscheduleInRunLoop:
[NSRunLoopcurrentRunLoop]forMode:
NSDefaultRunLoopMode];
[inStreamopen];
[outStreamopen];
}
-(void)stream:
(NSStream*)aStreamhandleEvent:
(NSStreamEvent)eventCode
{
switch(eventCode){
caseNSStreamEventHasBytesAvailable:
{
if(_isFirstFourBytes)//读取前4个字节,算出数据包大小
{
uint8_tbufferLen[4];
if([inStreamread:
bufferLenmaxLength:
4]==4)
{
remainingToRead=((bufferLen[0]<<24)&0xff000000)+((bufferLen[1]<<16)&0xff0000)+((bufferLen[2]<<8)&0xff00)+(bufferLen[3]&0xff);
_isFirstFourBytes=NO;
}
else
{
[selfcloseStreams];
//ErrorControl
}
}
else//根据数据包大小读取数据
{
intactuallyRead;
uint8_tbuffer[32768];//32KB的缓冲区,缓冲区太小的话会明显影响真机上的通信速度
if(!
dataBuffer){
dataBuffer=[[NSMutableDataalloc]init];
}
actuallyRead=[inStreamread:
buffermaxLength:
sizeof(buffer)];
if(actuallyRead==-1){
[selfcloseStreams];
//ErrorControl
}elseif(actuallyRead==0){
//Dosomethingifyouwant
}else{
[dataBufferappendBytes:
bufferlength:
actuallyRead];
remainingToRead-=actuallyRead;
}
if(remainingToRead==0)
{
_isFirstFourBytes=YES;
[selfmanageData:
dataBuffer];//数据接收完毕,把数据送回调用sream的函数
[dataBufferrelease];
dataBuffer=nil;
}
}
break;
}
caseNSStreamEventEndEncountered:
//连接断开或结束
{
[selfcloseStreams];
break;
}
caseNSStreamEventErrorOccurred:
//无法连接或断开连接
{
if([[aStreamstreamError]code])//确定code不是0……有时候正常使用时会跳出code为0的错误,但其实一点问题都没有,可以继续使用,很奇怪……
{
[selfcloseStreams];
break;
}
}
caseNSStreamEventOpenCompleted:
{
_hasEstablished=YES;
break;
}
caseNSStreamEventHasSpaceAvailable:
{
break;
}
caseNSStreamEventNone:
default:
break;
}
}
//判断是否能连接到服务器。
这个函数用来判断网络是否连通还好,要真的判断服务器上对应的端口是否可以连接,不是很好用来着……
-(BOOL)isServerAvailable{
NSString*addressString=/*你的服务器地址,比如我公司地址[url][/url]*/;
if(!
addressString){
returnNO;
}
SCNetworkReachabilityRefdefaultRouteReachability=SCNetworkReachabilityCreateWithName(kCFAllocatorDefault,[addressStringUTF8String]);
SCNetworkReachabilityFlagsflags;
BOOLdidRetrieveFlags=SCNetworkReachabilityGetFlags(defaultRouteReachability,&flags);
CF
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 使用 NSStream 实现 Socket