ページ 11

charの00をプログラムが終了合図だと勘違いしてしまいます。

Posted: 2012年4月22日(日) 00:28
by helloworld1853
webサーバに画像をpostするソフトを作っているのですが、

流れとしては

まず、プログラムがtest.bmpをバイナリモードで開きデータをchar bodyに保存

char bodyをtest.cgiにpost

test.cgiがそれをもとにtest.bmpを作成

画像アップロード 成功!!

です。

画像のバイナリコードは

42 4D E4 0A 00 00 00 ・・・

です。

しかし、00の箇所をソケットの通信終了合図だと勘違いしてしまい、

結局画像は42 4D E4 0Aで止まってしまいます。

通信系のプログラミングはまだなれていないので

お手柔らかにお願いします。

コード:

#include <stdio.h>
#include <winsock2.h>

int main( void )
{
	int sock, ret;
	struct sockaddr_in addr;
	FILE *fp;
	char *fname = "test.bmp";
	char buf[100000];
	char body[100000];
	int  i;
	int size;

	WSADATA wsadata;
	WSAStartup( 0x0101, &wsadata );
	fp = fopen( fname, "rb" );
	size = fread( buf, sizeof( unsigned char ), 10000, fp );

	for( i=0; i<size; i++ )
	{
		
		printf( "%02X ", buf[i] );
	}
	puts("");

	addr.sin_family = AF_INET;
	addr.sin_addr.s_addr = inet_addr("125.172.197.18");
	addr.sin_port = htons(80);
	
	sock = socket( AF_INET, SOCK_STREAM, IPPROTO_TCP );
	ret = connect( sock, (struct sockaddr *)&addr, sizeof addr );
	if ( ret < 0 ) 
	{
		printf( "can't open http port\n" );
		return 0;
	}
	sprintf(body,"namae=%s\r\n",buf);
	body[size+6]='\0';
	char head[] = "POST /~yamada_mama_papa/test.cgi HTTP/1.0\r\nHost: s1.muryo.etowns.net\r\n";
	char length[100];
	sprintf( length, "Content-Length: %d\r\n", strlen(body) );
	
	int n = 0;
	n += send( sock, head, strlen(head), 0 );
	n += send( sock, length, strlen(length), 0 );
	n += send( sock, "\r\n", 2, 0 );
	n += send( sock, body, strlen(body), 0 );
	printf( "send: %d\n", n );
	
	printf( "http recv data\n" );
	printf( "=============================\n" );
	while ( 1 ) 
	{
		n = recv( sock, buf, sizeof(buf)-1, 0 );
		if ( n <= 0 ) break;
		buf[ n ] = '\0';
		printf( buf );
	}
	closesocket( sock );
	fclose( fp );

}
以下がtest.cgi

コード:

#!/usr/local/bin/perl
use CGI;
$q = new CGI;
$Name = $q->param('namae');
print "Content-type: text/html\n\n";
print "<META http-equiv=\"Content-Type\" content=\"text/html; charset=Shift_JIS\">\n";
print $Name;
open (OUT,"> test.bmp");
print OUT $Name;
close (OUT);
print 'sucess';
exit;
[]/code]

よろしくお願いします。

Re: charの00をプログラムが終了合図だと勘違いしてしまいます。

Posted: 2012年4月22日(日) 01:04
by tamaneko
初心者で提示ソースコードは高度で何をしているのかさっぱり分らないのですが、
表題の事が起こるのはバイナリデータを文字列操作関数で操作しているからではないですか?
文字列操作関数はバイナリの00を番兵とするって聞いたことがあります。
ソースコードは文字列操作関数だらけで、バイナリ操作の関数ぽいのかないので
そのように感じました。

Re: charの00をプログラムが終了合図だと勘違いしてしまいます。

Posted: 2012年4月22日(日) 01:16
by h2so5
recvの戻り値で終了を判断せずとも、
ヘッダでBodyのサイズが分かるのだからそれを利用すればいいのではないでしょうか?


