一个简单的http下载程序

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

}

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值