0

我已经尝试了所有我能找到的解决方案,但似乎没有任何效果。文本文件以外的任何内容都会损坏;有人说 TCP 不能发送超过 8KB,所以我试图解决这个问题,我想我做到了。现在,当我发送一个文本文件(无论它是什么大小)时,它可以完美地到达,但其他任何东西都会损坏。我知道切割代码对性能的影响很大,但我稍后会考虑。

这是我的发件人代码:

private string SendFile(string tosend, string tosendname)
{
    ipadd = IPAddress.Parse(textBox2.Text);
    ep = new IPEndPoint(ipadd, 6112);
    Sender = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.IP);
    Sender.Connect(ep);
    Thread.Sleep(100);
    byte[] filetosend = System.IO.File.ReadAllBytes(tosend);
    FileStream fs = new FileStream(tosend, FileMode.Open, FileAccess.Read);
    //Read byte from image
    fs.Read(filetosend, 0, filetosend.Length);
    fs.Flush();
    fs.Close();
    int countt = filetosend.Count();
    int dividedcount = countt / 7000;
    Sender.Send(Encoding.ASCII.GetBytes("filesize#" + filetosend.Count().ToString()));
    Thread.Sleep(500);
    List<byte> cuttedtosend = new List<byte>();
    for (int counti = 0; counti < dividedcount; counti++)
    {
        cuttedtosend = new List<byte>();
        for (int index = 0; index < 7000; index++)
        {
            cuttedtosend.Add(filetosend[(filetosend.Count() - countt) + index]);
        }
        Sender.Send(cuttedtosend.ToArray());
        Thread.Sleep(100);
        countt -= 7000;
        richTextBox1.Invoke((MethodInvoker)delegate { richTextBox1.AppendText("Countt = " + countt + "\n"); });
        richTextBox1.Invoke((MethodInvoker)delegate { richTextBox1.AppendText("Counti = " + counti + "\n"); });
    }
    richTextBox1.Invoke((MethodInvoker)delegate { richTextBox1.AppendText("Done"); });
    cuttedtosend = new List<byte>();
    for (int index = filetosend.Count() - countt; index < filetosend.Count(); index++)
    {
        //richTextBox1.Invoke((MethodInvoker)delegate { richTextBox1.AppendText(index + "this is 2 \n"); });
        cuttedtosend.Add(filetosend[index]);
    }
    Sender.Send(cuttedtosend.ToArray());
    countt -= countt;
    return "";
}

这是我的接收代码:

private async void StartReceiving()
{
    List<byte> neededbytes = new List<byte>();
    receivedbyte = new byte[InputForm.s];
    Receiver.Bind(new IPEndPoint(IPAddress.Parse("0"), 6112));
    Receiver.Listen(1000);
    string filename = "Downloadedfile";
    bool cont = false;
    while (true)
    {
        Client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.IP);
        Client = Receiver.Accept();
        int filesize = 0;
        byte[] receivechecker = new byte[100];
        Client.Receive(receivechecker);
        if(Encoding.ASCII.GetString(receivechecker).Contains("filesize#"))
        {
            filesize = Convert.ToInt32(Encoding.ASCII.GetString(receivechecker).Remove(0, 9));
            Client.Receive(receivechecker);
        }
        if (Encoding.ASCII.GetString(receivechecker).Contains("#100254#"))
        {
            string[] splttedtext = Encoding.ASCII.GetString(receivechecker.ToArray()).Split('#');
            if (splttedtext[0] == "mess")
            {
                MessageBox.Show(splttedtext[2]);
            }
            else if (splttedtext[0] == "filename")
            {
                //MessageBox.Show(splttedtext[2]);
                filename = splttedtext[2];
                //filename.Replace(@"\", @"/");
                cont = true;
            }
        }
        else
        {
            List<byte> tosave = new List<byte>();
            richTextBox1.Invoke((MethodInvoker)delegate { richTextBox1.AppendText(filesize.ToString() + "\n"); });
            int countt = filesize / 7000;
            FileStream writer = File.Create("DownloadedFile.jpg");
            for (int counti = 0; counti < countt; counti++)
            {
                byte[] toadd = new byte[7000];
                richTextBox1.Invoke((MethodInvoker)delegate { richTextBox1.AppendText("Counti = " + counti.ToString() + "\n"); });
                Client.Receive(toadd);
                writer.Write(toadd,0,toadd.Count());
                neededbytes.AddRange(toadd);
                filesize -= 7000;
            }
            richTextBox1.Invoke((MethodInvoker)delegate { richTextBox1.AppendText(filesize.ToString() + "\n"); });
            byte[] toadds = new byte[filesize];
            Client.Receive(toadds);
            writer.Write(toadds,0,toadds.Count());
            writer.Close();
            neededbytes.AddRange(toadds);
            filesize -= filesize;
        }
   }

先谢谢了

编辑:我刚刚尝试发送一个 7mb 的文本文件,它完成了.......

4

2 回答 2

3

