上一篇文章中介绍了使用socket和服务端进行通信的基本过程,本文使用socket实现开发中最基本的网络请求过程。属于最底层的网络请求,弄明白socket通信过程,对于网络理解会更加深刻
全部代码如下,上一篇文章中已经对基本方法做了详细的描述,本文着重描述如何使用socket实现网络请求。
#import "GetBaiDuSocketViewController.h"
#import <sys/socket.h>
#import <netinet/in.h>
#import <arpa/inet.h>
@interface GetBaiDuSocketViewController ()
@property (nonatomic,assign) int clientSocket;
@property (strong, nonatomic) UIWebView *webView;
@end
@implementation GetBaiDuSocketViewController
- (void)viewDidLoad {
[super viewDidLoad];
[self.view addSubview:self.webView];
BOOL result = [self connectWithIp:@"61.135.169.121" Host:80];
if (!result) {
NSLog(@"连接失败");
return;
}
NSLog(@"连接成功");
//http请求头
NSString * requestHeader = @"GET / HTTP/1.1\r\n"
"Host: www.baidu.com\r\n"
"Connection: close\r\n\r\n";
//接收到的数据包含相应头和响应体
NSString * response = [self sendAdnReceveMsgWithMsg:requestHeader];
//http协议要求,请求结束后要关闭连接
close(self.clientSocket);
//取出相应体 相应头结束的标示:\r\n\r\n
NSRange range = [response rangeOfString:@"\r\n\r\n"];
response = [response substringFromIndex:range.location+range.length];
[self.webView loadHTMLString:response baseURL:[NSURL URLWithString:@"http://www.baidu.com"]];
}
//建立连接
-(BOOL) connectWithIp:(NSString *)ip Host:(int)host{
//1创建socket
//第一个参数:domain 协议簇 制定IPv4或者IPv6
//第二个参数:Type socket类型 流socket 数据报socket
//第三个参数:协议类型 TCP/UDP
int clientSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
self.clientSocket = clientSocket;
//2连接服务器
//第一个参数:socket的描述符
//第二个参数:结构体 ip地址和端口
//第三个参数:结构体长度 sizeof()
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = inet_addr(ip.UTF8String);
//调整字节顺序,从大到小或者从小到大。要和后台和其他端一致
addr.sin_port = htons(host);
int result = connect(clientSocket, (const struct sockaddr *) &addr, sizeof(addr));
if (result != 0) {
NSLog(@"失败");
return NO;
}
NSLog(@"连接成功");
return YES;
}
//发送请求
-(NSString *)sendAdnReceveMsgWithMsg:(NSString *)msg{
//3.发送数据
/**
参数:
带一个参数:socket
第二个参数:要发送的数据的指针
第三个:数据的大小,字符数
第四个:是否要阻塞线程,一般填0
//返回值:成功则返回实际传送出去的字符数,失败返回-1,错误原因存于errno 中
*/
const char * str = msg.UTF8String;
ssize_t sendCount = send(self.clientSocket, str, strlen(str), 0);
NSLog(@"发送的字节数 %zd",sendCount);
//4.接受服务器返回的数据
/**
带一个参数:socket
第二个参数:接受的字节数组
第三个:数据的大小,字符数
第四个:是否要阻塞线程,一般填0
*/
NSMutableData * mData = [NSMutableData data];
uint8_t buffer[1024];
ssize_t recvCont = recv(self.clientSocket, buffer, sizeof(buffer), 0);
[mData appendBytes:buffer length:recvCont];
//服务器返回多次数据,做拼接将所有的数据拼接起来
while (recvCont != 0) {
recvCont = recv(self.clientSocket, buffer, sizeof(buffer), 0);
[mData appendBytes:buffer length:recvCont];
NSLog(@"接受到的字节数 %zd",recvCont);
}
NSLog(@"接受到的字节数 %zd",recvCont);
//把字节数组转换成字符串
// NSData * recvData = [NSData dataWithBytes:buffer length:recvCont];
NSString * recvMsg = [[NSString alloc]initWithData:mData.copy encoding:NSUTF8StringEncoding];
NSLog(@"收到的数据:%@",recvMsg);
return recvMsg;
}
-(UIWebView *)webView {
if(!_webView){
_webView = [[UIWebView alloc]initWithFrame:CGRectMake(0, 0, self.view.bounds.size.width, self.view.bounds.size.height)];
}
return _webView;
}
@end
需要注意的点:
1、使用socket请求数据,不能使用域名,必须使用IP(一般的网络框架会多一步将域名解析成IP的步骤,这里没有,所以只能写真实IP),端口号一般默认80
使用终端ping 域名就可以拿到对应的真实IP,如:
获取IP
2、向服务器请求数据,就是和服务器建立连接之后,向服务器发送请求信息(www.baidu.com&参数)但是在使用socket做请求的时候,我们需要将对应的请求头信息一并传过去。AFN等第三方将请求头信息封装之后默认传入,这一步不需要操作。
//http请求头
NSString * requestHeader = @"GET / HTTP/1.1\r\n"
"Host: www.baidu.com\r\n"
"Connection: Keep-Alive\r\n\r\n";
获取请求头信息,可以使用浏览器的监控功能,使用Chrome打开www.baidu.com 之后使用command+option+i 打开监控页面,选择nectwork中的Header response Header 中的信息
其中GET / HTTP/1.1
和 Host: www.baidu.com
为必填项。在拼接各个字段的时候需要在每个字段后面加上\r\n
最后一个字段加上两个\r\n
。最后的两个\r\n\r\n
之后会作为请求头结束标志使用
3、按上面的操作,就可以请求到对应的百度页面的信息,但是请求到的信息是包含响应头和相应体的,所以要在接收到的数据中用\r\n\r\n
进行截取,拿到对应的响应体
//取出相应体 相应头结束的标示:\r\n\r\n
NSRange range = [response rangeOfString:@"\r\n\r\n"];
response = [response substringFromIndex:range.location+range.length];
4、关于baseUrl:
[self.webView loadHTMLString:response baseURL:[NSURL URLWithString:@"http://www.baidu.com"]];
这里的baseUrl我们在一般情况下是写nil的,但是如果是一个网页数据,请求到的只是html数据,对应的图片资源和css等有时候是不会都返回,所以要协商baseUrl,表明这些资源的地址
5、记得在plist文件中设置https白名单字段,应为在请求http://www.baidu.com的时候会进行重定向,所以不设置的话会请求失败。
网友评论