我尝试使用C#创建远程桌面服务器和客户端。服务器捕获屏幕,然后通过套接字将其发送到客户端。我使用下面的代码,虽然它只显示客户端上jpeg图像的一部分。我认为这是因为图像是以多个数据包发送的,此时代码只读取一个数据包并显示它。任何人都可以解释我如何更改我的代码,以便它在显示之前接收多个数据包(整个图像)。
服务器代码:
Socket serverSocket;
Socket clientSocket;
public Form1()
{
InitializeComponent();
backgroundWorker1.RunWorkerAsync();
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
try
{
serverSocket = new Socket(AddressFamily.InterNetwork,
SocketType.Stream,
ProtocolType.Tcp);
IPEndPoint ipEndPoint = new IPEndPoint(IPAddress.Any, 8221);
serverSocket.Bind(ipEndPoint);
serverSocket.Listen(4);
//Accept the incoming clients
serverSocket.BeginAccept(new AsyncCallback(OnAccept), null);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, "Stream Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
private void timer1_Tick(object sender, EventArgs e)
{
timer1.Stop();
Rectangle bounds = new Rectangle(0, 0, 1280, 720);
Bitmap bitmap = new Bitmap(bounds.Width, bounds.Height);
using (Graphics g = Graphics.FromImage(bitmap))
{
g.CopyFromScreen(Point.Empty, Point.Empty, bounds.Size);
}
System.IO.MemoryStream stream = new System.IO.MemoryStream();
ImageCodecInfo myImageCodecInfo;
System.Drawing.Imaging.Encoder myEncoder;
EncoderParameter myEncoderParameter;
EncoderParameters myEncoderParameters;
myEncoderParameters = new EncoderParameters(1);
myImageCodecInfo = GetEncoderInfo("image/jpeg");
myEncoder = System.Drawing.Imaging.Encoder.Quality;
myEncoderParameter = new EncoderParameter(myEncoder, 40L);
myEncoderParameters.Param[0] = myEncoderParameter;
bitmap.Save(stream, myImageCodecInfo, myEncoderParameters);
byte[] imageBytes = stream.ToArray();
stream.Dispose();
clientSocket.Send(imageBytes);
timer1.Start();
}
正如您所看到的,我使用了一个定时器设置为30的定时器来发送图像字节。
客户代码:
public Socket clientSocket;
byte[] byteData = new byte[2048];
MemoryStream ms;
public Form1()
{
InitializeComponent();
backgroundWorker1.RunWorkerAsync();
this.DoubleBuffered = true;
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
try
{
clientSocket = new Socket(AddressFamily.InterNetwork,
SocketType.Stream, ProtocolType.Tcp);
IPEndPoint ipEndPoint = new IPEndPoint(IPAddress.Parse("MY EXTERNAL IP HERE"), 8221);
//Connect to the server
clientSocket.BeginConnect(ipEndPoint,
new AsyncCallback(OnConnect), null);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, "SGSclient",
MessageBoxButtons.OK,
MessageBoxIcon.Error);
}
}
private void OnConnect(IAsyncResult ar)
{
try
{
//Start listening to the data asynchronously
clientSocket.BeginReceive(byteData,
0,
byteData.Length,
SocketFlags.None,
new AsyncCallback(OnReceive),
null);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, "Stream Error",
MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
private void OnReceive(IAsyncResult ar)
{
try
{
int byteCount = clientSocket.EndReceive(ar);
ms = new MemoryStream(byteData);
using (BinaryReader br = new BinaryReader(ms))
{
this.BackgroundImage = Image.FromStream(ms).GetThumbnailImage(this.ClientRectangle.Width, this.ClientRectangle.Height, null, IntPtr.Zero);
}
}
catch (ArgumentException e)
{
//MessageBox.Show(e.Message);
}
clientSocket.BeginReceive(byteData, 0, byteData.Length, SocketFlags.None, new AsyncCallback(OnReceive), null);
}
客户端旨在接收图像,然后将其显示在表单的背景上。
答案 0 :(得分:4)
您需要在套接字通信中添加应用程序级协议。
为发送的所有邮件添加标头。标头包含后面的字节数。它的代码更简单,并且具有字节数比阻塞终止序列更好。
客户端然后执行两组读取: 1)读取已知在任何标头中的字节数。
2)从报头中提取字节数后,循环读取,直到得到指示的字节数。
所有正在编写套接字通信的人都必读的文章:http://nitoprograms.blogspot.com/2009/04/message-framing.html 从那篇文章:重复这个咒语三次:“TCP不对数据包进行操作.TCP在数据流上运行。”
答案 1 :(得分:1)
您必须在图像的末尾设置一个分隔符,这些分隔符将使图像已结束的服务器的一些字节。它也称为文件结尾(EOF)或消息结束。 TCP不会将图像拆分为您的应用程序的逻辑数据包,因此您必须编写自己的信息控制。
逻辑与此类似: <强>客户端强>
byte[] EndOfMessage = System.Text.Encoding.ASCII.GetBytes("image_end");
byte[] ImageBytes = GetImageBytes();
byte[] BytesToSend = new byte[EndOfMessage.Length + ImageBytes.Length];
Array.Copy(ImageBytes, 0, BytesToSend);
Array.Copy(EndOfMessage, 0, BytesToSend, ImageBytes.Length, EndOfMessage.Length);
SendToServer(BytesToSend);
服务器强>
byte[] EndOfMessage = System.Text.Encoding.ASCII.GetBytes("image_end");
byte[] ReceivedBytes;
while(!IsEndOfMessage(ReceivedBytes, EndOfMessage ))
{
//continue reading from socket and adding to ReceivedBytes
}
ReceivedBytes = RemoveEndOfMessage(ReceivedBytes, EndOfMessage );
PrintImage(ReceivedBytes);
我现在正在工作,我无法提供完整的示例,对不起。
此致
支持方法:
private bool IsEndOfMessage(byte[] MessageToCheck, byte[] EndOfMessage)
{
for(int i = 0; i++; i < EndOfMessage.Length)
{
if(MessageToCheck[MessageToCheck.Length - (EndOfMessage.Length + i)] != EndOfMessage[i])
return false;
}
return true;
}
private byte[] RemoveEndOfMessage(byte[] MessageToClear, byte[] EndOfMessage)
{
byte[] Return = new byte[MessageToClear.Length - EndOfMessage.Length];
Array.Copy(MessageToClear, Return, Return.Length);
return Return;
}
同样,我无法测试它们,所以你可能会发现一些错误。
答案 2 :(得分:0)
我之前回答了类似的问题,并提供了一个完整的工作示例,我认为这正是您要做的。请参阅:transferring a screenshot over a TCP connection