[英]How receive a complete screenshot in Async socket?
我有一個Java android
代碼,它將數據(圖像或文本)發送到C#
應用程序,以接收我正在使用異步套接字的這些數據。 但是存在一個問題,相對於BeginReceive()
函數在發送圖像時沒有接收到完整的數據。那么我怎樣才能做出一種“循環”來接收完整數據並在Picturebox上顯示圖像后(例如)?
形成
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();
我總是在這一行得到錯誤無效參數
var bitmap = new Bitmap(msInner);
並且使用壓縮也在這里發生
z.CopyTo(msInner);
IvalidDataException
分別在ServerReceivedImage()方法上。
用這個
File.WriteAllBytes(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyPictures), "image.png"), newImage);
我注意到只接收15KB(文件大小而不使用壓縮)。
我正在寫評論,但它沒有給我足夠的空間來表達我對你的代碼的挫敗感。
我的要點是
您嘗試重新壓縮並完美壓縮圖像。 PNG是便攜式網絡圖形。 它專為網絡傳輸而設計。 如果可以接受,你應該使用像jpeg這樣的東西。
你只需使用UTF8.GetString對接收到的緩沖區進行解碼並搜索文本,然后重新編碼該字符串並嘗試解壓縮並從中讀取圖像,從索引6開始,考慮到你添加了一個兩字節大小的字段,這是非常無意義的。流的開始,你真的不知道“屏幕”的位置。
您不檢查是否已收到所有流數據。
所有的代碼看起來都像你已經搜索了SO的問題和答案,並創建了一個復制意大利面。
現在我的建議。
從網絡傳輸數據時,請勿嘗試發明輪子。 嘗試類似gRPC的東西,它同時包含android java和c#包。
如果您將使用原始數據,請知道您的字節數。
我假設您將通過添加新的命令對來擴展您的代碼。 由於您沒有某種信號系統的魔術標記,因此您很難將數據與標題區分開來。 對於一個簡單的實現,在標題中添加一些魔術數據並搜索該數據,然后讀取標題然后讀取數據。 您可能需要反復讀取套接字,直到收到所有數據。
424A72 0600 53435245454E 008E0005 ..... 724A42 BJ r 6 SCREEN 36352 ..... rJB
此示例數據顯示我們通過查看“BJr”獲得了有效的流。 然后讀取一個2字節的無符號整數來讀取命令大小,對於SCREEN是6。 讀命令然后讀取命令數據的四個字節無符號長度。 對於我們的樣本,它是36352.為了安全起見,我添加了一個命令結束標記“rJB”。
要獲得獎勵點,請嘗試減少內存分配/副本,您可以查看System.Span<T>
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.