コンピュータを楽しもう!!

今、自分が面白くていろいろやってみたことを書き綴りたいと思います。連絡先はtarosa.yでgmail.comです。

DevkitProで使われているdswifiについて(5)

今日は、dswifiを使ってhttpサーバからデータを取得するプログラムを作ったので紹介します。
昨日のブログに書いたSimpleHttpGet()からちょっとだけ拡張されています。

//**************************************************
//      指定httpサーバから指定ファイルを取得します
//  servername : httpホスト名
//  getfilename : 取得したいファイル名
//  savefilename : 取得したときにDSに保存する保存ファイル名
//  portnum : 接続ポート番号
//**************************************************
bool HttpGetFile( char* servername, char* getfilename, char* savefilename, short portnum )
{
FILE *fp = NULL;
bool  successFlg	= true;
char  toptext[HTTPMESSAGELENGTH];
int   i, j;
const char *endtext = "\r\nUser-Agent: Nintendo DS\r\n\r\n";

  //********** http GET コマンド生成 **********
  if( getfilename[0]!='/' ){
    strcpy( toptext, "GET /" );
  }
  else{
    strcpy( toptext, "GET " );
  }
  strcat( toptext, getfilename );
  strcat( toptext, " HTTP/1.1\r\nHost: " );
  strcat( toptext, servername );
  strcat( toptext, endtext );
  //*******************************************
	
  // サーバのIPアドレスを検索します
  struct hostent * myhost = gethostbyname( servername );

  //TCPソケットの生成
  int my_socket = socket( AF_INET, SOCK_STREAM, 0 );

  //ホストに接続するため、指定IPアドレスの指定ポートに接続します。
  struct sockaddr_in sain;
  sain.sin_family = AF_INET;
  sain.sin_port = htons( portnum );	//ポート指定
  sain.sin_addr.s_addr= *( (unsigned long *)(myhost->h_addr_list[0]) );	//IP指定
  connect( my_socket,(struct sockaddr *)&sain, sizeof(sain) );

  //接続先にファイル取得リクエストを飛ばします
  send( my_socket, toptext, strlen(toptext), 0 );

  //メモリの確保
  char *incoming_buffer = (char*)malloc( sizeof(char*) * HTTPGETDATASIZE );
  if(incoming_buffer==NULL ){
    ShinoPrint( SubScreenA, 0, 0, (u8 *)"Malloc Error", RGB(31,0,0), 0, 1 );
    while( true );
  }

  //最初の返信データを受信
  int recvd_len = recv( my_socket, incoming_buffer, HTTPGETDATASIZE, 0 );

  //recvd_lenが-1だったり、返事が 200 OK であるかどうか調べる
  if( strexist( incoming_buffer, (char *)"200", 20)==false
      || strexist( incoming_buffer, (char *)"OK", 20)==false
      || recvd_len==-1 ){
		
    //*** メッセージの送受信失敗 ***
    successFlg = false;
    recvd_len = strlen( (char *)incoming_buffer );
    //最初の受信分だけ LOGに保存して終了。
    fp = fopen( "httpgeterror.log", "wb" );
    if( fp==NULL ){
      ShinoPrintCenter( SubScreenA, 128, 12*12, (u8 *)"httpgeterror.log not create.", RGB(31,0,0), 0, 1 );
      while( true );
    }
    fwrite( incoming_buffer, recvd_len, sizeof(char), fp );
    fclose( fp );
  }
  else{
    //*** メッセージの送受信成功 ***
    //最初のパケットが"HTTP/1.1 200 OK"のみかどうか調べます
    if( strlen(incoming_buffer)<=15 ){
      //15バイト以下だったら再読み込みする
      recvd_len = recv( my_socket, incoming_buffer, HTTPGETDATASIZE, 0 );
    }

    //*** 受信開始 ***
    //ヘッダを取り除く(CRLFが2つ連続するところを探す)
    int foundFlg = 0;
    int crlfFlg = 0;
    // 0 : 終わりの3バイトがCRLFCRでない
    // 1 : 終わりの1バイトがCRである
    // 2 : 終わりの2バイトがCRLFである
    // 3 : 終わりの3バイトがCRLFCRである。
    while( true ){
      if( crlfFlg==1 && incoming_buffer[0]==0x0a 
          && incoming_buffer[1]==0x0d && incoming_buffer[2]==0x0a ){
        //見つかった
        for( i=3; i<recvd_len; i++ ){	incoming_buffer[i-3] = incoming_buffer[i];	}
        recvd_len -= 3;
        foundFlg = 1;
        break;
      }
      else if( crlfFlg==2 && incoming_buffer[0]==0x0d && incoming_buffer[1]==0x0a ){
        //見つかった
        for( i=2; i<recvd_len; i++ ){	incoming_buffer[i-2] = incoming_buffer[i];	}
        recvd_len -= 2;
        foundFlg = 1;
        break;
      }
      else if( crlfFlg==3 && incoming_buffer[0]==0x0a ){
        //見つかった
        for( i=1; i<recvd_len; i++ ){	incoming_buffer[i-1] = incoming_buffer[i];	}
        recvd_len -= 1;
        foundFlg = 1;
        break;
      }
      else{
        for( j=0; j<recvd_len-3; j++ ){
          if( incoming_buffer[j]==0x0d && incoming_buffer[j+1]==0x0a 
              && incoming_buffer[j+2]==0x0d && incoming_buffer[j+3]==0x0a ){
            //見つかった
            for( i=j+4; i<recvd_len; i++ ){
              incoming_buffer[i-(j+4)] = incoming_buffer[i];
            }
            recvd_len -= (j+4);
            foundFlg = 1;
            break;
          }
        }
        if( foundFlg==1 )	break;
        if( incoming_buffer[recvd_len-3]==0x0d && incoming_buffer[recvd_len-2]==0x0a
            && incoming_buffer[recvd_len-1]==0x0d ){
          crlfFlg = 3;
        }
        else if( incoming_buffer[recvd_len-2]==0x0d && incoming_buffer[recvd_len-1]==0x0a ){
          crlfFlg = 2;
        }
        else if( incoming_buffer[recvd_len-1]==0x0d ){
          crlfFlg = 1;
        }
      }
      if( ( recvd_len = recv( my_socket, incoming_buffer, HTTPGETDATASIZE, 0 ) ) == 0 ){	break;	}
    }
  }

  if( successFlg==true ){
    //*** 受信データを保存 ***
    fp = fopen( (char*)savefilename, "wb" );	//保存用ファイルをオープンします
    if( fp==NULL ){
      ShinoPrintCenter( SubScreenA, 128, 10*12, (u8 *)savefilename, RGB(31,0,0), 0, 1 );
      ShinoPrintCenter( SubScreenA, 128, 12*12, (u8 *)"↑ファイル not create.", RGB(31,0,0), 0, 1 );
      while( true );
    }
    fwrite( incoming_buffer, recvd_len, sizeof(char), fp );
	
    //データサイズが0になるまで、データを取得します
    while( ( recvd_len = recv( my_socket, incoming_buffer, HTTPGETDATASIZE, 0 ) ) != 0 ){
      if( recvd_len>0 ){
        swiWaitForVBlank();	
        fwrite( incoming_buffer, recvd_len, sizeof(char), fp );
      }
    }
    fclose( fp );
  }

  //メモリの開放
  free( incoming_buffer );
	
  //ソケットをシャットダウンします
  shutdown(my_socket,0);

  //少し間をおく(こうしないとソケットが切れない・・・)
  for( i=0; i<20; i++ ){	swiWaitForVBlank();	}
	
  //ソケットを閉じます
  closesocket( my_socket );
	
  return( successFlg );
}

説明はほぼ注釈に書いてあるとおりです。ユーザーエージェント名を変更したいときは、endtext変数の設定内容を変更してください。
DevkitProは32768bytes程度のmallocはずっこけなさそうです。大きくなるとダメっぽいですね。エラーが返ってきたときには、root下のhttpgeterror.logファイルに受信内容が保存されます。
連続してファイルを取得したいときには、ソケットをシャットダウンしないでhttpリクエストを出すように作り変えればいいと思います。

ソースはNDS Program Roomに上げておきますね。

本当にやりたいことリスト

(ブログの終わりにやりたいことを書いておきたいと思います)

  • 求職活動・・・このブログで興味を持った人一声かけてください。m(_ _)m
  • Androidプログラム
  • Web系のプログラム