使用套接字编程C#发送大文件 [英] Sending big files in socket programming c#

查看:68
本文介绍了使用套接字编程C#发送大文件的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想用c#发送带有套接字的大文件.可以传输文件,但是当我想打开它时,我看到它已损坏.有什么问题?我在客户端代码中将文件拆分为2KB的数组,并将其发送.然后,在服务器代码中,我收到了它,并将其放入字节数组中并转换为文件.

I want to send big files with socket in c#. File can be transferred but when I want to open it, I see it is damaged. What's the problem? I broke the file to 2KB in array in client code and send it. Then, in server code, I received it and put it in byte array and convert to file.

服务器代码:

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

推荐答案

问题是您忽略了 Receive()的返回值.不能保证您的缓冲区将被完全填满.

The problem is that you ignore the return value from Receive(). There is no guarantee what-so-ever that your buffer will be completely filled.

返回值告诉您套接字实际从网络读取了多少数据.调整接收代码,以便将其考虑在内.发送也一样.如果内部套接字缓冲区已满,它将报告发送的字节数少于您请求发送的字节数.

The return value tells you how much data that the socket actually read from the network. Adjust your receiving code so it's taken into account. Same goes for the send. If the internal socket buffer gets full it will report less bytes sent than you have requested to send.

除此之外,我建议您开始给变量赋予有意义的名称.您的代码不是很容易阅读.

Other than that, I suggest you start to give your variables meaningful names. Your code is not very readable as is.

这是一个更好的接收例程:

Here is a better receive routine:

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

但是,由于要传输大文件,将它直接写到文件流中不是更好吗?

However, since you are transferring large files, isn't it better to write it directly to a file stream?

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

在发送方,我会做这样的事情:

On the send side I would do something like this:

// 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 已优化为发送文件.您可以随时为打开的套接字调用它.

Finally you have a Socket.SendFile which have been optimized to send files. You can invoke it at any time for an open socket.

这篇关于使用套接字编程C#发送大文件的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