最直接的问题是您正在保存不一定收到的字节。例如,您有:

        for (int counti = 0; counti < countt; counti++)
        {
            byte[] toadd = new byte[7000];
            richTextBox1.Invoke((MethodInvoker)delegate { richTextBox1.AppendText("Counti = " + counti.ToString() + "\n"); });
            Client.Receive(toadd);
            writer.Write(toadd,0,toadd.Count());
            neededbytes.AddRange(toadd);
            filesize -= 7000;
        }

的文档Receive说该方法将接收最多您请求的字节数。它返回比您请求的字节少的字节并不少见,尤其是在文件末尾(因为它不能接收超过文件长度)。

你需要写:

var bytesRead = Client.Receive(toadd);
writer.Write(toadd, 0, bytesRead);  // only write as many bytes as you've read

通常,您的代码非常复杂,并且您还有其他几个可能的问题正等着咬您。例如,发送文件大小的代码休眠 500 毫秒,这恰好足以让接收器读取发送的字节数。没有那种睡眠,你的代码就会失败。

您有接收文件名的代码,但没有发送它的代码。

我建议您消除 ASCII 标记并以二进制形式发送内容。以下是您重写的发送方法。

private string SendFile(string tosend, string tosendname)
{
    ipadd = IPAddress.Parse(textBox2.Text);
    ep = new IPEndPoint(ipadd, 6112);
    Sender = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.IP);
    Sender.Connect(ep);

    byte[] filetosend = System.IO.File.ReadAllBytes(tosend);
    byte[] filesizeBytes = BitConverter.GetBytes(filetosend.Length);
    Sender.Send(filesizeBytes); // sends the length as an integer

    // note: You could use Socket.Send(filetosend) here.
    // but I'll show an example of sending in chunks.
    int totalBytesSent = 0;
    while (totalBytesSent < filetosend.Length)
    {
        int bytesLeft = filetosend.Length - totalBytesSent;
        int bytesToSend = Math.Min(bytesLeft, 7000);
        Sender.Send(filetosend, totalBytesSent, bytesToSend);
        richTextBox1.Invoke((MethodInvoker)delegate 
            { richTextBox1.Append(totalBytesSent + " bytes sent\n"); });
        totalBytesSent += bytesToSend;
    }
    richTextBox1.Invoke((MethodInvoker)delegate { richTextBox1.AppendText("Done"); });
    return "";
}

接收器代码也进行了类似的简化:

private async void StartReceiving()
{
    Receiver.Bind(new IPEndPoint(IPAddress.Parse("0"), 6112));
    Receiver.Listen(1000);
    string filename = "Downloadedfile";
    bool cont = false;
    while (true)
    {
        Client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.IP);
        Client = Receiver.Accept();

        // read the length
        byte[] filesizeBytes = new byte[4];
        int totalBytesReceived = 0;
        while (totalBytesReceived < 4)
        {
            int bytesRead = Client.Receive(
                filesizeBytes, totalBytesReceived, 4-totalBytesReceived);
            totalBytesReceived += bytesRead;
        }
        int filesize = BitConverter.ToInt32(filesizeBytes);
        richTextBox1.Invoke((MethodInvoker)delegate 
            { richTextBox1.AppendText(filesize.ToString() + "\n"); });

        // now read the file
        using (FileStream writer = File.Create("DownloadedFile.jpg"))
        {
            byte[] readBuffer = new byte[7000];
            totalBytesReceived = 0;
            while (totalBytesReceived < filesize)
            {
                int bytesToRead = Math.Min(7000, filesize - totalBytesReceived);
                int bytesRead = Client.Receive(readBuffer, 0, bytesToRead);
                totalBytesRead += bytesRead;
                writer.Write(readBuffer, 0, bytesRead);
                richTextBox1.Invoke((MethodInvoker)delegate 
                    { richTextBox1.AppendText("Read " + bytesRead.ToString() + "bytes\n"); });
            }
            richTextBox1.Invoke((MethodInvoker)delegate 
                { richTextBox1.AppendText("Done. " + totalBytesRead.ToString() + " bytes\n"); });
        }
   }

如果您想发送文件名,那么我建议将其转换为 UTF8 ( Encoding.UTF8.GetBytes(filename)),然后发送一个 int(4 个字节)来说明它的长度,然后是缓冲区。要接收它,请读取 4 字节的文件名长度,就像我展示的如何读取文件大小一样,然后读取文件名的字节数,然后转换回字符串 ( Encoding.UTF8.GetString(bytes, 0, filenameLength))。

请原谅代码中的任何拼写错误或小错误。我是凭记忆做到这一点的,并试图与您的编码风格保持一致。

于 2013-09-04T18:33:59.717 回答
1

我怀疑您希望收到您发送的相同块;即,记录边界将被保留。事实并非如此。TCP 保证发送的每个字节都将被接收并保留该顺序;但你可以做 1 个大的 10k 发送和接收 10k 1 字节的消息。

于 2013-09-04T18:39:54.070 回答