関係なさそうなので撤回します。

Re: charの00をプログラムが終了合図だと勘違いしてしまいます。

Posted: 2012年4月22日(日) 13:19
by helloworld1853
>初心者で提示ソースコードは高度で何をしているのかさっぱり分らないのですが、

僕のソースコードに無駄な部分が多いだけです。

あるソースコードを改造したものなので変な所もあるかもしれませんが

気にしないでください。

ソースコード・改です。

見やすくなったはずです。

コード:

#include <stdio.h>
#include <winsock2.h>

int main( void )
{
	int sock, ret;
	struct sockaddr_in addr;
	FILE *fp;
	char *fname = "test.bmp";
	char buf[100000];
	char body[100000];
	int  i;
	int size;

	WSADATA wsadata;
	WSAStartup( 0x0101, &wsadata );
	fp = fopen( fname, "rb" );
	size = fread( buf, sizeof( unsigned char ), 10000, fp );

	for( i=0; i<size; i++ )
	{
		
		printf( "%02X ", buf[i] );
	}
	puts("");

	addr.sin_family = AF_INET;
	addr.sin_addr.s_addr = inet_addr("125.172.197.18");
	addr.sin_port = htons(80);
	
	sock = socket( AF_INET, SOCK_STREAM, IPPROTO_TCP );
	ret = connect( sock, (struct sockaddr *)&addr, sizeof addr );
	if ( ret < 0 ) 
	{
		printf( "can't open http port\n" );
		return 0;
	}
	sprintf(body,"namae=%s\r\n",buf);
	body[size+6]='\0';
	char head[] = "POST /~yamada_mama_papa/test.cgi HTTP/1.0\r\nHost: s1.muryo.etowns.net\r\n";
	char length[100];
	sprintf( length, "Content-Length: %d\r\n", strlen(body) );
	
	closesocket( sock );
	fclose( fp );

}
さて、バイナリ操作の関数が必要な用ですが、

具体的にどの関数をつかえばいいのでしょうか。

調べてみたのですが、なかなか見つかりません。

それとも関数を作らなければいけないのでしょうか。

正直あまり技術がないので、関数を設計したくないのですが・・・

Re: charの00をプログラムが終了合図だと勘違いしてしまいます。

Posted: 2012年4月22日(日) 15:48
by h2so5
helloworld1853 さんが書きました: 画像のバイナリコードは

42 4D E4 0A 00 00 00 ・・・

です。

しかし、00の箇所をソケットの通信終了合図だと勘違いしてしまい、

結局画像は42 4D E4 0Aで止まってしまいます。
どうしてそう判断されたのか教えてください。
このコードでは 42 4D E4 0A という形式ではデータが表示されません。実際の出力を書いてください。

Re: charの00をプログラムが終了合図だと勘違いしてしまいます。

Posted: 2012年4月22日(日) 16:02
by helloworld1853
>実際の出力を書いてください。

そんな高度な手法は使いませんでした。

http://s1.etowns.slyip.net/~yamada_mama_papa/test.bmp

に画像が生成されるのでそれを保存し、

バイナリエディタで開きました。

Re: charの00をプログラムが終了合図だと勘違いしてしまいます。

Posted: 2012年4月22日(日) 18:29
by みけCAT
試してはいません。

コード:

sprintf(body,"namae=%s\r\n",buf);

コード:

strcpy(body,"namae=");
memcpy(&body[6],buf,size);
memcpy(&body[6+size],"\r\n");
とし、

コード:

sprintf( length, "Content-Length: %d\r\n", strlen(body) );

コード:

sprintf( length, "Content-Length: %d\r\n", 6+size );
としたらどうですか?

Re: charの00をプログラムが終了合図だと勘違いしてしまいます。

Posted: 2012年4月22日(日) 20:19
by helloworld1853
みけCATさんのお力を借りて修正しましたが

