socket 请求接收完整的一个http响应(设置recv 接收超时选项SO_RCVTIMEO)

在前面的系列网络编程文章中,我们都是使用socket 自己实现客户端和服务器端来互相发数据测试,现在尝试使用socket 客户端发

送http 请求给某个网站,然后接收网站的响应数据。http 协议参考 这里

代码如下:

#include<stdio.h>
#include<stdlib.h>
#include<string.h>    //strlen
#include<sys/socket.h>
#include<arpa/inet.h> //inet_addr
#include<netdb.h>
#include<errno.h>

int main(int argc , char *argv[])
{
    int socket_desc;
    struct sockaddr_in server;
    char *message;

    //Create socket
    socket_desc = socket(AF_INET , SOCK_STREAM , 0);
    if (socket_desc == -1)
    {
        printf("Could not create socket");
    }

    char ip[20] = {0};
    char *hostname = "www.google.com.hk";
    struct hostent *hp;
    if ((hp = gethostbyname(hostname)) == NULL)
        return 1;
    //  #define h_addr h_addr_list[0]
    strcpy(ip, inet_ntoa(*(struct in_addr *)hp->h_addr_list[0]));

    server.sin_addr.s_addr = inet_addr(ip);
    server.sin_family = AF_INET;
    server.sin_port = htons( 80 );


    //Connect to remote server
    if (connect(socket_desc , (struct sockaddr *)&server , sizeof(server)) < 0)
    {
        puts("connect error");
        return 1;
    }

    puts("Connected\n");

    //Send some data
    message = "GET /?st=1 HTTP/1.1\r\nHost: www.google.com.hk\r\n\r\n";
    if( send(socket_desc , message , strlen(message) , 0) < 0)
    {
        puts("Send failed");
        return 1;
    }
    puts("Data Send\n");

    struct timeval timeout = {3, 0};
    setsockopt(socket_desc, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout, sizeof(struct timeval));

    //Receive a reply from the server
    //loop
    int size_recv , total_size = 0;
    char chunk[512];
    while(1)
    {
        memset(chunk , 0 , 512); //clear the variable
        if((size_recv =  recv(socket_desc , chunk , 512 , 0) ) == -1)
        {
            if (errno == EWOULDBLOCK || errno == EAGAIN)
            {
                printf("recv timeout ...\n");
                break;
            }
            else if (errno == EINTR)
            {
                printf("interrupt by signal...\n");
                continue;
            }
            else if (errno == ENOENT)
            {
                printf("recv RST segement...\n");
                break;
            }
            else
            {
                printf("unknown error!\n");
                exit(1);
            }
        }
        else if (size_recv == 0)
        {
            printf("peer closed ...\n");
            break;
        }
        else
        {
            total_size += size_recv;
            printf("%s" , chunk);
        }
    }

    printf("Reply received, total_size = %d bytes\n", total_size);

    return 0;
}

输出如下:

.............................省略................................

从上面的输出可以看到有完整的<html> </html> ,即已经完整接收,但有一点不解的是为什么最后会接收到一个0?

Chunked transfer encoding uses a chunk size of 0 to mark the end of the content.

程序中  struct timeval timeout = {3,0}; 

setsockopt(socket_desc, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout, sizeof(struct timeval));

设置超时时间为3s,现在recv 为阻塞接收,如果超时时间内接收缓冲区没有一点数据,则返回-1 且errno = EWOULDBLOCK 。

退出循环,程序结束。

在这里顺便提一下,recv的第四个参数如果设置为MSG_WAITALL,在阻塞模式下不等到指定数目的数据是不会返回的,除非超时时间到或者被信号打断。但在这里我们并不知道对方会发来具体多少数据,所以不能使用这种方法来读取数据,否则可能出现一直阻塞的情况。

注:在阻塞发送时,也有人喜欢设置发送超时,超时判断返回值,如果没有发送完整则继续发送。但实际上本身阻塞发送会一直阻

塞到发送完整才返回,好像二者并无大的区别。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏开发与安全

linux系统编程之基础必备(一):计算机体系结构一点基础知识

下面这张图来自《深入理解计算机系统》: IO桥部分一般还分为北桥和南桥,北桥当然是靠上的了。 ? •CPU –主频:  CPU的时钟频率,内核工作的时钟频...

1845
来自专栏JAVA高级架构

JVM内存管理--计算机内存和Java内存组件

JVM一向很好的帮我们管理内存,它就是一个贤内助:“向政府(内存空间)能要到地盘,还能有效的对自己的一亩三分地进行管理。”但是有时候呢,我们不懂怜香惜玉的一而再...

35414
来自专栏java相关

Memcached相关内容总结

Memcached使用Slab Allocator机制分配和管理内存,这种分配机制可以减少内存碎片的产生,减轻系统管理内存的负担。

863
来自专栏熊二哥

服务保障框架-Hystrix快速入门

祝大家国庆快乐! 对大部分电商和快递公司来说,每年年底(Q4季度)由于双11等大促活动的存在,将面对大量的用户流量,尤其是属于大促的那几天,无论是用户的商品订...

681
来自专栏Java技术交流群809340374

史上最全Java面试266题:算法+缓存+TCP+JVM+搜索+分布式+数据库

490
来自专栏木可大大

漫谈虚拟内存

如上图,程序1、程序2、程序3装入到内存,而程序2运行完成被换出,内存空闲出20k,然后进来程序4,大小为25K,此时,只有两处空闲块,10K和20K,没有一处...

4794
来自专栏Golang语言社区

go语言 runtime

golang写在前面 golang由于其能够支持大量并发运算的特性而广泛地应用于云计算领域。 本文对golang的底层进行一个简单的分析与说明。 golang ...

3325
来自专栏coding

2018年python3与selenium教程第3节切换Frame延时等待

get()方法会在网页框架加载完成后结束执行,此时的网页源码可能并不完整,如有些是ajax获取的数据,需要延时等待才能获取到

1102
来自专栏个人分享

Spark Job的提交与task本地化分析(源码阅读八)

  我们又都知道,Spark中任务的处理也要考虑数据的本地性(locality),Spark目前支持PROCESS_LOCAL(本地进程)、NODE_LOCAL...

682
来自专栏Spark学习技巧

Kafka源码系列之kafka如何实现高性能读写的

本文依然是以kafka 0.8.2.2的源码为例进行讲解。 一,kafka高性能的原因 Kafka吞吐量是大家公认的高,那么这是为什么呢?个人总结为以下三点: ...

7577

扫码关注云+社区