#c# #image #networking #tcp #client
#c# #изображение #сеть #tcp #клиент
Вопрос:
У меня есть клиентское приложение и серверное приложение. Клиентское приложение активирует эту функцию каждые 40 миллисекунд: * Обратите внимание, что nstream является экземпляром NetworkStream.
private void SendScreen(NetworkStream nstream)
{
StreamWriter writer = new StreamWriter(nstream);
ScreenCapture sc = new ScreenCapture();
System.Drawing.Image img = sc.CaptureScreen();
MemoryStream ms = new MemoryStream();
img.Save(ms, System.Drawing.Imaging.ImageFormat.Bmp);
byte[] buffer = new byte[ms.Length];
ms.Seek(0, SeekOrigin.Begin);
ms.Read(buffer, 0, buffer.Length);
Console.WriteLine(buffer.Length);
writer.WriteLine(buffer.Length);
writer.Flush();
nstream.Write(buffer, 0, buffer.Length);
}
и это код серверного приложения для получения изображения:
private async void ShowImage()
{
while (true)
{
string num = await reader.ReadLineAsync();
Console.WriteLine(num);
int ctBytes = int.Parse(num);
byte[] buffer = new byte[ctBytes];
await stream.ReadAsync(buffer, 0, buffer.Length);
MemoryStream ms = new MemoryStream(buffer);
Image img = Image.FromStream(ms);
Bitmap bmp = new Bitmap(img);
this.Height = bmp.Height;
this.Width = bmp.Width;
this.BackgroundImage = bmp;
}
}
Я не могу придумать ни одной части, которая нарушает эти действия.
На первой итерации серверного приложения все работает (хотя вместо скриншота я вижу черный экран, но количество отправленных и полученных байтов совпадает.)
На второй итерации, когда я записываю строку num, она показывает ошибку, а затем останавливается (потому что ее нельзя разобрать в int).
Комментарии:
1. Использование
reader.ReadLineAsync
кажется странным, поскольку это будет интерпретировать ваши данные как строку символов, которая явно не является тем, что отправляется.2. Сначала я отправляю буфер. Длина, которая является int (преобразование в строку символов)..
3. Отправьте его как
int
then, а не как строку символов.4. какая разница?
5. Я успешно воспроизвел вашу проблему. Моя установка отправляет заголовок перед каждым изображением и ожидает подтверждения. Но, как и в вашем подходе, я могу получить только первое изображение (неполное) Прямо сейчас я озадачен тем, почему это так… Я собираюсь запустить сеанс и посмотреть, что я могу придумать.
Ответ №1:
По сути, я подозреваю, что у вас проблема с синхронизацией, как и у меня, когда я попытался воспроизвести вашу проблему (успешно).
Будь то a Header
или single Integer
, который объявляет о предстоящей Bitmap
передаче. Все это ужасно легко рассинхронизировать.
Если вы не хотите погрузиться в сетевую синхронизацию и управление потоками, я рекомендую использовать один TransmissionObject
для каждой передачи между сервером и клиентом (и, возможно, другой наоборот)
Этот объект может хранить любые данные, которые должны быть переданы. (через наследование)
Наконец, при размещении объекта в сетевом потоке вы можете использовать любой согласованный Serializer
Serializer
Выполняет всю работу по синхронизации информации туда, где она необходима (метаинформация, такая как длина и, наконец, содержимое)
Ну, в моем проекте repro я придумал этот класс для хранения растрового изображения.
В качестве примера того, как использовать его на сервере:
public void Send(Bitmap bmp)
{
// prepare Bitmap
BitmapTransmission bt = new BitmapTransmission(bmp);
// try transmitting the object
try
{
// lock to exclude other Threads of using the stream
lock (m_objSendLock)
{
bt.Send(m_stream);
}
}
catch (BitmapTransmissionException ex)
{ // will catch any exception thrown in bt.Send(...)
Debug.Print("BitmapHeaderException: " ex.Message);
///BitmapTransmissionException provides a Property `CloseConnection`
/// it will inform you whether the Exception is recoverable
}
}
Где m_objSendLock
— объект для предотвращения одновременного доступа к сетевому потоку.
m_stream
подключен ли сетевой поток к клиенту.
Echo
это механизм ведения журнала.
Клиент может получать такие передачи через
public void Receive() {
BitmapTransmission bt = bt.Receive(m_stream);
// check for exceptions maybe?
pictureBox1.Image = bt.Bitmap;
}
Где как снова m_stream
это сетевой поток, подключенный к серверу.
Если вы хотите взглянуть на (грязный, но работающий) проект repro, скажите об этом.
Комментарии:
1. Привет, я попытался использовать только BinaryFormatter (без объекта BitmapTransmission, поскольку он мне действительно не нужен — зачем мне вставлять растровое изображение в другой объект?), И он выдает следующее исключение: Двоичный поток ‘0’ не содержит допустимого двоичного заголовка. Возможные причины — недопустимый поток или изменение версии объекта между сериализацией и десериализацией.
2. Возможно, я недостаточно подчеркнул этот аспект, но причина, по которой вы хотите поместить растровое изображение в один и тот же объект, заключается в синхронизации его передачи с метаинформацией!
3. Я попробовал ваш класс, и он выдает исключение SerializationException при получении
4. Отправляете ли вы что-нибудь, кроме объекта, в сетевом потоке? Проверьте контекст, в котором я его использую здесь
5. ну да, я это сделал, но я перестаю отправлять другие вещи, когда отправляю объект.