在socket编程中发送大文件c#

时间:2016-01-14 12:17:16

标签: c# file sockets transfer

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

1 个答案:

答案 0 :(得分:2)

问题是您忽略了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已经过优化以发送文件。您可以随时为打开的套接字调用它。