我想使用TcpClient和TcpListener通过网络发送mp3文件。我使用套接字实现了这个解决方案,但是有一些问题所以我正在研究一种新的/更好的方式来发送文件。
我创建一个如下所示的字节数组: length_of_filename |文件名|文件
然后应该使用上面提到的类传输,但在服务器端,我读取的字节数组完全搞砸了,我不知道为什么。
我用来发送的方法:
public static void Send(String filePath)
{
try
{
IPEndPoint endPoint = new IPEndPoint(Settings.IpAddress, Settings.Port + 1);
Byte[] fileData = File.ReadAllBytes(filePath);
FileInfo fi = new FileInfo(filePath);
List<byte> dataToSend = new List<byte>();
dataToSend.AddRange(BitConverter.GetBytes(Encoding.Unicode.GetByteCount(fi.Name))); // length of filename
dataToSend.AddRange(Encoding.Unicode.GetBytes(fi.Name)); // filename
dataToSend.AddRange(fileData); // file binary data
using (TcpClient client = new TcpClient())
{
client.Connect(Settings.IpAddress, Settings.Port + 1);
// Get a client stream for reading and writing.
using (NetworkStream stream = client.GetStream())
{
// server is ready
stream.Write(dataToSend.ToArray(), 0, dataToSend.ToArray().Length);
}
}
}
catch (ArgumentNullException e)
{
Debug.WriteLine(e);
}
catch (SocketException e)
{
Debug.WriteLine(e);
}
}
}
然后在服务器端看起来如下:
private void Listen()
{
TcpListener server = null;
try
{
// Setup the TcpListener
Int32 port = Settings.Port + 1;
IPAddress localAddr = IPAddress.Parse("127.0.0.1");
// TcpListener server = new TcpListener(port);
server = new TcpListener(localAddr, port);
// Start listening for client requests.
server.Start();
// Buffer for reading data
Byte[] bytes = new Byte[1024];
List<byte> data;
// Enter the listening loop.
while (true)
{
Debug.WriteLine("Waiting for a connection... ");
string filePath = string.Empty;
// Perform a blocking call to accept requests.
// You could also user server.AcceptSocket() here.
using (TcpClient client = server.AcceptTcpClient())
{
Debug.WriteLine("Connected to client!");
data = new List<byte>();
// Get a stream object for reading and writing
using (NetworkStream stream = client.GetStream())
{
// Loop to receive all the data sent by the client.
while ((stream.Read(bytes, 0, bytes.Length)) != 0)
{
data.AddRange(bytes);
}
}
}
int fileNameLength = BitConverter.ToInt32(data.ToArray(), 0);
filePath = Encoding.Unicode.GetString(data.ToArray(), 4, fileNameLength);
var binary = data.GetRange(4 + fileNameLength, data.Count - 4 - fileNameLength);
Debug.WriteLine("File successfully downloaded!");
// write it to disk
using (BinaryWriter writer = new BinaryWriter(File.Open(filePath, FileMode.Append)))
{
writer.Write(binary.ToArray(), 0, binary.Count);
}
}
}
catch (Exception ex)
{
Debug.WriteLine(ex);
}
finally
{
// Stop listening for new clients.
server.Stop();
}
}
任何人都可以看到我遗失/做错的事吗?
答案 0 :(得分:4)
损坏是由服务器上的以下代码引起的:
// Loop to receive all the data sent by the client.
while ((stream.Read(bytes, 0, bytes.Length)) != 0)
{
data.AddRange(bytes);
}
stream.Read
并不总是填充bytes
缓冲区。如果TCP套接字没有更多可用数据,或者在读取消息的最后一个块时(除非它是缓冲区大小的精确倍数),它将不会被填充。
data.AddRange
调用会添加bytes
的所有内容(假设它始终为满)。因此,这有时会导致从之前的stream.Read
调用中添加数据。要解决此问题,您需要存储Read
返回的字节数,并仅添加此字节数:
int length;
while ((length = stream.Read(bytes, 0, bytes.Length)) != 0)
{
var copy = new byte[length];
Array.Copy(bytes, 0, copy, 0, length);
data.AddRange(copy);
}
请注意,您可能希望重新构建代码以提高性能和内存使用率(并且可能会使其更容易阅读)。在发送之前,您可以直接写入NetworkStream
,而不是将所有数据读入客户端的内存中。在服务器上,您无需将流中的所有内容复制到内存中。您可以读取4字节文件名长度并对其进行解码,然后读取并解码文件名,最后将流的其余部分直接复制到FileStream
(BinaryWriter
是不必要的)。
还值得注意的是,您正在使用FileMode.Append
创建输出文件。这意味着发送的每个文件都将附加到同名的前一个副本。您可能希望使用FileMode.Create
代替,如果文件已存在,则会覆盖。