美文网首页
Socket详解《二》---使用socket请求百度首页数据

Socket详解《二》---使用socket请求百度首页数据

作者: 冰棍儿好烫嘴 | 来源:发表于2019-02-28 14:11 被阅读0次

    上一篇文章中介绍了使用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.1Host: 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的时候会进行重定向,所以不设置的话会请求失败。

    相关文章

      网友评论

          本文标题:Socket详解《二》---使用socket请求百度首页数据

          本文链接:https://www.haomeiwen.com/subject/sqchuqtx.html