変わりませんでした。

僕がソースコードをいじくってしまったからかもしれません。

引数が少ないとコンパイラがほざきやがったので・・・

どういじくったのかというと・・・

コード:

strcpy(body,"namae=");
memcpy(&body[6],buf,size);
memcpy(&body[6+size],"\r\n");

コード:

strcpy(body,"namae=");
memcpy(&body[6],buf,size);
memcpy(&body[6+size],"\r\n",4);
にしました。

なにか間違って入れば教えてください。

Re: charの00をプログラムが終了合図だと勘違いしてしまいます。

Posted: 2012年4月22日(日) 21:10
by みけCAT
helloworld1853 さんが書きました:引数が少ないとコンパイラがほざきやがったので・・・
ごめんなさい、私のミスです。

コード:

memcpy(&body[6+size],"\r\n",4);
ではなく

コード:

memcpy(&body[6+size],"\r\n",2);
です。
No.4のソースコードを元にしています。
このソースコードには、送信部分が無いようですが、どうなっていますか?

Re: charの00をプログラムが終了合図だと勘違いしてしまいます。

Posted: 2012年4月22日(日) 22:14
by helloworld1853
みけCAT さん

ありがとうございます。

重要なミスに気づきました。

送信部分がないですね・・・

正直はずかしいミスでした。

あと生成された画像のバイナリを

42 4D E4 0Aと書きましたが

実際は42 4D E4 0A 0D 0Aでした。

でいま送信部分を書き直して実行した結果、

バイナリデータが42 4D E4 0Aでした。

これも僕のミスです。

とにかく少し結果が改善されましたが

プログラムの勘違いはそのままです。

話がややこしくなっていますがお許しを

Re: charの00をプログラムが終了合図だと勘違いしてしまいます。

Posted: 2012年4月22日(日) 22:19
by みけCAT
とりあえず現状のプログラムを貼ってみてもらえますか?

Re: charの00をプログラムが終了合図だと勘違いしてしまいます。

Posted: 2012年4月22日(日) 23:31
by helloworld1853
最終ソースコードです。

recvを使用した箇所は受信用なので省いてもいいと判断しました。

コード:

#include <stdio.h>
#include <winsock2.h>

int main( void )
{
	int sock, ret;
	struct sockaddr_in addr;
	FILE *fp;
	char *fname = "test.bmp";
	unsigned char buf[100000];
	char body[100000];
	int  i;
	int size;

	WSADATA wsadata;
	WSAStartup( 0x0101, &wsadata );
	fp = fopen( fname, "rb" );
	size = fread( buf, sizeof( unsigned char ), 100000, fp );

	for( i=0; i<size; i++ )
	{
		
		printf( "%02X ", buf[i] );
	}
	puts("");

	addr.sin_family = AF_INET;
	addr.sin_addr.s_addr = inet_addr("125.172.197.18");
	addr.sin_port = htons(80);
	
	sock = socket( AF_INET, SOCK_STREAM, IPPROTO_TCP );
	ret = connect( sock, (struct sockaddr *)&addr, sizeof addr );
	if ( ret < 0 ) 
	{
		printf( "can't open http port\n" );
		return 0;
	}
	strcpy(body,"namae=");
	memcpy(&body[6],buf,size);
	memcpy(&body[6+size],"\r\n",2);

	body[size+6]='\0';

	char head[] = "POST /~yamada_mama_papa/test.cgi HTTP/1.0\r\nHost: s1.muryo.etowns.net\r\n";
	char length[100];
	sprintf( length, "Content-Length: %d\r\n", 6+size );
	int n=0;
	n+=send(sock,head,strlen(head),0);
	n+=send(sock,length,strlen(length),0);
	n+=send(sock,"\r\n",2,0);
	n+=send(sock,body,strlen(body),0);

	closesocket( sock );

	fclose( fp );

}

Re: charの00をプログラムが終了合図だと勘違いしてしまいます。

