#c# #sockets #ssl
#c# #сокеты #ssl
Вопрос:
Я пытаюсь безопасно передавать файлы между двумя устройствами, поэтому я использую SslStream, подключенный к TcpClient. Документы и текстовые файлы отображаются просто отлично, но файлы изображений отображаются некорректно. Ниже приведен код сервера:
listener = new TcpListener(IPAddress.Any, 1337);
listener.Start();
while (true)
{
TcpClient client = listener.AcceptTcpClient();
SslStream sslStream = new SslStream(client.GetStream(), false, new RemoteCertificateValidationCallback(CertificateValidationCallback), new LocalCertificateSelectionCallback(CertificateSelectionCallback));
var certificate = Connection.GetClientCertificate(((IPEndPoint)client.Client.RemoteEndPoint).Address.ToString());
try
{
sslStream.AuthenticateAsServer(certificate, true, SslProtocols.Default, true);
sslStream.ReadTimeout = 5000;
sslStream.WriteTimeout = 5000;
var messageData = ReadMessage(sslStream);
var mode = messageData[0];
var tokenBytes = messageData.Splice(1, 16);
var fileNameBytes = messageData.Splice(17, 128);
var fileBytes = messageData.Splice(146);
var fileName = Encoding.ASCII.GetString(fileNameBytes).TrimEnd('');
using (var tempFile = new FileStream(fileName, FileMode.OpenOrCreate, FileAccess.Write))
{
tempFile.Write(fileBytes, 0, fileBytes.Length);
tempFile.Flush();
}
if (mode == 0)
tempFiles.Add(fileName);
Process.Start(fileName);
}
catch (AuthenticationException e)
{
MessageBox.Show("The other side failed to authenticate.");
}
finally
{
sslStream.Close();
client.Close();
}
}
И ReadMessage определяется следующим образом:
private static byte[] ReadMessage(SslStream sslStream)
{
byte[] buffer = new byte[2048];
MemoryStream stream = new MemoryStream();
int bytes = -1;
while (bytes != 0)
{
bytes = sslStream.Read(buffer, 0, buffer.Length);
stream.Write(buffer, 0, bytes);
}
return stream.ToArray();
}
И тогда клиентский код выглядит следующим образом:
TcpClient client = new TcpClient();
client.Connect(new IPEndPoint(IPAddress.Parse(ip), 1337));
SslStream sslStream = new SslStream(client.GetStream(), false, new RemoteCertificateValidationCallback(CertificateValidationCallback), new LocalCertificateSelectionCallback(CertificateSelectionCallback));
var certificate = Connection.GetClientCertificate(ip);
try
{
sslStream.AuthenticateAsClient(ip, new X509CertificateCollection() { certificate }, SslProtocols.Default, false);
sslStream.ReadTimeout = 5000;
sslStream.WriteTimeout = 5000;
sslStream.Write(data);
}
catch (AuthenticationException e)
{
MessageBox.Show("The other side failed to authenticate.");
}
finally
{
sslStream.Close();
client.Close();
}
И код, который вызывает его, просто выполняет:
var fileBytes = File.ReadAllBytes(file);
var tokenBytes = Encoding.UTF8.GetBytes(token);
var fileNameBytes = Encoding.UTF8.GetBytes(Path.GetFileName(file));
var buffer = new byte[145 fileBytes.Length];
buffer[0] = 1;
for (int i = 0; i < 16; i )
{
buffer[i 1] = tokenBytes[i];
}
for (int i = 0; i < fileNameBytes.Length; i )
{
buffer[i 17] = fileNameBytes[i];
}
for (int i = 0; i < fileBytes.Length; i )
{
buffer[i 145] = fileBytes[i];
}
SocketConnection.Send(ip, buffer);
Есть ли что-то изначально неправильное в том, что я делаю, или мне нужно сделать что-то другое для изображений?
РЕДАКТИРОВАТЬ: я изменил его, чтобы отразить текущий код, а также, после выполнения дампа необработанных байтов на обоих концах, похоже, что по какой-то причине байты перестраиваются, когда они передаются по проводам. Есть ли какой-либо способ гарантировать, что байты отображаются в исходном порядке?
Комментарии:
1. Я обновил свой ответ ниже, чтобы решить новую проблему.
Ответ №1:
В ReadMessage
вы записываете bytes.Length
байты в поток, независимо от количества фактически прочитанных байтов. Попробуйте:
private static byte[] ReadMessage(SslStream sslStream)
{
byte[] buffer = new byte[2048];
MemoryStream stream = new MemoryStream();
int bytes = -1;
while (bytes != 0)
{
bytes = sslStream.Read(buffer, 0, buffer.Length);
// Use "bytes" instead of "buffer.Length" here
stream.Write(buffer, 0, bytes);
}
return stream.ToArray();
}
Основываясь на ваших последующих действиях, вы также извлекаете данные файла из неправильной точки в буфере, и поэтому вы теряете первый байт файла.
Ваш код должен быть:
var fileBytes = messageData.Splice(145); // File data starts at 145, not 146
Комментарии:
1. О, да, конечно. Я изменил это, потому что это было определенно неправильно. Однако, похоже, это не исправило проблему. Размер файла теперь правильный, но когда я пытаюсь открыть изображение, он просто говорит, что оно повреждено. Я также добавил
tempfile.Flush()
при сохранении файла, чтобы убедиться, что все это записывается.
Ответ №2:
Возможно ли, что это конфликт между порядковым номером? Если байты с сервера имеют значение ABCDEF, а клиент видит байты изображения как BADCFE, то в этом проблема.
Я не работал с файлами изображений, но когда я считываю short или int вместо строки из байтов, поступающих по проводам, я делаю что-то вроде этого:
int intFromStream = IPAddress.NetworkToHostOrder(BitConverter.ToInt32(byteArrayWithLength4, 0));