#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);
не завершит выполнение до конца потока, что не позволит запустить инициализацию окна.
Что вам нужно, так это
- Запуск части подключения в отдельном потоке, чтобы он не блокировал поток пользовательского интерфейса
- Способ различения кадров изображений и получения их по одному для помещения в picturebox
Для достижения первого пункта вы можете использовать System.Threading.Task
. Вам нужно помнить, что вы не можете получить доступ к пользовательскому интерфейсу напрямую из отдельного потока, поэтому вам нужно будет использовать Invoke() в picturebox. Чтобы достичь 2-го, я бы разработал какой-нибудь простой протокол связи и скопировал данные сетевого потока для разделения MemoryStream
, чтобы создать из него изображение.
Например, всегда отправляйте один Int32
, указывающий количество байтов для чтения (т. е. Размер отправленного изображения), затем n байтов, содержащих изображение. Затем вы можете перейти к чтению одного ввода из сетевого потока, определить объем данных для копирования MemoryStream
, а затем скопировать этот объем для создания изображения.