SOCKET模擬HTTP請求

ForTechnology發表於2011-08-14
Normal 0 7.8 磅 0 2 false false false MicrosoftInternetExplorer4 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

 

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/25897606/viewspace-704825/,如需轉載,請註明出處,否則將追究法律責任。

相關文章