在前面的系列網絡編程文章中,我們都是使用socket 自己實現客戶端和服務器端來互相發數據測試,現在嘗試使用socket 客戶端發
送http 請求給某個網站,然后接收網站的響應數據。http 協議參考 這里。
代碼如下:
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 |
#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,在阻塞模式下不等到指定數目的數據是不會返回的,除非超時時間到或者被信號打斷。但在這里我們並不知道對方會發來具體多少數據,所以不能使用這種方法來讀取數據,否則可能出現一直阻塞的情況。
注:在阻塞發送時,也有人喜歡設置發送超時,超時判斷返回值,如果沒有發送完整則繼續發送。但實際上本身阻塞發送會一直阻
塞到發送完整才返回,好像二者並無大的區別。