最近研究了一下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;
}