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")