我想在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();
}
}
}
答案 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已经过优化以发送文件。您可以随时为打开的套接字调用它。