0

我正在尝试使用 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);
}

客户端旨在接收图像,然后将其显示在表单的背景上。

4

3 回答 3

4

You need to add application level protocol to your socket communications.

Add a header to all messages sent. The header contains the count of bytes that follow. It is simpler code and performs better to have a byte count than to kludge a termination sequence.

The client then does two sets of reads: 1) Read for the number of bytes that are known to be in any header.

2) After extracting the byte count from the header, loop reading until you get the indicated byte count.

A must-read article for all people who are writing socket communications: http://nitoprograms.blogspot.com/2009/04/message-framing.html From that article: Repeat this mantra three times: "TCP does not operate on packets of data. TCP operates on streams of data."

于 2012-08-03T20:05:59.760 回答
1

我之前回答过一个类似的问题,并提供了一个完整的示例,我认为它完全符合您的要求。请参阅:通过 TCP 连接传输屏幕截图

于 2012-08-03T19:46:14.210 回答
0

您必须在图像的末尾设置一个分隔符,一些字节集将 sinalize 图像已结束的服务器。它也称为文件结束(EOF) 或消息结束。TCP 不会将图像拆分为应用程序的逻辑数据包,因此您必须编写自己的信息控制。

逻辑类似于: CLIENT

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;
}

同样,我无法测试它们,因此您可能会发现一些错误。

于 2012-08-03T19:06:52.183 回答