我遇到了两个问题,在尝试了我在 stackoverflow 上读到的一些技术后,问题仍然存在。我正在尝试使用下面的代码将文件从服务器发送到客户端,但问题是该文件总是短几个字节,导致文件损坏。第二个问题是尽管实现了,但流并未关闭末尾的零长度数据包表示传输已完成而无需关闭连接。
服务器代码片段:
/*
* Received request from client for file, sending file to client.
*/
//open file to send to client
FileStream fs = new FileStream(fileLocation, FileMode.Open, FileAccess.Read);
byte[] data = new byte[1024];
long fileSize = fs.Length;
long sent = 0;
int count = 0;
while (sent < fileSize)
{
count = fs.Read(data, 0, data.Length);
netStream.Write(data, 0, count);
sent += count;
}
netStream.Write(new byte[1024], 0, 0); //send zero length byte stream to indicate end of file.
fs.Flush();
netStream.Flush();
客户端代码片段:
TcpClient client;
NetworkStream serverStream;
/*
* [...] client connect
*/
//send request to server for file
byte[] dataToSend = SerializeObject(obj);
serverStream.Write(dataToSend, 0, dataToSend.Length);
//create filestream to save file
FileStream fs = new FileStream(fileName, FileMode.Create, FileAccess.Write);
//handle response from server
byte[] response = new byte[client.ReceiveBufferSize];
byte[] bufferSize = new byte[1024];
int bytesRead;
while ((bytesRead = serverStream.Read(bufferSize, 0, bufferSize.Length)) > 0 && client.ReceiveBufferSize > 0)
{
Debug.WriteLine("Bytes read: " + bytesRead);
fs.Write(response, 0, bytesRead);
}
fs.Close();
最佳答案
使用 UDP,您可以传输有效的空数据包,但 TCP 不允许您这样做。在应用层,TCP 协议(protocol)是字节流,所有数据包级别的内容都被抽象出来。发送零字节不会导致客户端的流级别发生任何事情。
发出文件传输结束信号可以像让服务器在发送最后一个数据 block 后关闭连接一样简单。客户端将收到最终的数据包,然后注意到套接字已关闭,这表明数据已完全传递。此方法的缺陷是 TCP 连接可能会因其他原因而关闭,从而使客户端处于一种认为自己拥有所有数据的状态,即使连接因其他原因而断开也是如此。
因此,即使您要使用“完成时关闭”方法来表示传输结束,您也需要有一种机制,允许客户端识别文件实际上已完成。
最常见的形式是在传输开始时发送一个 header block ,告诉您有关正在传输的数据的信息。这可能像 4 字节长度值一样简单,也可能是一个可变长度描述符结构,其中包括有关文件的各种元数据,例如文件的长度、名称、创建/修改时间以及您可以使用的校验和或哈希值验证收到的内容。客户端首先读取 header ,然后将流中的其余数据作为内容进行处理。
让我们采用最简单的情况,在流的开头发送 4 字节长度指示符。
服务器代码:
public void SendStream(Socket client, Stream data)
{
// Send length of stream as first 4 bytes
byte[] lenBytes = BitConverter.GetBytes((int)data.Length);
client.Send(lenBytes);
// Send stream data
byte[] buffer = new byte[1024];
int rc;
data.Position = 0;
while ((rc = data.Read(buffer, 0, 1024)) > 0)
client.Send(buffer, rc, SocketFlags.None);
}
客户端代码:
public bool ReceiveStream(Socket server, Stream outdata)
{
// Get length of data in stream from first 4 bytes
byte[] lenBytes = new byte[4];
if (server.Receive(lenBytes) < 4)
return false;
long len = (long)BitConverter.ToInt32(lenBytes, 0);
// Receive remainder of stream data
byte[] buffer = new byte[1024];
int rc;
while ((rc = server.Receive(buffer)) > 0)
outdata.Write(buffer, 0, rc);
// Check that we received the expected amount of data
return len == outdata.Position;
}
没有太多的错误检查等方式,以及在各个方向上阻止代码,但你明白了。
关于C# TCP NetworkStream 缺少几个字节的数据,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23462066/