Posted: 2012年4月23日(月) 16:29
by helloworld1853
ほかの関数をつかうしか手がないのでしょうか。

それとも自作関数を用いる必要があるのでしょうか。

御回答をお待ちしております。

Re: charの00をプログラムが終了合図だと勘違いしてしまいます。

Posted: 2012年4月23日(月) 16:34
by h2so5
strlenは文字列の長さを返す関数ですから、strlen(body)は適当ではないですね。
この場合、画像ファイルのサイズはsizeに入っているのではないでしょうか。

Re: charの00をプログラムが終了合図だと勘違いしてしまいます。

Posted: 2012年4月23日(月) 16:38
by YuO
バイナリデータの取り扱い方を勉強する方が先のような気もしますが……。

バイナリデータの長さをデータから汎用的に知る方法はありません。
なので,「バイナリデータの長さを取得する関数」は存在しません。

基本的には,freadで読み込んだサイズだけsendすることになります。
sendは指定したサイズ丁度を送信するとは限らないため,部分的に送信していた場合は残りの部分を送信する,というコードも必要になります。
さらに,一回のfreadで全部を読み込んだとも限らないため,終了を検知するまでfreadする必要もあります。
# Content-Lengthに与える値は,FindFirstFile API等で知ることになります。

Re: charの00をプログラムが終了合図だと勘違いしてしまいます。

Posted: 2012年4月23日(月) 17:34
by helloworld1853
みなさまのおかげで無事完成しました。

本当に有難うございました。

ソースコードです。

コード:

#include <stdio.h>
#include <winsock2.h>

int main( void )
{
	int sock, ret;
	struct sockaddr_in addr;
	FILE *fp;
	char *fname = "test.bmp";
	unsigned char buf[100000];
	//char buf1[1000000];
	char body[100000];
	int  i;
	int size;
	//int counter;

	WSADATA wsadata;
	WSAStartup( 0x0101, &wsadata );
	fp = fopen( fname, "rb" );
	size = fread( buf, sizeof( unsigned char ), 100000, fp );

	for( i=0; i<size; i++ )
	{
		
		printf( "%02X ", buf[i] );
	}
	puts("");
	//PROC pfn0rig;
	//printf("%d\n",buf[5]);
	addr.sin_family = AF_INET;
	addr.sin_addr.s_addr = inet_addr("61.214.105.140");
	addr.sin_port = htons(80);
	
	sock = socket( AF_INET, SOCK_STREAM, IPPROTO_TCP );
	ret = connect( sock, (struct sockaddr *)&addr, sizeof addr );
	if ( ret < 0 ) 
	{
		printf( "can't open http port\n" );
		return 0;
	}
	strcpy(body,"namae=");
	memcpy(&body[6],buf,size);
	memcpy(&body[6+size],"\r\n",2);
	//sprintf(body,"namae=%s\r\n",buf);
	body[size+6]='\0';
	//puts("*****************************************************************************************");
	//printf( "%s ",body);
	//body[strlen(body)]='\0';
	//printf("%s",body);
	//char body[] = "namae=2222222222222222222222\r\n";
	char head[] = "POST /~yamada_mama_papa/test.cgi HTTP/1.0\r\nHost: s1.muryo.etowns.net\r\n";
	char length[100];
	sprintf( length, "Content-Length: %d\r\n", 6+size );
	int n=0;
	n+=send(sock,head,strlen(head),0);
	n+=send(sock,length,strlen(length),0);
	n+=send(sock,"\r\n",2,0);
	n+=send(sock,body,size,0);
	//char buf[256];
	//printf( "http recv data\n" );
	//printf( "=============================\n" );
	
	closesocket( sock );
	//close( sock );
	fclose( fp );

}

Re: charの00をプログラムが終了合図だと勘違いしてしまいます。

Posted: 2012年4月23日(月) 17:34
by helloworld1853
またよろしくお願いします。