LoginSignup
1
1

More than 5 years have passed since last update.

C++兼SDL_netでHTTP通信するコード。400 Bad Requestの原因をコメントで指摘。

Last updated at Posted at 2016-03-21
main.cpp

#include<windows.h>
#include<SDL_net.h>
#pragma comment(lib,"SDL2_net.lib")
int main(int argc, char_t *argv[]){
    IPaddress ip;
    TCPsocket server;
    if(!SDLNet_Init()){
        if(!SDLNet_ResolveHost(&ip,"k-db.com\0",80)){
            if(server=SDLNet_TCP_Open(&ip)){
                char    bufsend[]=
                    "GET https://www.google.co.jp/webhp?hl=ja\r\n"
                //原因1
                //リクエストで\nを改行コードとして誤って使っている。正しくは\r\nである。
                    "\r\n";
                //原因2
                //ヘッダの最後で一行を空け忘れている。
                //GETリクエストの末尾は改行+空行で〆るので"\r\n\r\n"となるべきである。
                SDLNet_TCP_Send(server,bufsend,sizeof(bufsend)+1);//+1は\0の分
                while(true){
                    char    bufrecv[256+1];
                    memset(bufrecv,0,sizeof(bufrecv));
                    if(SDLNet_TCP_Recv(server,bufrecv,sizeof(bufrecv))){
                        bufrecv[sizeof(bufrecv)-1]=0;
                        MessageBoxA(NULL,bufrecv,"title",MB_OK);
                    }else{
                        break;
                    }
                    //socketに溜まった返事を256byteずつ最後まで表示。
                }
                SDLNet_TCP_Close(server);
            }
        }
        SDLNet_Quit();
    }
    return  0;
}

SDLNet_TCP_Recvで1byteを読み込む動作を
1回,10回,100回,1000回,10000回繰り返す時間は
125,125,125,125,144(ms)だった。
だから多分SDLNet_TCP_Recvは内部のバッファに溜め込んでいる。
それを利用して早くパーサを作りたい。

2016/4/7追記
以上を応用して簡単なダウンローダを作った。
Content-lengthでもchunkedでもいける。

a.h
//tolua_end
#include<SDL_net.h>
#include<string>
#include<algorithm>
#pragma comment(lib,"SDL2_net.lib")
struct  HTTP{//tolua_export
    static  const   int SIZE=8192;
    char*   m_buff;
    TCPsocket   m_sckt;
    ~HTTP(){
        SDLNet_Quit();
        delete  m_buff;
    }
    struct  CURL{
        std::string m_host,m_path;
        CURL(const  char*t){
            std::string src(t);
            int n1=src.find("//",0)+2;
            int n2=src.find("/",n1);
            m_host=src.substr(n1,n2-n1);
            m_path=src.substr(n2);
        }
        IPaddress   resolve(){
            IPaddress ip={0};
            SDLNet_ResolveHost(&ip,m_host.c_str(),80);
            return  ip;
        }
    };
    bool    open(IPaddress i){
        return  (m_sckt=SDLNet_TCP_Open(&i))==NULL;
    }
    void    close(){
        SDLNet_TCP_Close(m_sckt);
    }
    void    sendhead(const  char*format,...){
        va_list arg;
        va_start(arg,format);
        int n=vsprintf_s(m_buff,SIZE,format,arg);
        if(n>=0){
            m_buff[n]=0;
            SDLNet_TCP_Send(m_sckt,m_buff,n+1);
        }
        va_end(arg);
    }
    char    recvchar(){
        char    t;
        while(SDLNet_TCP_Recv(m_sckt,&t,1)!=sizeof(t)){}
        return  t;
    }
    std::string recvhead(){
        std::string tmp;
        char    t[2]={0};
        for(int i=0;i<0xff;i++){
            t[0]=t[1];
            t[1]=recvchar();
            //順番大事
            if(((t[0]=='\r')&&(t[1]=='\n'))||((t[0]==':')&&(t[1]==' ')))break;
            if(*t)tmp.push_back(*t);
        }
        //比較前に小文字化
        std::transform(tmp.begin(),tmp.end(),tmp.begin(),tolower);
        return  tmp;
    }
    bool    recv(int size,void*p,void(*proc)(void*,void*,int n)){
        for(int n=0,i=0;i<size;i+=n){
            n=SDLNet_TCP_Recv(m_sckt,m_buff,min(size-i,SIZE));
            if(n){
                proc(p,m_buff,n);
            }else{
                return  true;
            }
        }
        return  false;
    }
    static  void    proc_fw(void*a,void*b,int c){
        fwrite(b,1,c,(FILE*)a);
    }
    //tolua_begin
    HTTP(){
        SDLNet_Init();
        m_buff=new  char[SIZE];
    }
    void    get(const   char*tmp,const  char*dst){
        CURL    url(tmp);
        if(open(url.resolve()))return;
        sendhead("GET %s HTTP/1.1\r\nhost: %s\r\n\r\n",url.m_path.c_str(),url.m_host.c_str());
        bool    chunked=false;
        signed  size;
        while(1){
            std::string a=recvhead();
            if(a==""){
                break;
            }
            if(a=="content-length"){
                size=atoi(recvhead().c_str());
            }
            if(a=="transfer-encoding"){
                if(recvhead()=="chunked"){
                    chunked=true;
                }
            }
        }
        FILE*   f;
        fopen_s(&f,dst,"wb");
        if(chunked){
            while(true){
                size=strtol(recvhead().c_str(),NULL,16);
                if(size>0){
                    recv(size,f,proc_fw);
                }else{
                    break;
                }
                //最後も改行
                recvhead();
            }
        }else{
            recv(size,f,proc_fw);//完成
        }
        fclose(f);
        close();
    }   
};

例)このページをdest.htmlとしてダウンロードする
HTTP h;
h.get("http://qiita.com/misumi3104/items/8e61ff68fc9fe68db533","dest.html")

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1