iOS从零开始学习socket编程——HTTP1.0客户端
2015-04-16 21:51
435 查看
在开始socket编程之前,首先需要明确几个概念:
1.网络上的两个程序通过一个双向的通信连接实现数据的交换,这个连接的一端称为一个socket。
2.socket中文名为“套接字”,是基于TCP/IP协议通信机制。
3.客户端的socket连接需要指定主机的ip地址和端口,ip地址类似于家庭地址,用于唯一确认一台主机,而端口类似于门牌号,用于唯一确认主机上的某一个程序。
我们模拟一次HTTP的请求。首先在终端中输入
我们会得到这样的提示
这时候表示已经和服务器建立了socket连接,接下来就是传递参数。
输入:
两次回车后得到这样的数据
这表示已经请求成功,其中空白的那一行上面的是请求返回的头部,浏览器需要解析这段数据,对我们有用的是Content-Type字段和空白行下面的内容。Content-Type字段告诉我们接下来的数据应该以何种方式被保存。
HTTP协议是基于TCP/IP协议的,所以显然可以利用socket模拟一次HTTP请求。iOS提供了基于C的socket编程接口CFSocket以及输入输出流CFReadStreamRef、CFWriteStreamRef,但是其实现方法比较复杂,这里我们使用著名的AsyncSocket开源框架进行socket编程。使用AsyncSocket并不影响我们对socket的理解,同时也被QQ等知名软件用来实现即时通讯、文件传输等功能。
AsyncSocket下载地址:https://github.com/robbiehanson/CocoaAsyncSocket
下载后把RunLoop文件夹下的AsyncSocket.h和AsyncSocket.m拷贝到工程文件中即可使用。
需要说明的是,AsyncSocket基于Runloop,可以异步调用方法,并不需要多开一个线程。
我们新建一个SocketDemoViewController类,因为是简单的HTTP客户端,所以所有的操作、视图将会在这个类中实现。在这个简单的客户端中,我们将会模拟一个HTTP的访问,将返回头部显示在屏幕上,并且保存获取到的图片和html文件等。
HOST_IP和HOST_PORT我选择了东北大学首页www.neu.edu.cn。
我们创建了一个AsyncSocket类的对象client,两个textview用于显示输入和输出内容。connectServer方法与服务器进行连接,reConnect重新连接,sendMsg方法向服务器发送数据。
接下来是SocketDemoViewController.m的代码,有点长,慢慢分析。
运行程序后,只要在输入框中,输入之前命令行中的那一段代码(记得要换行,参见截图中光标的位置),即可模拟HTTP请求。
在onSocket:(AsyncSocket )sock didReadData:(NSData )data withTag:(long)tag方法中,我们根据Content-Type字段来选择处理数据的方式,并且将HTML文件保存在沙盒documents目录下的get.html方法中。讲可能存在的图片保存在本地相册中。
对于详细的didReadData解析,参见上一篇文章:《AsyncSocket didReadData函数详解》
/article/1477420.html
1.网络上的两个程序通过一个双向的通信连接实现数据的交换,这个连接的一端称为一个socket。
2.socket中文名为“套接字”,是基于TCP/IP协议通信机制。
3.客户端的socket连接需要指定主机的ip地址和端口,ip地址类似于家庭地址,用于唯一确认一台主机,而端口类似于门牌号,用于唯一确认主机上的某一个程序。
我们模拟一次HTTP的请求。首先在终端中输入
telnet 202.118.1.7 80
我们会得到这样的提示
Trying 202.118.1.7... Connected to www.neu.edu.cn. Escape character is '^]'.
这时候表示已经和服务器建立了socket连接,接下来就是传递参数。
输入:
GET / HTTP/1.0 HOST: www.neu.edu.cn
两次回车后得到这样的数据
HTTP/1.1 200 OK Date: Thu, 16 Apr 2015 13:58:33 GMT Content-Type: text/html Content-Length: 9710 Last-Modified: Thu, 16 Apr 2015 08:51:04 GMT Connection: close Vary: Accept-Encoding ETag: "552f77f8-25ee" Server: Apache/2.4.12 (FreeBSD) Accept-Ranges: bytes <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> …………
这表示已经请求成功,其中空白的那一行上面的是请求返回的头部,浏览器需要解析这段数据,对我们有用的是Content-Type字段和空白行下面的内容。Content-Type字段告诉我们接下来的数据应该以何种方式被保存。
HTTP协议是基于TCP/IP协议的,所以显然可以利用socket模拟一次HTTP请求。iOS提供了基于C的socket编程接口CFSocket以及输入输出流CFReadStreamRef、CFWriteStreamRef,但是其实现方法比较复杂,这里我们使用著名的AsyncSocket开源框架进行socket编程。使用AsyncSocket并不影响我们对socket的理解,同时也被QQ等知名软件用来实现即时通讯、文件传输等功能。
AsyncSocket下载地址:https://github.com/robbiehanson/CocoaAsyncSocket
下载后把RunLoop文件夹下的AsyncSocket.h和AsyncSocket.m拷贝到工程文件中即可使用。
需要说明的是,AsyncSocket基于Runloop,可以异步调用方法,并不需要多开一个线程。
我们新建一个SocketDemoViewController类,因为是简单的HTTP客户端,所以所有的操作、视图将会在这个类中实现。在这个简单的客户端中,我们将会模拟一个HTTP的访问,将返回头部显示在屏幕上,并且保存获取到的图片和html文件等。
//SocketDemoViewController.h #import <UIKit/UIKit.h> #import "AsyncSocket.h" #define HOST_IP @"202.118.1.7" #define HOST_PORT 80 @interface SocketDemoViewController : UIViewController <UITextViewDelegate> @property (nonatomic, strong) AsyncSocket *client; @property (nonatomic, strong) UITextView *inputMsg; @property (nonatomic, strong) UITextView *outputMsg; @property (nonatomic, strong) NSString *fileName; @property (nonatomic, strong) NSMutableData *receiveData; - (int) connectServer: (NSString *) hostIP port:(int) hostPort; - (void) sendMsg; - (void) reConnect;
HOST_IP和HOST_PORT我选择了东北大学首页www.neu.edu.cn。
我们创建了一个AsyncSocket类的对象client,两个textview用于显示输入和输出内容。connectServer方法与服务器进行连接,reConnect重新连接,sendMsg方法向服务器发送数据。
接下来是SocketDemoViewController.m的代码,有点长,慢慢分析。
#import "SocketDemoViewController.h" @implementation SocketDemoViewController @synthesize inputMsg, outputMsg,fileName,receiveData; @synthesize client; - (void)viewDidLoad { [super viewDidLoad]; //UI部分的实现 inputMsg = [[UITextView alloc] initWithFrame: CGRectMake( 10.0f, 40.0f, 355.0f, 100.0f)]; inputMsg.delegate = self; inputMsg.backgroundColor = [UIColor lightGrayColor]; [self.view addSubview: inputMsg]; outputMsg = [[UITextView alloc] initWithFrame: CGRectMake( 10.0f, 180.0f, 355.0f, 150.0f)]; outputMsg.textColor = [UIColor redColor]; outputMsg.backgroundColor = [UIColor lightGrayColor]; [self.view addSubview: outputMsg]; UIButton *btnSend = [UIButton buttonWithType: UIButtonTypeRoundedRect]; btnSend.frame = CGRectMake(150.0f, 350.0f, 75.0f, 30.0f); [btnSend setTitle: @"发送" forState: UIControlStateNormal]; [btnSend addTarget: self action: @selector(sendMsg) forControlEvents: UIControlEventTouchUpInside]; [self.view addSubview: btnSend]; UIButton *reConnect = [UIButton buttonWithType: UIButtonTypeRoundedRect]; reConnect.frame = CGRectMake(150.0f, 400.0f, 75.0f, 30.0f); [reConnect setTitle: @"reConnect" forState: UIControlStateNormal]; [reConnect addTarget: self action: @selector(reConnect) forControlEvents: UIControlEventTouchUpInside]; [self.view addSubview: reConnect]; //与服务器进行连接 [self connectServer:HOST_IP port:HOST_PORT]; } //链接server - (int) connectServer: (NSString *) hostIP port:(int) hostPort{ if (client == nil) { client = [[AsyncSocket alloc] initWithDelegate:self];//初始化client,记得设置代理 NSError *err = nil; if (![client connectToHost:hostIP onPort:hostPort error:&err]) { //连接失败 return 2; } else { NSLog(@"连接成功"); return 1; } } else { [client readDataWithTimeout:-1 tag:0]; return 0; } } - (void) reConnect{ int stat = [self connectServer:HOST_IP port:HOST_PORT]; } - (void)sendMsg{ //把inputMsg的内容发送给服务器 NSString *inputMsgStr = self.inputMsg.text; NSData *data = [inputMsgStr dataUsingEncoding:NSUTF8StringEncoding]; [client writeData:data withTimeout:-1 tag:0]; } #pragma mark - #pragma mark close Keyboard - (void)textFieldDidEndEditing:(UITextField *)textField { [inputMsg resignFirstResponder]; } - (BOOL)textFieldShouldReturn:(UITextField *)textField { [inputMsg resignFirstResponder]; return YES; } - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{ [inputMsg resignFirstResponder]; } #pragma mark socket delegate - (void)onSocket:(AsyncSocket *)sock didConnectToHost:(NSString *)host port:(UInt16)port{ //成功连接到服务器执行的回调函数 [client readDataWithTimeout:-1 tag:0]; } - (void)onSocketDidDisconnect:(AsyncSocket *)sock { client = nil;//置空client } - (void)onSocket:(AsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag { //收到数据的处理 if (receiveData == nil) { receiveData = [[NSMutableData alloc] init]; } [receiveData appendData:data]; NSString* aStr = [[NSString alloc] initWithData:receiveData encoding:NSUTF8StringEncoding]; NSRange endRange = [aStr rangeOfString:@"\r"]; if (endRange.location != NSNotFound) { NSRange range = [aStr rangeOfString:@"\r\n\r\n"]; if (range.location != NSNotFound) { NSString *requestHeader = [aStr substringToIndex:(unsigned long)range.location]; self.outputMsg.text = requestHeader; } else{ self.outputMsg.text = aStr; } //获取真正的请求到的数据 NSData *contentData = [self getContentDataWithData:data]; NSRange htmlRange = [aStr rangeOfString:@"text/html"]; if (htmlRange.location != NSNotFound) { NSArray *paths =NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask, YES); NSString *documentsDirectory =[paths objectAtIndex:0]; NSString *ducumentPlistPath = [documentsDirectory stringByAppendingPathComponent:@"get.html"];//plist文件位置 [[NSFileManager defaultManager] createFileAtPath:ducumentPlistPath contents:contentData attributes:nil]; } UIImageView *imgv = [[UIImageView alloc]initWithFrame:CGRectMake(0, 0, 50, 50)]; imgv.image = [UIImage imageWithData:contentData]; if (imgv.image != nil) { [self.view addSubview:imgv]; UIImageWriteToSavedPhotosAlbum(imgv.image, self, @selector(imageSavedToPhotosAlbum:didFinishSavingWithError:contextInfo:), nil); } receiveData = nil; } [client readDataWithTimeout:-1 tag:0]; } - (NSData *)getContentDataWithData:(NSData *)data{ //将数据块和返回头部区分开来,返回NSData类型的请求到的数据 NSString *toSearch = @"\r\n\r\n"; NSData *target = [toSearch dataUsingEncoding:NSUTF8StringEncoding]; NSRange doubleChangeLine = [data rangeOfData:target options:0 range:NSMakeRange(0, [data length])]; if (doubleChangeLine.location != NSNotFound) { NSData *content = [data subdataWithRange:NSMakeRange(doubleChangeLine.location + 4, [data length] - doubleChangeLine.location - 4)]; return content; } else{ return nil; } } - (void)imageSavedToPhotosAlbum:(UIImage *)image didFinishSavingWithError:(NSError *)error contextInfo:(void *)contextInfo{ //把image保存到本地相册中 if (!error) { NSLog(@"成功保存到相册"); } } @end
运行程序后,只要在输入框中,输入之前命令行中的那一段代码(记得要换行,参见截图中光标的位置),即可模拟HTTP请求。
在onSocket:(AsyncSocket )sock didReadData:(NSData )data withTag:(long)tag方法中,我们根据Content-Type字段来选择处理数据的方式,并且将HTML文件保存在沙盒documents目录下的get.html方法中。讲可能存在的图片保存在本地相册中。
对于详细的didReadData解析,参见上一篇文章:《AsyncSocket didReadData函数详解》
/article/1477420.html
相关文章推荐
- iOS从零开始学习socket编程——HTTP1.0服务器端
- iOS从零开始学习socket编程——高并发多线程服务器
- iOS网络编程实践--NSStream实现TCP Socket iPhone客户端
- iOS 各种网络编程总结--进程、线程、Socket、HTTP、TCP/IP、TCP和UDP
- iOS网络编程实践--NSStream实现TCP Socket iPhone客户端
- 从零开始学习Node.js例子七 发送HTTP客户端请求并显示响应结果
- iOS中的Socket编程,socket,http,https基础知识(二)
- ios网络编程(http、socket)
- Java Socket编程 - 基于Socket实现HTTP下载客户端
- ios网络编程(http、socket)
- IOS学习笔记之 Socket 编程
- iOS网络编程实践–NSStream实现TCP Socket iPhone客户端
- iOS网络编程实践--NSStream实现TCP Socket iPhone客户端
- 从零开始学习iOS开发-股票记帐本1.0(2)
- IOS学习之 网络编程(1)--HTTP协议
- iOS网络编程实践--NSStream实现TCP Socket iPhone客户端
- socket编程api学习二客户端的编程
- iOS网络编程实践--NSStream实现TCP Socket iPhone客户端
- iOS网络编程之Socket(客户端)
- ios网络编程(http、socket)