如何在异步套接字中接收完整的屏幕截图? [英] How receive a complete screenshot in Async socket?

查看:107
本文介绍了如何在异步套接字中接收完整的屏幕截图?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个Java android代码,该代码将数据(图像或文本)发送到C#应用程序,以接收这些数据(我正在使用异步套接字).但是存在一个相对于 BeginReceive() 函数的问题,当发送图像时,该函数没有接收到完整的数据.那么,我该如何制作一种循环"来接收完整的数据并在显示后(例如)图片框上的图片?

I have a Java android code that sends data (image or text) to a C# application, to receive these data I'm using Async socket. But exists a problem that is relative to BeginReceive() function is not receiving the complete data when is sent an image.. Then how I can make a kind of "loop" to receive full data and after show the image on Picturebox (for example)?

表格

private Listener listener;
private Thread startListen;

private Bitmap _buffer;

public frmMain()
{
  InitializeComponent();
}

private void serverReceivedImage(Client client, byte[] image)
{
    try
    {
        byte[] newImage = new byte[image.Length - 6];

        Array.Copy(image, 6, newImage, 0, newImage.Length);

            using (var stream = new MemoryStream(newImage))
            {
                using (var msInner = new MemoryStream())
                {

                  stream.Seek(2, SeekOrigin.Begin);

                  using (DeflateStream z = new DeflateStream(stream, CompressionMode.Decompress))
                  {
                    z.CopyTo(msInner);
                  }

                  msInner.Seek(0, SeekOrigin.Begin);

                  var bitmap = new Bitmap(msInner);
                  Invoke(new frmMain.ImageCompleteDelegate(ImageComplete), new object[] { bitmap });
                }
            }
    }
     catch (Exception)
     {
        System.Diagnostics.Process.GetCurrentProcess().Kill();
     }
}

private delegate void ImageCompleteDelegate(Bitmap bitmap);
private void ImageComplete(Bitmap bitmap)
{
   if (_buffer != null)
       _buffer.Dispose();

       _buffer = new Bitmap(bitmap);
       pictureBox1.Size = _buffer.Size;
       pictureBox1.Invalidate();
}

private void pictureBox1_Paint(object sender, PaintEventArgs e)
{
  if (_buffer == null) return;
      e.Graphics.DrawImage(_buffer, 0, 0);
}

private void startToolStripMenuItem_Click(object sender, EventArgs e)
{
  startListen = new Thread(listen);
  startListen.Start();
}

private void listen()
{
  listener = new Listener();
  listener.BeginListen(101);
  listener.receivedImage += new Listener.ReceivedImageEventHandler(serverReceivedImage);

  startToolStripMenuItem.Enabled = false;
}

监听器

class Listener
{

    private Socket s;
    public List<Client> clients;

    public delegate void ReceivedImageEventHandler(Client client, byte[] image);
    public event ReceivedImageEventHandler receivedImage;

    private bool listening = false;

    public Listener()
    {
        clients = new List<Client>();
        s = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
    }

    public bool Running
    {
        get { return listening; }
    }

    public void BeginListen(int port)
    {
        s.Bind(new IPEndPoint(IPAddress.Any, port));
        s.Listen(100);
        s.BeginAccept(new AsyncCallback(AcceptCallback), s);
        listening = true;
    }

    public void StopListen()
    {
        if (listening == true)
        {
            s.Close();
            listening = false;
        }
    }

    void AcceptCallback(IAsyncResult ar)
    {
        Socket handler = (Socket)ar.AsyncState;
        Socket sock = handler.EndAccept(ar);
        Client client = new Client(sock);
        clients.Add(client);

        sock.BeginReceive(client.buffer, 0, client.buffer.Length, SocketFlags.None, new AsyncCallback(ReadCallback), client);

        client.Send("REQUEST_PRINT" + Environment.NewLine); 

        handler.BeginAccept(new AsyncCallback(AcceptCallback), handler);
    }

    void ReadCallback(IAsyncResult ar)
    {

        Client client = (Client)ar.AsyncState;
        try
        {
            int rec = client.sock.EndReceive(ar);
            if (rec != 0)
            {
                string data = Encoding.UTF8.GetString(client.buffer, 0, rec);

                if (data.Contains("SCREEN"))
                { 
                    byte[] bytes = Encoding.UTF8.GetBytes(data);
                    receivedImage(client, bytes);
                }
                else // not is a image, is a text
                {
                    // prepare text to show in TextBox
                }
            }
            else
            {
                Disconnected(client);
                return;
            }

            client.sock.BeginReceive(client.buffer, 0, client.buffer.Length, SocketFlags.None, new AsyncCallback(ReadCallback), client);
        }
        catch
        {
            Disconnected(client);
            client.sock.Close();
            clients.Remove(client);
        }
    }

}

客户

class Client
{
    public Socket sock;
    public byte[] buffer = new byte[8192];

    public Client(Socket sock)
    {
        this.sock = sock;
    }

