ソケットの利用(大量データの送受信)


画面を取り込んでJpegにしたデータを離れた相手に送るためにソケットを使います。 Jpegデータはサイズが大きいので複数のパケットに分かれてデータが移動します。 送信するのは簡単ですが、受信時はデータがパケットに別れてくるので分割された データをつなぎ合わせる作業が必要になります。
元のデータサイズが分からないとどこまでの受信データをつなぎ合わせるか分からない ので、今回はデータの先頭にデータサイズを付けて送受信するようにしました。

★バイナリデータを送るときは Socket->SendStream(void *buffer,int length); を使います。
最初にデータの始まりを表すデータ0xffを送信して、送信するデータサイズを4バイトで 送信します。

// 送信データの始まりの印
unsigned char mark=0xff;
Socket->SendBuf(&mark,1);

// 送信データの総数
unsigned int a=Stream1->Size;
Socket->SendBuf(&a,4);
Stream1->Memory の内容をStream1->Sizeバイト送信します。
Socket->SendBuf(Stream1->Memory,Stream1->Size);


★データを受信するときの流れ

データを受信するときはTClientSocketのOnRead イベントに以下のように書きます。
void __fastcall TForm1::ClientSocket1Read(TObject *Sender,
      TCustomWinSocket *Socket)
{

    // 画像データを取り込み始める時はPositionは 0 になっている
    if(Stream1->Position == 0)
    {
        //データの始まりを探す
        unsigned char mark=0;
        Socket->ReceiveBuf(&mark,1);
        if(mark!=0xff)
        {
	    //データの始まりでなければ以降の処理をやめる
            return;
        }

        Length=0;
        int b=0;
        //データが送られるまで少し待たないとLengthの値がおかしくなる
        while(b<100)
        {
            Err++;
            b=Socket->ReceiveLength();
        }
        Panel4->Caption = Err;
        Err=0;

	//データのサイズは4バイトと決めたので4バイト受信する
        Socket->ReceiveBuf(&Length,4);
        Panel3->Caption = Length;
	
	//ストリームサイズをLength にあわせる
        Stream1->SetSize(Length);
        ProgressBar1->Max = Length;
    }

    // 画像データをStream1 に取り込む
    char *buf;

    //受信できるデータ量を取得
    int bufs = Socket->ReceiveLength();

    //bufsバイトのメモリーを確保
    buf = (char*)malloc(bufs);
    int len= Socket->ReceiveBuf(buf,bufs);
    Stream1->Write(buf,len);
    bufs=0;

    //受信した量だけLengthから減らす。Lengthは残り必要なバイト数。
    Length -= len;

    //不明のバグ。特定のPCとやり取りすると、受信データが数バイト少ない。
    //が、実際にjpeg画像に問題はない。
    if(Length<5)
    {
        Panel4->Caption = "bug?";
        Length=0;
    }

    free(buf);

    // 全て取り込み終わったら
    if(Length == 0)
    {
        // 送られてきたjpegをbitmapに復元してImage1に表示
        TJPEGImage *jp = new TJPEGImage();
        Stream1->Position = 0;

        //Stream1の内容をjpに取り込む
        jp->LoadFromStream(Stream1);

        // jpをImage1に取り込む
        Image1->Picture->Bitmap->Assign(jp);
        Stream1->Position = 0;
        delete jp;
    }
}


masaki@tube.ee.uec.ac.jp