Domanda

Voglio usare il TcpClient e TcpListener per inviare un file mp3 su una rete. Ho implementato una soluzione di questo utilizzando i socket, ma ci sono stati alcuni problemi in modo da sto indagando su un nuovo modo / meglio per inviare un file.

creo un array di byte che assomiglia a questo: length_of_filename | filename | file

Questo dovrebbe quindi essere trasmessa utilizzando le classi di cui sopra, ma sul lato server l'array di byte che ho letto è completamente incasinato e non sono sicuro perché.

Il metodo che uso per l'invio:

 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);
        }
    }
}

Poi sul lato server appare come segue:

    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();
        }
    }

Chiunque può vedere qualcosa che mi manca / facendo male?

È stato utile?

Soluzione

La corruzione è causato dal seguente codice sul server:

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

stream.Read non sarà sempre riempire il buffer bytes. Non sarà riempita se il socket TCP non ha più dati disponibili, o durante la lettura l'ultimo pezzo del messaggio (a meno che non sia un multiplo esatto della dimensione del buffer).

La chiamata data.AddRange aggiunge tutto da bytes (facendo l'ipotesi che sia sempre pieno). Come risultato, questo a volte finire l'aggiunta di dati dalla precedente chiamata a stream.Read. Per ovviare a tale situazione, è necessario memorizzare il numero di byte restituiti da Read e solo aggiungere questo numero di byte:

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);
}

Si noti che si potrebbe desiderare di ristrutturare il codice per migliorare le prestazioni e l'utilizzo della memoria (e, probabilmente, rendere più facile da leggere come un risultato). Invece di leggere tutti i dati nella memoria sul client prima di inviare si può semplicemente scrivere direttamente al NetworkStream. Sul server non è necessario copiare tutto dal flusso in memoria. È possibile leggere la lunghezza 4 byte di nome di file e decodificarlo, poi leggere e decodificare il nome del file e, infine, copiare il resto del flusso direttamente ad un FileStream (la BinaryWriter non è necessaria).

E 'anche interessante notare che si sta creando il file di output con FileMode.Append. Questo significa che ogni file inviato verrà aggiunto alla copia precedente con lo stesso nome. Si consiglia di utilizzare FileMode.Create invece, che sovrascriverà se il file esiste già.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top