C#のファイルとソケットの優れている点は、両方がストリームとして公開されていることです。あるストリームから別のストリームに大きなファイルをコピーするのは非常に簡単です。
byte[] data = new byte[1024];
while(true) {
int bytesRead = filestream.read(data,0,data.Length);
if (bytesRead==0) break;
netstream.write(data,0,bytesRead);
}
完了したら、ソケットを閉じます。
メタデータ(ファイル名、サイズ)を送信する場合、または接続を閉じたくない場合は、これを処理するための何らかのプロトコルが必要です。FTPは、2つの別個のソケットを使用します(1つはメタデータ用、もう1つはデータ用。これは帯域外通信と呼ばれます)。ファイアウォールのないLANを使用している場合は、それで十分です。一方、インターネット転送を行う場合は、1つのポートを開くのは困難で十分な作業であり、2つは耐えられません。パフォーマンスをあまり気にしない場合は、base64エンコーディングでバイトをエンコードできます。これにより、バイトが特定のバイト範囲内にあることが確認されます。base64を使用すると、メッセージを改行またはその他の英数字以外の文字で区切ることができます。次に、最初のメッセージにファイル名やサイズなどを含め、データを2番目のメッセージとして送信してから、「that」を送信します。
メッセージのもう1つの戦術は、エスケープシーケンスを使用することです。たとえば、バイトストリームを取得し、「\0」の各インスタンスを「\0\0」に置き換えます。次に、「\ 0 \ 1」を使用してメッセージの終わりを通知します。これは、データメッセージに含まれないことが保証されています。'\ 0\0'をデコードして受信側の'\0'に戻します。これはCで十分に機能しますが、実際には、各バイトのループはC#でバッファー全体を読み取るよりも遅くなる可能性があることがわかりました。
最良の方法は、ある種の適応長プロトコルを採用することです。たとえば、特定のサイズ(512バイトなど)のチャンクでデータを送信します。各チャンクの前に、System.BitConverterを介してチャンクのサイズを表す32ビット整数を送信します。したがって、メッセージは次のようになります(英語):
Here's 512 bytes:
[data]
Here's 512 bytes:
[data]
Here's 32 bytes:
[data]
Here's 4 bytes:
That was the whole file
ここでの利点は、コピー/読み取りバッファーを機能させることができることです(一度に512バイトを読み取る)。つまり、スループットはC#コードではなくネットワークスタックによって制限されます。クライアントは、固定長の32ビットintを読み取り、次の[data]セグメントに使用する必要があるバッファーのサイズを通知します。
このようなメッセージを書くためのコードは次のとおりです。
logger.logger.debug("Sending message of length " + length);
byte[] clength = System.BitConverter.GetBytes(buffer.Length);
plaintextStream.Write(clength,0,clength.Length);
plaintextStream.Write(buffer,0,buffer.Length);
plaintextStream.Flush();
そして、それらを読むためのいくつかのコードがあります:
byte[] intbuf = new byte[int_32_size];
int offset = 0;
while (offset < int_32_size)
{
int read = 0;
read = d.plaintextStream.Read(intbuf,offset,int_32_size - offset);
offset += read;
}
int msg_size = System.BitConverter.ToInt32(intbuf,0);
//allocate a new buffer to fill the message
byte[] msg_buffer = new byte[msg_size];
offset = 0;
while (offset < msg_size)
{
int read = 0;
read = d.plaintextStream.Read(msg_buffer,offset,msg_size - offset);
offset += read;
}
return msg_buffer;