неверная отправка .txt файла TCP
почему, когда я отправляю текст из txt файла, он отправляется неправильно. Для теста я взял файл, в котором было 5029 строк текста такого формата:
blablalba:blabla:Hello my friends,
однако на сервер пришло 5036 строк +-1. Вот код сервера:
using (NetworkStream stream = client.GetStream())
{
BinaryFormatter outformat = new BinaryFormatter();
using (FileStream fs = new FileStream("Accounts.txt", FileMode.Append))
{
using (BinaryWriter bw = new BinaryWriter(fs))
{
// Получаем кол-во строк передаваемого файла
int line = int.Parse(outformat.Deserialize(stream).ToString());
// Получаем размер файла
int count = int.Parse(outformat.Deserialize(stream).ToString());
// Записываем по 1024 байта
for (int i = 0; i < count; i += 1024)
{
byte[] buf = (byte[])(outformat.Deserialize(stream));
bw.Write(buf);
}
Logger.WarninMessage = "Recv [" + DateTime.Now.ToString("T") + "][" + ipClientConnected + "]: " + line;
}
}
}
Вот код клиента:
using (client = new TcpClient())
{
client.Connect(address, port);
BinaryFormatter format = new BinaryFormatter();
using(NetworkStream stream = client.GetStream())
{
// Отправляем кол-во строк в файле
int line = File.ReadAllLines("file.txt").Length;
format.Serialize(stream, line.ToString());
// Отравка файла
using (FileStream file = new FileStream("file.txt", FileMode.Open))
{
byte[] buf = new byte[1024];
BinaryReader reader = new BinaryReader(file);
// Отправляем размер файла
format.Serialize(stream, file.Length.ToString());
while ((reader.Read(buf, 0, 1024)) > 0)
{
format.Serialize(stream, buf);
}
}
}
}
Ответы (1 шт):
Вы перемудрили. BinaryFormatter устарел и не является безопасным, его нельзя использовать для передачи данных по сети. К тому же проще передавать все файлы как двоичные.
Вот вам 2 метода
private static void SendFile(string path, Stream stream)
{
FileInfo file = new FileInfo(path);
if (!file.Exists)
{
// файл не найден
throw new IOException("File not found: " + path);
}
long length = file.Length; // длина файла в байтах
string name = file.Name; // имя файла
Encoding utf8 = new UTF8Encoding(false); // UTF-8 без BOM, самый стандартный стандарт из всех стандартов
using (BinaryWriter bw = new BinaryWriter(stream, utf8, true))
{
bw.Write(length);
bw.Write(name);
}
using (FileStream fs = file.OpenRead())
{
fs.CopyTo(stream);
}
}
private static void ReceiveFile(string directory, Stream stream)
{
Directory.Create(directory); // создает каталог, если он не существует
Encoding utf8 = new UTF8Encoding(false);
long length;
string name;
using (BinaryReader br = new BinaryReader(stream, utf8, true))
{
length = br.ReadInt64();
name = br.ReadString();
}
// Path.GetFileName для безопасности. Вдруг кто-то пришлет серверу полный путь,
// чтобы он записал что-то в папку Windows или повредил свои файлы. Этого нельзя допустить.
string path = Path.Combine(directory, Path.GetFileName(name));
using (FileStream fs = File.Create(path))
{
byte[] buffer = new byte[1024];
long received = 0;
while (received < length)
{
int toReceive = (int)Math.Min(buffer.Length, length - received);
int bytesReceived = stream.Read(buffer, 0, toReceive);
if (bytesReceived == 0) // Неожиданный конец потока
throw new IOException("Unexpected end of stream while receiving file: " + path);
received += bytesReceived;
fs.Write(buffer, 0, bytesReceived);
}
}
}
Тогда ваш код из вопроса будет выглядеть так
using (var client = new TcpClient())
{
client.Connect(address, port);
using (NetworkStream stream = client.GetStream())
{
try
{
SendFile("file.txt", stream);
// здесь файл успешно отправлен
}
catch (Exception ex)
{
// произошла ошибка при отправке (ex.Message)
}
}
}
using (NetworkStream stream = client.GetStream())
{
try
{
ReceiveFile("ReceivedFiles", stream); // указываем каталог, куда записать полученный файл
// здесь файл успешно получен
}
catch (Exception ex)
{
// произошла ошибка при получении (ex.Message)
}
}
При этом отправлять и принимать можно с любой стороны. Имя сохраняемого на диск файла задается отправляющим.
Смысла различать текстовые файлы и двоичные нет никакого, конечно до тех пор, пока вы не захотите текстовые файлы упаковывать при передаче для экономии трафика (см. GZipStream). Но я не рекомендую вам этим заниматься, пока вы не настроите полностью и не отладите свой протокол передачи данных.