    public void Send(string data)
    {
        byte[] buffer = Encoding.ASCII.GetBytes(data);
        sock.BeginSend(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback((ar) =>
        {
            sock.EndSend(ar);
        }), buffer);
    }
}


Android代码

private byte[] compress(byte[] data) {

    Deflater deflater = new Deflater();
    deflater.setInput(data);
    ByteArrayOutputStream outputStream = new ByteArrayOutputStream(data.length);
    deflater.finish();
    byte[] buffer = new byte[1024];
    while (!deflater.finished()) {
        int count = deflater.deflate(buffer);
        outputStream.write(buffer, 0, count);
    }
    outputStream.close();
    byte[] output = outputStream.toByteArray();

    return output;
}

public static DataOutputStream dos;
public static byte[] array;

ByteArrayOutputStream bos = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.PNG, 100, bos);
array = compress(bos.toByteArray());

//...

dos = new DataOutputStream(SocketBackgroundService.clientSocket.getOutputStream());
byte[] header = ("SCREEN").getBytes(StandardCharsets.UTF_8);
byte[] dataToSend = new byte[header.length + array.length];
System.arraycopy(header, 0, dataToSend, 0, header.length);
System.arraycopy(array, 0, dataToSend, header.length, array.length);
dos.writeInt(dataToSend.length);
dos.write(dataToSend, 0, dataToSend.length);

dos.flush();


EDITION

我总是在此行中得到错误无效参数


EDITION

i'm always getting the error Invalid Parameter in this line

var bitmap = new Bitmap(msInner);

在这里使用压缩也一样

z.CopyTo(msInner);

IvalidDataException

分别在 ServerReceivedImage()方法上.

使用此

File.WriteAllBytes(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyPictures), "image.png"), newImage);

我注意到它仅接收15KB(不使用压缩的文件大小).

i noted that is receiving only 15KB (size of file without use compression).

推荐答案

我在写评论,但没有足够的空间来表达对代码的不满.

I was writing a comment but it does not give me enough space to express my frustration with your code.

我的主要观点是

  • 您尝试重新压缩并完美压缩图像. PNG是便携式网络图形.它是专为网络传输而设计的.如果可以接受,则应使用类似jpeg的名称.

  • You try to recompress and perfectly compressed image. PNG is portable network graphics. It was designed for network transfers. If it is acceptable you should use something like jpeg.

您只需使用UTF8.GetString解码接收到的缓冲区并搜索文本,然后重新编码该字符串并尝试从索引6开始解压缩并从中读取图像,考虑到您添加的索引,这是毫无意义的流的开头有两个字节大小的字段,您真的不知道"SCREEN"的位置.

You just decode received buffer using UTF8.GetString and search for a text, then re-encode that string and try to decompress and read an image from it, by starting from index 6 which is pretty meaningless considering you added a two byte size field to the start of stream and you really do not know position of "SCREEN".

您不会检查是否已收到所有流数据.

You do not check if you have received ALL of the stream data.

所有代码看起来像您已经搜查过SO问题和答案并创建了复制面食.

All of the code looks like you have scoured the SO questions and answers and created a copy pasta.

现在我的建议.

  • 从网络传输数据时,请勿尝试发明轮子.尝试使用类似gRPC的东西,它同时具有android java和c#软件包.

  • When transferring data from network, do not try to invent wheels. Try something like gRPC which has both android java and c# packages.

如果您要使用原始数据,请知道您的字节数.

If you will use raw data, please, please know your bytes.

我假设您将通过添加新的命令对来扩展代码.由于您没有某种信号系统的魔力标记,因此很难将数据与标头区分开.对于一个简单的实现,将某种魔术数据添加到标头中并搜索该数据,然后读取标头,然后读取数据.您可能需要一次又一次地从套接字读取数据,直到收到所有数据为止.

I assume you will extend your code by adding new command pairs. Since you have no magic markers of some kind of signal system, it will be very hard for you to distinguish data from header. For a simple implementation add some kind of magic data to your header and search for that data, then read header and then read data. You may need to read from socket again and again until you receive all of the data.

424A72 0600 53435245454E 008E0005   .....  724A42
 B J r    6  S C R E E N    36352   .....  rJB

此样本数据通过查看"BJr"表明我们有一个有效的流.然后读取2字节的无符号整数以读取命令大小(对于SCREEN)为6.读取命令,然后读取四个字节的无符号长度的命令数据.对于我们的示例,它是36352.为安全起见,我添加了命令标记"rJB"的结尾.

this sample data shows that we have a valid stream by looking at "BJr". Then read a 2 byte unsigned integer to read command size which is 6 for SCREEN. Read command and then read four bytes unsigned length for command data. For our sample it is 36352. Just to be safe I've added an end of command marker "rJB".

要获得奖励积分,请尝试减少内存分配/副本,您可以查看System.Span<T>

For a bonus point try reducing memory allocations / copies, you can look at System.Span<T>

这篇关于如何在异步套接字中接收完整的屏幕截图?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