0

TcpClientとTcpListenerを使用して、ネットワーク経由でmp3ファイルを送信したいと思います。ソケットを使用してこれの解決策を実装しましたが、いくつかの問題があったため、ファイルを送信するための新しい/より良い方法を調査しています。

次のようなバイト配列を作成します:length_of_filename | filename | file

これは、上記のクラスを使用して送信する必要がありますが、サーバー側では、読み取ったバイト配列が完全に混乱しているため、理由がわかりません。

私が送信するために使用する方法:

 public static void Send(String filePath)
    {
        try
        {
            IPEndPoint endPoint = new IPEndPoint(Settings.IpAddress, Settings.Port + 1);
            Byte[] fileData = File.ReadAllBytes(filePath);
            FileInfo fi = new FileInfo(filePath);

            List<byte> dataToSend = new List<byte>();
            dataToSend.AddRange(BitConverter.GetBytes(Encoding.Unicode.GetByteCount(fi.Name))); // length of filename
            dataToSend.AddRange(Encoding.Unicode.GetBytes(fi.Name)); // filename
            dataToSend.AddRange(fileData); // file binary data


            using (TcpClient client = new TcpClient())
            {
                client.Connect(Settings.IpAddress, Settings.Port + 1);

                // Get a client stream for reading and writing.
                using (NetworkStream stream = client.GetStream())
                {
                    // server is ready 
                    stream.Write(dataToSend.ToArray(), 0, dataToSend.ToArray().Length);
                }
            }

        }
        catch (ArgumentNullException e)
        {
            Debug.WriteLine(e);
        }
        catch (SocketException e)
        {
            Debug.WriteLine(e);
        }
    }
}

次に、サーバー側では次のようになります。

    private void Listen()
    {
        TcpListener server = null;
        try
        {
            // Setup the TcpListener
            Int32 port = Settings.Port + 1;
            IPAddress localAddr = IPAddress.Parse("127.0.0.1");

            // TcpListener server = new TcpListener(port);
            server = new TcpListener(localAddr, port);

            // Start listening for client requests.
            server.Start();

            // Buffer for reading data
            Byte[] bytes = new Byte[1024];
            List<byte> data;

            // Enter the listening loop.
            while (true)
            {
                Debug.WriteLine("Waiting for a connection... ");
                string filePath = string.Empty;

                // Perform a blocking call to accept requests.
                // You could also user server.AcceptSocket() here.
                using (TcpClient client = server.AcceptTcpClient())
                {
                    Debug.WriteLine("Connected to client!");
                    data = new List<byte>();

                    // Get a stream object for reading and writing
                    using (NetworkStream stream = client.GetStream())
                    {
                        // Loop to receive all the data sent by the client.
                        while ((stream.Read(bytes, 0, bytes.Length)) != 0)
                        {
                            data.AddRange(bytes);
                        }
                    }
                }

                int fileNameLength = BitConverter.ToInt32(data.ToArray(), 0);
                filePath = Encoding.Unicode.GetString(data.ToArray(), 4, fileNameLength);
                var binary = data.GetRange(4 + fileNameLength, data.Count - 4 - fileNameLength);

                Debug.WriteLine("File successfully downloaded!");

                // write it to disk
                using (BinaryWriter writer = new BinaryWriter(File.Open(filePath, FileMode.Append)))
                {
                    writer.Write(binary.ToArray(), 0, binary.Count);
                }
            }
        }
        catch (Exception ex)
        {
            Debug.WriteLine(ex);
        }
        finally
        {
            // Stop listening for new clients.
            server.Stop();
        }
    }

誰かが私が見逃している/間違っていることを見ることができますか?

4

1 に答える 1

4

破損は、サーバー上の次のコードが原因で発生します。

// Loop to receive all the data sent by the client.
while ((stream.Read(bytes, 0, bytes.Length)) != 0)
{
    data.AddRange(bytes);
}

stream.Readbytes常にバッファがいっぱいになるとは限りません。TCPソケットに使用可能なデータがない場合、またはメッセージの最後のチャンクを読み取る場合(バッファーサイズの正確な倍数でない限り)、データは入力されません。

data.AddRange呼び出しはからすべてを追加します(bytes常にいっぱいであると仮定します)。その結果、これにより、の前の呼び出しからのデータが追加されることがありますstream.Read。これを修正するには、によって返されたバイト数を保存し、Readこのバイト数のみを追加する必要があります。

int length;

while ((length = stream.Read(bytes, 0, bytes.Length)) != 0)
{
    var copy = new byte[length];
    Array.Copy(bytes, 0, copy, 0, length);
    data.AddRange(copy);
}

パフォーマンスとメモリ使用量を改善するために(そしておそらく結果として読みやすくするために)コードを再構築したい場合があることに注意してください。送信する前にすべてのデータをクライアントのメモリに読み込む代わりに、に直接書き込むことができますNetworkStream。サーバーでは、ストリームからメモリにすべてをコピーする必要はありません。4バイトのファイル名の長さを読み取ってデコードし、次にファイル名を読み取ってデコードし、最後にストリームの残りの部分を直接にコピーできますFileStream(これBinaryWriterは不要です)。

を使用して出力ファイルを作成していることにも注意してくださいFileMode.Append。これは、送信された各ファイルが同じ名前の前のコピーに追加されることを意味します。代わりに使用することをお勧めしますFileMode.Create。ファイルがすでに存在する場合は上書きされます。

于 2010-03-08T23:41:06.820 に答える