Как непрерывно отправлять сетевой поток в c#

#c# #.net #networking #server #tcp

Вопрос:

Я пытаюсь понять связь tcp-сервера с клиентом для школы.

То, что я пытаюсь сделать, — это отправить клиенту скриншот экрана.

Все работает нормально, но теперь я хочу, чтобы он отправлял его снова и снова (в значительной степени скриншот), и я сам не могу найти решение.

Вот код:

Сервер

 class Program
{

    static void Main(string[] args)
    {
        Run();
    }

    private static void Run()
    {
        StartClientAsync();
    }

    public static async System.Threading.Tasks.Task StartClientAsync()
    {
        try
        {
            IPAddress ipAddress = Dns.Resolve("localhost").AddressList[0];
            TcpListener server = new TcpListener(ipAddress, 9500);
            server.Start();
            Console.WriteLine("Waiting for client to connect...");

            while (true)
            {
                if (server.Pending())
                {
                    Bitmap tImage;
                    byte[] bStream;

                    while (true)
                    {
                        using (var client = await server.AcceptTcpClientAsync())
                        {
                            Console.WriteLine("Connected");
                            NetworkStream nStream = client.GetStream();

                            try
                            {
                                tImage = Screenshot();
                                bStream = ImageToByte(tImage);
                                nStream.Write(bStream, 0, bStream.Length);
                                Console.WriteLine("Sent Image");
                            }
                            catch (SocketException e1)
                            {
                                Console.WriteLine("SocketException: "   e1);
                            }

                        }
                        Console.WriteLine("Disconnected");
                    }
                }
            }
        }

        catch (SocketException e1)
        {
            Console.WriteLine("SocketException: "   e1);
        }
    }

    static byte[] ImageToByte(Image iImage)
    {
        MemoryStream mMemoryStream = new MemoryStream();
        iImage.Save(mMemoryStream, System.Drawing.Imaging.ImageFormat.Png);
        return mMemoryStream.ToArray();
    }

    private static Bitmap Screenshot()
    {
        var bmpScreenshot = new Bitmap(Screen.PrimaryScreen.Bounds.Width,
            Screen.PrimaryScreen.Bounds.Height,
            PixelFormat.Format32bppArgb);

        // Create a graphics object from the bitmap.
        var gfxScreenshot = Graphics.FromImage(bmpScreenshot);

        // Take the screenshot from the upper left corner to the right bottom corner.
        gfxScreenshot.CopyFromScreen(Screen.PrimaryScreen.Bounds.X,
            Screen.PrimaryScreen.Bounds.Y,
            0,
            0,
            Screen.PrimaryScreen.Bounds.Size,
            CopyPixelOperation.SourceCopy);

        return bmpScreenshot;
    }
}
 

Клиент

 public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
    }

    private void Form1_Load(object sender, EventArgs e)
    {
        Start();
    }

    public void Start()
    {
        IPAddress ipAddress = IPAddress.Parse("127.0.0.1");
        using (TcpClient client = new TcpClient())
        {
            client.Connect(ipAddress, 9500);
            Log("Connected...");

            Thread.Sleep(500);
            NetworkStream nNetStream = client.GetStream();
            Image returnImage = Image.FromStream(nNetStream);
            pictureBox1.Image = returnImage;
        }
    }

    private void Log(string text)
    {
        if (ControlInvokeRequired(textBox1, () => Log(text))) return;
        Console.WriteLine(text);
        textBox1.AppendText(text);
        textBox1.AppendText(Environment.NewLine);
    }

    public bool ControlInvokeRequired(Control c, Action a)
    {
        if (c.InvokeRequired) c.Invoke(new MethodInvoker(delegate { a(); }));
        else return false;

        return true;
    }

    private void button1_Click(object sender, EventArgs e)
    {
        pictureBox1.Image.Save(DateTime.Now.Ticks.ToString("D19")   ".png", ImageFormat.Png);
    }
}
 

Я попытался использовать дополнительный цикл while, но когда я его выполняю, клиент не запустит форму, пока я не остановлю сервер вручную.

Изображение должно обновляться в поле «Картинка» каждый раз при отправке нового изображения.

Спасибо

Ответ №1:

Ваша проблема в том, что

 Image returnImage = Image.FromStream(nNetStream);
 

не завершит выполнение до конца потока, что не позволит запустить инициализацию окна.
Что вам нужно, так это

  1. Запуск части подключения в отдельном потоке, чтобы он не блокировал поток пользовательского интерфейса
  2. Способ различения кадров изображений и получения их по одному для помещения в picturebox

Для достижения первого пункта вы можете использовать System.Threading.Task . Вам нужно помнить, что вы не можете получить доступ к пользовательскому интерфейсу напрямую из отдельного потока, поэтому вам нужно будет использовать Invoke() в picturebox. Чтобы достичь 2-го, я бы разработал какой-нибудь простой протокол связи и скопировал данные сетевого потока для разделения MemoryStream , чтобы создать из него изображение.

Например, всегда отправляйте один Int32 , указывающий количество байтов для чтения (т. е. Размер отправленного изображения), затем n байтов, содержащих изображение. Затем вы можете перейти к чтению одного ввода из сетевого потока, определить объем данных для копирования MemoryStream , а затем скопировать этот объем для создания изображения.