使用AsyncSocket实现RTSP协议
之前Socket一直使用Linux c来实现,优点是很容易掌握socket的通信流程,缺点是过程比较繁琐。
所以选择了AsyncSocket来实现socket通信。另外实现了RTSP的通信过程,并在play命令之后,
再次开始一个UDP会话,用于传输数据。
项目原型是用RTSP+RTP来实现摄像头的实时监控,RTSP使用TCP来实现,RTP使用UDP实现,
直接上代码吧!
工程结构截图如下:
关于怎么在项目中使用AysncSocket,请查看http://www.cnblogs.com/foxmin/archive/2012/03/11/2389734.html
RTSPClient.h如下:
1 //
2 // ViewController.m
3 // RTSPClient
4 //
5 // Created by Fox on 12-3-11.
6 // Copyright (c) 2012年 __MyCompanyName__. All rights reserved.
7 //
8
9 #import "RTSPClient.h"
10 #import "AsyncSocket.h"
11 #import "AsyncUdpSocket.h"
12
13
14
15 #define KENTER @"\r\n"
16 #define KBLACK @" "
17 #define MAX_BUF 10240
18
19
20 static NSString* SERVERADDR = @"172.16.108.200";
21 static const int SERVERPORT = 554;
22
23
24 @interface RTSPClient ()
25
26 @end
27
28 @implementation RTSPClient
29
30 - (void)viewDidLoad
31 {
32 [super viewDidLoad];
33 // Do any additional setup after loading the view, typically from a nib.
34
35
36 socket = [[AsyncSocket alloc] initWithDelegate:self];
37
38 [socket connectToHost:@"172.16.108.200" onPort:554 error:nil];
39 [self getOptions];
40
41
42 }
43
44 - (void)viewDidUnload
45 {
46 [super viewDidUnload];
47 // Release any retained subviews of the main view.
48 }
49
50 - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
51 {
52 return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown);
53 }
54
55
56
57
58
59
60 #pragma mark RTSP Session
61 /*
62 *建立连接,返回服务器对OPTIONS请求的响应
63 */
64 -(NSMutableString*)getOptions
65 {
66
67 NSMutableString* options = [[NSMutableString alloc] init];
68
69 [options appendFormat:@"OPTIONS rtsp://%@:%d/H264 RTSP/1.0%@",SERVERADDR,SERVERPORT,KENTER];
70 [options appendFormat:@"CSeq: 2%@",KENTER];
71 [options appendFormat:@"User-Agent: LibVLC/1.1.11 (LIVE555 Streaming Media v2011.05.25)%@%@",KENTER,KENTER];
72
73
74 [socket writeData:[options dataUsingEncoding:NSUTF8StringEncoding] withTimeout:3 tag:1];
75 NSMutableString* readString = [[NSMutableString alloc] init];
76 [socket readDataWithTimeout:3 tag:1];
77
78 return readString;
79 }
80
81 /*
82 *建立连接,返回服务器对DESCRIBE请求的响应
83 */
84 -(NSMutableString*)getDescribe
85 {
86
87 NSMutableString* describe = [[NSMutableString alloc] init];
88
89 [describe appendFormat:@"DESCRIBE rtsp://%@:%d/H264 RTSP/1.0%@",SERVERADDR,SERVERPORT,KENTER];
90 [describe appendFormat:@"CSeq: 3%@",KENTER];
91 [describe appendFormat:@"Authorization: Basic YWRtaW4=%@",KENTER];
92 [describe appendFormat:@"User-Agent: LibVLC/1.1.11 (LIVE555 Streaming Media v2011.05.25)%@",KENTER];
93 [describe appendFormat:@"Accept: application/sdp%@%@",KENTER,KENTER];
94
95
96
97 [socket writeData:[describe dataUsingEncoding:NSUTF8StringEncoding] withTimeout:3 tag:1];
98 NSMutableString* readString = [[NSMutableString alloc] init];
99 [socket readDataWithTimeout:3 tag:2];
100
101
102 return readString;
103 }
104
105 /*
106 *建立连接,返回通过tcp连接服务器对SETUP请求的响应
107 */
108 -(NSMutableString*)getTcpSetup
109 {
110
111
112 NSMutableString* tcpsetup = [[NSMutableString alloc] init];
113
114 [tcpsetup appendFormat:@"SETUP rtsp://%@:%d/H264/track1 RTSP/1.0%@",SERVERADDR,SERVERPORT,KENTER];
115 [tcpsetup appendFormat:@"CSeq: 4%@",KENTER];
116
117 [tcpsetup appendFormat:@"Authorization: Basic YWRtaW4=%@",KENTER];
118 [tcpsetup appendFormat:@"User-Agent: LibVLC/1.1.11 (LIVE555 Streaming Media v2011.05.25)%@",KENTER];
119 [tcpsetup appendFormat:@"Transport: RTP/AVP/TCP;unicast;interleaved=0-1%@%@",KENTER,KENTER];
120
121
122
123 [socket writeData:[tcpsetup dataUsingEncoding:NSUTF8StringEncoding] withTimeout:3 tag:1];
124 NSMutableString* readString = [[NSMutableString alloc] init];
125 [socket readDataWithTimeout:3 tag:3];
126
127
128 return readString;
129
130 }
131
132 /*
133 *建立连接,返回通过udp连接服务器对SETUP请求的响应
134 */
135 -(NSMutableString*)getUdpSetup
136 {
137
138 NSMutableString* udpsetup = [[NSMutableString alloc] init];
139
140 [udpsetup appendFormat:@"SETUP rtsp://%@/H264/track1 RTSP/1.0%@",SERVERADDR,KENTER];
141 [udpsetup appendFormat:@"CSeq: 4%@",KENTER];
142 [udpsetup appendFormat:@"Authorization: Basic YWRtaW4=%@",KENTER];
143 [udpsetup appendFormat:@"User-Agent: LibVLC/1.1.11 (LIVE555 Streaming Media v2011.05.25)%@",KENTER];
144 [udpsetup appendFormat:@"Transport: RTP/AVP;unicast;client_port=7777-7778%@%@",KENTER,KENTER];
145
146
147
148 [socket writeData:[udpsetup dataUsingEncoding:NSUTF8StringEncoding] withTimeout:3 tag:1];
149 NSMutableString* readString = [[NSMutableString alloc] init];
150 [socket readDataWithTimeout:3 tag:3];
151
152
153 return readString;
154
155 }
156
157 /*
158 *建立连接,返回向服务器对PLAY请求的响应
159 */
160 -(NSMutableString*)getPlay:(NSString*)session
161 {
162
163 NSMutableString* play = [[NSMutableString alloc] init];
164
165 [play appendFormat:@"PLAY rtsp://%@/H264 RTSP/1.0%@",SERVERADDR,KENTER];
166 [play appendFormat:@"CSeq: 5%@",KENTER];
167 [play appendFormat:@"Range: npt=0.000-%@",KENTER];
168 [play appendFormat:@"Session:%@%@",session,KENTER];
169 [play appendFormat:@"User-Agent: LibVLC/1.1.11 (LIVE555 Streaming Media v2011.05.25)%@%@",KENTER,KENTER];
170
171
172
173 [socket writeData:[play dataUsingEncoding:NSUTF8StringEncoding] withTimeout:3 tag:1];
174 NSMutableString* readString = [[NSMutableString alloc] init];
175 [socket readDataWithTimeout:3 tag:4];
176
177
178 return readString;
179
180 }
181
182
183
184 /*
185 *建立连接,返回向服务器对TEARDOWN请求的响应
186 */
187 -(NSMutableString*)getTeardown:(NSString*)session
188 {
189
190 NSMutableString* teardown = [[NSMutableString alloc] init];
191
192 [teardown appendFormat:@"TEARDOWN rtsp://%@:%d/H264 RTSP/1.0%@",SERVERADDR,SERVERPORT,KENTER];
193 [teardown appendFormat:@"CSeq: 5%@",KENTER];
194 [teardown appendFormat:@"Session:%@%@",session,KENTER];
195 [teardown appendFormat:@"User-Agent: LibVLC/1.1.11 (LIVE555 Streaming Media v2011.05.25)%@%@",KENTER,KENTER];
196
197
198 [socket writeData:[teardown dataUsingEncoding:NSUTF8StringEncoding] withTimeout:3 tag:1];
199 NSMutableString* readString = [[NSMutableString alloc] init];
200 [socket readDataWithTimeout:3 tag:5];
201 return readString;
202
203 }
204
205 /*
206 *返回服务器地址
207 */
208 -(NSString*)getServeraddr
209 {
210 return SERVERADDR;
211 }
212
213 /*
214 *返回RTSP回话标识,info为客户端向服务器发送setup请求时的响应信息
215 */
216 -(NSString*)getSession:(NSString*)info
217 {
218
219 //从文件中再一行一行读取数据,将包含session的一行保留出来
220 NSString *tmp;
221 NSArray *lines; /*将文件转化为一行一行的*/
222 lines = [info componentsSeparatedByString:@"\r\n"];
223
224 NSEnumerator *nse = [lines objectEnumerator];
225 NSString* session = [[NSString alloc] init];
226 while(tmp = [nse nextObject]) {
227 if ([tmp hasPrefix:@"Session:"]) {
228 //tmp为包含session那一行的
229 NSInteger len = @"Session:".length;
230 session = [[tmp substringFromIndex:len] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
231 NSLog(@"Session:%@\n",session);
232 break;
233 }
234 }
235
236 return session;
237
238 }
239
240 /*
241 *返回RTSP回话的Content-Base
242 */
243 -(NSString*)getContentBase:(NSString*)info
244 {
245 //info为Describe请求所响应的信息
246 NSString *tmp;
247 NSArray *lines; /*将文件转化为一行一行的*/
248 lines = [info componentsSeparatedByString:@"\r\n"];
249
250 NSEnumerator *nse = [lines objectEnumerator];
251 NSString* contentbase = [[NSString alloc] init];
252 while(tmp = [nse nextObject]) {
253 if ([tmp hasPrefix:@"Content-Base:"]) {
254 //tmp为包含session那一行的
255 NSInteger len = @"Content-Base:".length;
256 contentbase = [[info substringFromIndex:len] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
257 NSLog(@"Content-Base:%@\n",contentbase);
258 break;
259 }
260 }
261
262 return contentbase;
263
264 }
265
266
267 /*
268 *返回RTSP回话的Content-Type
269 */
270 -(NSString*)getContentType:(NSString*)info
271 {
272
273 //info为Describe请求所响应的信息
274 NSString *tmp;
275 NSArray *lines; /*将文件转化为一行一行的*/
276 lines = [info componentsSeparatedByString:@"\r\n"];
277
278 NSEnumerator *nse = [lines objectEnumerator];
279 NSString* contenttype = [[NSString alloc] init];
280 while(tmp = [nse nextObject]) {
281 if ([tmp hasPrefix:@"Content-Type:"]) {
282 //tmp为包含session那一行的
283 NSInteger len = @"Content-Type:".length;
284 contenttype = [[info substringFromIndex:len] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
285 NSLog(@"CContent-Type:%@\n",contenttype);
286 break;
287 }
288 }
289
290 return contenttype;
291
292 }
293
294
295
296 /*
297 *返回RTSP回话的contentlength
298 */
299 -(NSString*)getContentLength:(NSString*)info
300 {
301
302 //info为Describe请求所响应的信息
303 NSString *tmp;
304 NSArray *lines; /*将文件转化为一行一行的*/
305 lines = [info componentsSeparatedByString:@"\r\n"];
306
307 NSEnumerator *nse = [lines objectEnumerator];
308 NSString* contentlength = [[NSString alloc] init];
309 while(tmp = [nse nextObject]) {
310 if ([tmp hasPrefix:@"Content-Type:"]) {
311 //tmp为包含session那一行的
312 NSInteger len = @"Content-Type:".length;
313 contentlength = [[info substringFromIndex:len] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
314 NSLog(@"CContent-Type:%@\n",contentlength);
315 break;
316 }
317 }
318 return contentlength;
319 }
320
321 /*
322 *本机作为客户端连接接受服务器发送的消息
323 */
324 -(void)RecvUDPData{
325
326 socketudp = [[AsyncUdpSocket alloc] initWithDelegate:self];
327
328 [socketudp bindToPort:7777 error:nil];
329 [socketudp enableBroadcast:YES error:nil];//设置为广播
330
331 if ([socketudp connectToHost:@"172.16.108.200" onPort:6970 error:nil]) {
332 [socketudp receiveWithTimeout:-1 tag:1];//将不断接受摄像头发送的数据
333 }
334
335 }
336
337 #pragma mark AsyncSocketDelegate
338 /*
339 *通过主机名和端口号连接服务器
340 */
341 - (void)onSocket:(AsyncSocket *)sock didConnectToHost:(NSString *)host port:(UInt16)port{
342 NSLog(@"did connect to host");
343 }
344 /*
345 *向内存中写入数据
346 */
347 - (void)onSocket:(AsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag{
348 NSString *message;
349 NSString* info = [[NSString alloc] init];
350 switch (tag) {
351 case 1:
352 NSLog(@"getOptions:did read data");
353 message = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
354 NSLog(@"message is: \n%@",message);
355 [self getDescribe];
356 break;
357 case 2:
358 NSLog(@"getDescribe:did read data");
359 message = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
360 NSLog(@"message is: \n%@",message);
361 info = [self getUdpSetup];
362 break;
363 case 3:
364 NSLog(@"getUdpSetup:did read data");
365 message = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
366 NSLog(@"message is: \n%@",message);
367 NSString* session = [self getSession:info];
368 [self getPlay:session];
369 break;
370 case 4:
371 NSLog(@"getPlay:did read data");
372 message = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
373 NSLog(@"message is: \n%@",message);
374 [self RecvUDPData];
375 break;
376 case 5:
377 NSLog(@"getTeardown:did read data");
378 message = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
379 NSLog(@"message is: \n%@",message);
380 break;
381 default:
382 break;
383 }
384
385
386
387 }
388
389
390
391
392 #pragma mark AsyncUdpSocketDelegate
393 - (BOOL)onUdpSocket:(AsyncUdpSocket *)sock didReceiveData:(NSData *)data withTag:(long)tag fromHost:(NSString *)host port:(UInt16)port
394 {
395 NSLog(@"333333333");
396 NSString *theLine=[[NSString alloc] initWithData:data encoding:NSASCIIStringEncoding]; //Convert the UDP data to an NSString
397
398 NSLog(@"%@", theLine);
399
400 [theLine release];
401
402 [socketudp receiveWithTimeout:-1 tag:1];
403
404
405 return YES;
406
407 }
408
409
410 @end
建立UDP会话后,摄像头开始向客户端发送RTP数据包,多包构成一帧数据,然后进行解码显示。