最近研究了一下http协议,用C++在linux下写了一个简单的http下载程序,程序的功能很简单,在终端输入文件所在网址,程序会单线程从服务器上下载该文件到本地。程序里面涉及了socket编程、http协议、二进制文件的写入。有兴趣的兄弟可以随便看看,玩具而已,大家一起玩玩了。
对于Http协议不是很了解的兄弟,可以去http://biz.chinabyte.com/209/2151709.shtml 看看,对于初学者,这篇文章是相当不错的。
所有源码可以到http://www.kunxu.com/kun/download/code/C/http_client.rar 下载
下面贴出来的是文件的源码:
CSocket.h:
#ifndef __CSOCKET_H__ #define __CSOCKET_H__
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <stdarg.h> #include <string.h> #include <errno.h> #include <netdb.h> #include <fcntl.h> #include <sys/time.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <string> #include <iostream>
using namespace std;
typedef enum socket_type { SERVER_TYPE = 0, CLIENT_TYPE } s_type; typedef enum socket_state{ CONN_ERROR = -1, CONN_OK = 0, CONN_WAIT = 1 } s_state;
class Sock_instance { public: Sock_instance(string hostname, unsigned port, s_type type); ~Sock_instance(); bool Connect(); int fd(); s_state state(); bool Send(string msg); int Receive(); void Close(); unsigned char* data() const; int datalen() const; string http_head() const; private: string _hostname; unsigned _port; int _fd; s_state _state; s_type _type; unsigned char *_data; int _datalen; string _http_head; };
#endif
|
CSocket.cpp:
#include "CSocket.h"
Sock_instance::Sock_instance(string hostname, unsigned port, s_type type) : _hostname( hostname ), _port( port ), _fd( 0 ), _state( CONN_ERROR ), _type( type ) { _datalen = 0; _data = new unsigned char[1024*1024]; }
Sock_instance::~Sock_instance() { delete[] _data; }
bool Sock_instance::Connect() { if( _type == SERVER_TYPE ) { cout << "The socket is a server, don't use conncet!" << endl; return false; }
struct sockaddr_in peer; int fd;
bzero( &peer, sizeof(peer) ); peer.sin_family = AF_INET; struct hostent *hp = gethostbyname( _hostname.c_str() ); if ( hp == NULL ) { cout << "unknow host: " << _hostname << endl; return false; } peer.sin_addr = *( ( struct in_addr * )hp->h_addr ); peer.sin_port = htons(_port);
cout << "conncet to " << inet_ntoa(peer.sin_addr) << ": " << ntohs( peer.sin_port ) << endl;
fd = socket( AF_INET, SOCK_STREAM, 0 ); if ( fd < 0 ) { cout << "socket call failed" << endl; return false; }
if( connect( fd, (struct sockaddr *)&peer, sizeof( peer ) ) ) { cout << errno << "connect failed" << endl; _state = CONN_ERROR; close( fd ); return false; }
_state = CONN_OK; _fd = fd;
return true; }
s_state Sock_instance::state() { return _state; }
int Sock_instance::fd() { return _fd; }
bool Sock_instance::Send(string msg) { if( state() != CONN_OK ) { cout << "the socket is not ok" << endl; return false; }
int rc;
if((rc = send(_fd, msg.c_str(), msg.size(), 0)) == -1) { if((errno != EWOULDBLOCK) && (errno != EAGAIN)) { _state = CONN_ERROR; return false; } } return true; }
int Sock_instance::Receive() { int rc; char buf[BUFSIZ]; char *p_buf = buf;
static bool b_readhead = true;
bzero(buf, BUFSIZ);
if((rc = recv( _fd, buf, BUFSIZ - 1, 0 )) < 0 ) { cout << "recive error" << endl; _state = CONN_WAIT; Close(); } else if( rc == 0 ) { cout << "server teminated" << endl; _state = CONN_WAIT; Close(); } else { //read HTTP head int ix = 0; while( b_readhead ) { // 2 0D 0A just for head end if( ix >= rc ) break; if( buf[ix] == 13 && buf[ix+1] == 10 && buf[ix+2] == 13 && buf[ix+3] == 10 ) { b_readhead = false; char *p = new char[ix+5]; memset( p, 0, ix+5 ); memcpy( p, buf, ix+5 ); p_buf += ix + 5; _http_head = p; delete[] p; cout << _http_head << endl; break; } ix ++; }
//copy data to _data if( ix != 0 && ix < rc ) { //this buf has head so data begin with buf+ix+4 memcpy( _data+_datalen, buf+ix+4, rc-ix-4 ); _datalen += rc-ix-4; } else { memcpy( _data+_datalen, buf, rc ); _datalen += rc; } }
return rc; }
inline void Sock_instance::Close() { close( _fd ); }
//string Sock_instance::data() const unsigned char * Sock_instance::data() const { return _data; }
int Sock_instance::datalen() const { return _datalen; }
string Sock_instance::http_head() const { return _http_head; }
|
http_main.cpp:
#include "CSocket.h" #include <fstream>
int main() { string word; string filename; string hostname; int pos1 = 0; int pos2 = 0;
cout << "enter the host name" << endl; cin >> word;
//bulid the query for http string quest = "GET "; quest += word; quest += " HTTP/1.0/r/n"; quest += "User-agent:Mozilla/4.0/r/n"; quest += "Accept-language:zh-cn/r/n/r/n";
//get the hostname and filename from the word string str_http = "http://"; pos1 = word.find_first_of (str_http, 0); pos2 = word.find_first_of ("/", pos1+7); hostname = word.substr( pos1+7, pos2-pos1-7 ); pos1 = word.find_last_of( "/", word.size() ); filename = word.substr( pos1+1, word.size()-pos1-1 );
cout << "filename: " << filename << endl; cout << "hostname: " << hostname << endl;
//use the hostname and port 80 to connect Sock_instance s_client( hostname, 80, CLIENT_TYPE ); if( !s_client.Connect() ) { cout << "connect error" << endl; return -1; }
//send the http query to the host cout << "connect is ok!" << endl; if( !s_client.Send(quest) ) { cout << "send is error!" << endl; return -1; }
//recieve all the file from the hsot while( s_client.Receive() > 0 ) { }
//write the binary data which is recieved on the file FILE *fp = fopen( filename.c_str(), "wb" ); fwrite( s_client.data(), sizeof( unsigned char ), s_client.datalen(), fp ); fclose( fp ); return 0; } |