c# - How to continuously send a network stream in c#
问题描述
I am trying to understand tcp server/client communication for school.
What I am trying to do is to send a screenshot of the screen to the client.
All is working fine, but now I want it to make it send over and over again (pretty much a screenshare) and I can't find a solution myself.
Here is the code:
Server
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;
}
}
Client
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);
}
}
I tried using a additional while loop, but when I execute it, the client won't start the form, until I stop the server manually.
The Image should be updated in the picturebox, every time a new image was sent.
Thanks
解决方案
你的问题是
Image returnImage = Image.FromStream(nNetStream);
直到流结束才会完成执行,因此不会让窗口初始化运行。你需要的是
- 在单独的线程中运行连接部分,因此它不会阻塞 UI 线程
- 区分图像帧并一一放入图片框的方法
要实现第一点,您可以使用System.Threading.Task
. 您需要记住,您不能直接从单独的线程访问 UI,因此您需要在图片框上使用 Invoke()。为了实现第二个,我会设计一些简单的通信协议并复制网络流数据以分离MemoryStream
以从中创建图像。
例如,总是发送一个Int32
指示要读取的字节数(即发送的图像的大小),然后发送包含图像的 n 个字节。然后您可以从网络流中读取单个 int,确定要复制到的数据量,MemoryStream
然后复制该量以创建 Image。
推荐阅读
- heroku - 以 Phoenix 应用程序作为代理运行 Vue 应用程序的正确方法
- typescript - 当 Visual Studio 想要显示 TypeScript 文件时,如何阻止 QtLinguist 启动?
- reactive-programming - 使用项目反应器将验证纳入反应链
- html - 在 html 标签中声明多个类时的优先顺序
- sapui5 - 多输入令牌未显示
- amazon-web-services - AWS Elastic Beanstalk - 保护生产环境免受意外部署
- vue.js - Vue.js。如何从字符串(道具)制作函数?
- blazor - 如何防止 MarkupString 在 Blazor 中自动关闭标签
- html - ngModel 属性在我的子 html 组件中不起作用
- r - Thesisdown:加载 huxtable 包和创建表格时,编织到 PDF 失败