在C#的套接字编程中发送大文件

3
我想用C#中的socket发送大文件。文件可以成功传输,但当我想打开它时,发现它已经损坏了。问题出在哪里? 在客户端代码中,我将文件分成2KB的数组并发送。然后,在服务器端代码中,我接收到它并将其放入字节数组中并转换为文件。
服务器端代码:
    using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;
using System.Net;
using System.Net.Sockets;

namespace MyServer
{
    class Program
    {
        static void Main(string[] args)
        {
            bool full = false;
            byte[] data = new byte[2049];
            byte[] bsize = new byte[2048];
            List<byte> bytes = new List<byte>();
            IPEndPoint ipep = new IPEndPoint(IPAddress.Any, 9050);
            Socket newsock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            newsock.Bind(ipep);
            newsock.Listen(100);
            Console.WriteLine("Wating For Client ...");
            Socket client = newsock.Accept();
            IPEndPoint clientep = (IPEndPoint)client.RemoteEndPoint;
            Console.WriteLine("Connected with {0} at port {1}",clientep.Address,clientep.Port);
            Console.Write("Enter the name of file:  ");
            string Address = Console.ReadLine();
            client.Receive(bsize);
            int size = BitConverter.ToInt32(bsize, 0);
            byte[] bytes2 = new byte[size];
            int k = 0;
            while (true)
            {
                client.Receive(data);
                for(int i = k,j=0; i < k + 2048 && j<2048; i++,j++)
                {
                    if (i == size)
                    {
                        full = true;
                        break;
                    }
                    bytes2[i] = data[j];
                    //bytes.Insert(i,data[j]);
                }
                k += 2048;
                if (full == true)
                    break;
                /*if (bytes.Count >= size)
                    break;*/
            }
            File.WriteAllBytes(@"G:\"+Address,bytes2 /*bytes.ToArray()*/);
            Console.WriteLine("Disconnected from Client {0}",clientep.Address);
            client.Close();
            newsock.Close();
            Console.ReadKey();
        }
    }
}

客户端代码:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;
using System.Net;
using System.Net.Sockets;

namespace MyClient
{
    class Program
    {
        static void Main(string[] args)
        {
            int reza = 0;
            IPEndPoint ipep = new IPEndPoint(IPAddress.Parse("192.168.0.2"), 9050);
            Socket server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            try
            {
                server.Connect(ipep);
            }
            catch (SocketException e)
            {
                Console.WriteLine("Unable to connect to Server");
                Console.WriteLine(e.ToString());
                Console.ReadKey();
                return;
            }
            Console.Write("Enter Address Of File:");
            string Address = Console.ReadLine();
            FileStream File = new FileStream(Address, FileMode.Open, FileAccess.Read);
            byte[] bytes = System.IO.File.ReadAllBytes(@Address);
            File.Close();
            byte[] data = new byte[2048];
            byte[] Size = BitConverter.GetBytes(bytes.Length);
            server.Send(Size, Size.Length, SocketFlags.None);
            for (int i = 0; i <= bytes.Length; i +=2048)
            {
                int k = i + 2048;
                for(int j=i,d=0 ;j< k && j<bytes.Length; j++,d++)
                {
                    data[d] = bytes[j];
                    reza++;
                }
                if (reza == 2048)
                {
                    server.Send(data, data.Length, SocketFlags.None);
                    Console.Write("*");
                    reza = 0;
                }
                else
                {
                    server.Send(data, reza, SocketFlags.None);
                    Console.WriteLine("*");
                }
            }
            Console.WriteLine("Disconnecting from server ...");
            server.Shutdown(SocketShutdown.Both);
            server.Close();
            Console.ReadKey();
        }
    }
}

它的大小是否相同? - Buda Gavril
完全具有相同的大小 - R.MSZD
当你在十六进制编辑器中比较两个文件时会发生什么?它们看起来完全不同吗?尝试发送一个小于2KB的文件并进行比较。 - Alexey
我比较它们,它们是不同的,当传输小于2KB的文件时,所有字节都为零!!!!! - R.MSZD
1个回答

3
问题在于你忽略了Receive()的返回值。无法保证你的缓冲区完全填满。
返回值告诉你套接字实际从网络中读取了多少数据。调整接收代码以考虑此返回值。同样适用于发送。如果内部套接字缓冲区已满,它将报告比您请求发送的字节数少的字节数。
除此之外,我建议你开始给变量起有意义的名称。你的代码不太易读。
以下是更好的接收例程:
var fileBuffer = new byte[size];
var receiveBuffer = new byte[2048];
var bytesLeftToReceive = size;
var fileOffset = 0;

while (bytesLeftToReceive > 0)
{
    //receive
    var bytesRead = client.Receive(receiveBuffer);
    if (bytesRead == 0) 
        throw new InvalidOperationException("Remote endpoint disconnected");

    //if the socket is used for other things after the file transfer
    //we need to make sure that we do not copy that data
    //to the file
    int bytesToCopy = Math.Min(bytesRead, bytesLeftToReceive);

    // copy data from our socket buffer to the file buffer.
    Buffer.BlockCopy(receiveBuffer, 0, bytesLeftToReceive, fileBuffer, fileOffset);

    //move forward in the file buffer
    fileOffset += bytesToCopy;

    //update our tracker.
    bytesLeftToReceive -= bytesToCopy;
}

然而,由于你正在传输大文件,直接将其写入文件流是否更好?
var stream = File.Create(@"C:\path\to\file.dat");
var receiveBuffer = new byte[2048];
var bytesLeftToReceive = size;

while (bytesLeftToReceive > 0)
{
    //receive
    var bytesRead = client.Receive(receiveBuffer);
    if (bytesRead == 0) 
        throw new InvalidOperationException("Remote endpoint disconnected");

    //if the socket is used for other things after the file transfer
    //we need to make sure that we do not copy that data
    //to the file
    int bytesToCopy = Math.Min(bytesRead, bytesLeftToReceive);

    // write to file
    stream.Write(receiveBuffer, 0, bytesToCopy);

    //update our tracker.
    bytesLeftToReceive -= bytesToCopy;
}

在发送端,我会像这样做:

// read directly from the file to reduce memory usage.
var fileName = @"....";
using (var file = File.OpenRead(fileName))
{
    var sendBuffer = new byte[2048];
    var fileSize = BitConverter.GetBytes((int)file.Length);
    server.Send(fileSize, fileSize.Length, SocketFlags.None);

    var bytesLeftToTransmit = fileSize;
    while (bytesLeftToTransmit > 0)
    {
        var dataToSend = file.Read(sendBuffer, 0, sendBuffer.Length);
        bytesLeftToTransmit -= dataToSend;

        //loop until the socket have sent everything in the buffer.
        var offset=0;
        while (dataToSend > 0)
        {
            var bytesSent = socket.Send(sendBuffer, offset, dataToSend);
            dataToSend -= bytesSent;
            offset += bytesSent;
        }
    }
}

现在有一个被优化的Socket.SendFile方法可以用于发送文件。你可以在开放的套接字中随时调用它。


1
问题在于你忽略了Receive()的返回值 - 每一个套接字相关的问题都会出现这种情况。 - CodeCaster
我是初学者,不太理解你的代码!:( 你能帮我修复一下吗?! - R.MSZD
2
如果你想学习和了解它,那我强烈建议你将其粘贴到Visual Studio控制台应用程序中。或者停止使用sockets。 - jgauffin
有人能告诉我,接收方如何知道发送的文件的“大小”?我们是否首先发送另一条消息,告知数据类型和文件大小?然后再发送文件? - Fandi Susanto
you send it before - jgauffin

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接