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系のプログラム