原创  一个简单的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;

}

 

 

发表于 @ 2005年10月31日 11:36:00 | 评论( loading... ) | 编辑| 举报| 收藏

旧一篇:重学C++(3)——用C++实现简单的文件I/O操作 (ZZ) | 新一篇:web2.0,你就忽悠去吧

  • 发表评论
  • 评论内容:
  •  
Copyright © kunp
Powered by CSDN Blog