`
memorymyann
  • 浏览: 266736 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

24.SOCKET模拟HTTP请求

阅读更多

HTTP请求头部样例:

GET http://www.baidu.com/ HTTP/1.1

Accept: html/text

Host: 220.181.6.175:80

Connection: Close

 

这是一个请求百度页面的头部。

属性和值的命名中间用:和空格隔开,结尾使用\r\n,头部结束使用\r\n\r\n

GET表示采用GET方法,当然我们常见的还有POST等其他方法,具体每个方法的意义可以查看RFC文档(附件)。

http://www.baidu.com/请求URL的绝对地址,如果使用相对地址可以改为/或者/index.html.注:后面的/不能少。

HTTP/1.1 版本号

Accept 接受响应的类型

Host请求的主机地址和端口

Connection:如果值为close则告诉服务器,当本次数据传递完毕以后,就会断开TCP链接。如果值为Keep-Alive则告诉服务器,数据传输结束后,本次链接不断开,等待后续请求。

 

 

用SOCKET模拟递交HTTP请求步骤:

1.首先建立和HTTP服务器的TCP链接

2.组织HTTP请求

3.发送请求

4.获取响应

 

一个下载百度首页的例子:

#include "stdlib.h"
#include "sys/types.h"
#include "sys/socket.h"
#include "netinet/in.h"
#include "netdb.h"
#include "string.h"
#include "arpa/inet.h"
#include "ctype.h"
#include "stdio.h"
#include "sys/stat.h"
#include "fcntl.h"

void send_and_recv(int sockfd, char * url, char * fun_type, char * accept_type, char * ip, int port, char * file_loc, char * body, char * connection_type);

//sockfd表示TCP链接的套接字,url请求服务的相对或者绝对地址,fun_type请求方法,accept_type接受类型,ip,port请求的服务器的地址和端口,file_loc下载文件存放位置,body请求的主体,connection_type用来指定connection的类型

int main() {
        int sockfd;
        struct sockaddr_in serv_socket;
        int port = 80;
        char ip[] = "220.181.6.175"; //ip地址,可以通过gethostbyname来获取
        char file_loc[] = "/programe/http/temp.html"; //下载的存放位置

        bzero(&serv_socket, sizeof(struct sockaddr_in));
        serv_socket.sin_family = AF_INET;
        serv_socket.sin_port = htons(port);
        inet_pton(AF_INET, ip, &serv_socket.sin_addr);

        sockfd = socket(AF_INET, SOCK_STREAM, 0);      

        int flag = connect(sockfd, (struct sockaddr *)&serv_socket, sizeof(serv_socket)); //建立和HTTP服务器的TCP链接
        if(flag < 0) {
                printf("connect error!!! flag = %d\n", flag);
                exit(1);
        }

        send_and_recv(sockfd, "http://www.baidu.com/", "GET", "html/text", ip, port, file_loc, NULL, "Close"); //下载的主体函数
        close(sockfd);
        exit(0);
}

void send_and_recv(int sockfd, char * url, char * fun_type, char * accept_type, char * ip, int port, char * file_loc, char * body, char * connection_type) {
        char * request = (char *) malloc (4 * 1024 * sizeof(char));
        if(body)
                sprintf(request, "%s %s HTTP/1.1\r\nAccept: %s\r\nHost: %s:%d\r\nConnection: %s\r\nContent-Type: application/x-www-form-urlencoded\r\nContent-L
ength: %d\r\n\r\n%s", fun_type, url, accept_type, ip, port, connection_type, body, strlen(body));
        else
                sprintf(request, "%s %s HTTP/1.1\r\nAccept: %s\r\nHost: %s:%d\r\nConnection: %s\r\n\r\n", fun_type, url, accept_type, ip, port, connection_type
);

//以上是在组织请求的头部,打印出的结果就是文章开头所写

        int send = write(sockfd, request, strlen(request));
        printf("%s", request);
        free(request);
        char * response = (char *) malloc (1024 * sizeof(char));
        if(file_loc) {
                int file = open(file_loc, O_RDWR | O_APPEND);
                int length;
                do {
                        length = read(sockfd, response, 1024);
                        char * loc = strstr(response, "\r\n\r\n"); //截获返回头部,以\r\n\r\n为标识
                        if(loc) {
                                int loci = loc - response + 4;
                                write(1, response, loci);//如果是响应头部就打印至屏幕
                                write(file, loc, length - loci);//如果是响应主体就写入文件
                        } else {
                                write(file, response, length);
                        }
                        if(!length)//注意,因为之前采用的是close方法,也就是说一旦传输数据完毕,则服务器端会断开链接,则read函数会返回0,所以这里会退出循环。如果采用的是Keep-Alive则服务器不关闭TCP链接,也就说程序将会被阻塞在read函数中,因此要注意的是自己判断是否读到了响应的结尾,然后在再次调用read之前退出循环。
                                break;
                } while(1);
                close(file);
        } else {
                int length;
                do {
                        length = read(sockfd, response, 1024);
                        printf("%s", response);
                        if(!length)
                                break;
                } while(1);
        }
        free(response);
}

 

之前的头部比较简单,在发送请求的时候,我们常常会递交表单,如果采用GET方法,则可以通过URL传递参数。如果采用POST,则新的HTTP请求看上去应该是这样。(带COOKIE)

 

POST http://192.168.1.154:8888/httpstudy2/servlet/IndexServlet HTTP/1.1
Accept: html/text
Host: 192.168.1.154:8888

Cookie: username=difa; password=yuna
Connection: Close
Content-Type: application/x-www-form-urlencoded
Content-Length: 29

username=hello&password=world

 

Content-Type表示主体类型

Content-Length表示主体长度,不包括头部。

 

整个发送的HTTP请求应该是:

POST http://192.168.1.154:8888/httpstudy2/servlet/IndexServlet HTTP/1.1\r\nAccept: html/text\r\nHost: 192.168.1.154:8888\r\nCookie: username=difa; password=yuna\r\nConnection: Close\r\nContent-Type: application/x-www-form-urlencoded\r\nContent-Length: 29\r\n\r\nusername=hello&password=world

 

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics