简体   繁体   English

如何在Async套接字中获得完整的屏幕截图?

[英]How receive a complete screenshot in Async socket?

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. 我有一个Java android代码,它将数据(图像或文本)发送到C#应用程序,以接收我正在使用异步套接字的这些数据。 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)? 但是存在一个问题,相对于BeginReceive()函数在发送图像时没有接收到完整的数据。那么我怎样才能做出一种“循环”来接收完整数据并在Picturebox上显示图像后(例如)?

Form 形成

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

Listener 倾听者

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

}

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 code 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

i'm always getting the error Invalid Parameter in this line 我总是在这一行得到错误无效参数

var bitmap = new Bitmap(msInner);

and using compression also happens the same here 并且使用压缩也在这里发生

z.CopyTo(msInner);

IvalidDataException IvalidDataException

on ServerReceivedImage() method respectively. 分别在ServerReceivedImage()方法上。

using this 用这个

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

i noted that is receiving only 15KB (size of file without use compression). 我注意到只接收15KB(文件大小而不使用压缩)。

I was writing a comment but it does not give me enough space to express my frustration with your code. 我正在写评论,但它没有给我足够的空间来表达我对你的代码的挫败感。

My main points are 我的要点是

  • You try to recompress and perfectly compressed image. 您尝试重新压缩并完美压缩图像。 PNG is portable network graphics. PNG是便携式网络图形。 It was designed for network transfers. 它专为网络传输而设计。 If it is acceptable you should use something like jpeg. 如果可以接受,你应该使用像jpeg这样的东西。

  • 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". 你只需使用UTF8.GetString对接收到的缓冲区进行解码并搜索文本,然后重新编码该字符串并尝试解压缩并从中读取图像,从索引6开始,考虑到你添加了一个两字节大小的字段,这是非常无意义的。流的开始,你真的不知道“屏幕”的位置。

  • You do not check if you have received ALL of the stream data. 您不检查是否已收到所有流数据。

  • All of the code looks like you have scoured the SO questions and answers and created a copy pasta. 所有的代码看起来都像你已经搜索了SO的问题和答案,并创建了一个复制意大利面。

Now my recommendations. 现在我的建议。

  • When transferring data from network, do not try to invent wheels. 从网络传输数据时,请勿尝试发明轮子。 Try something like gRPC which has both android java and c# packages. 尝试类似gRPC的东西,它同时包含android java和c#包。

  • 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 BJ r 6 SCREEN 36352 ..... rJB 

this sample data shows that we have a valid stream by looking at "BJr". 此示例数据显示我们通过查看“BJr”获得了有效的流。 Then read a 2 byte unsigned integer to read command size which is 6 for SCREEN. 然后读取一个2字节的无符号整数来读取命令大小,对于SCREEN是6。 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". 对于我们的样本,它是36352.为了安全起见,我添加了一个命令结束标记“rJB”。

For a bonus point try reducing memory allocations / copies, you can look at System.Span<T> 要获得奖励点,请尝试减少内存分配/副本,您可以查看System.Span<T>

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM