【问题标题】:Socket is not working as it should help!套接字无法正常工作,因为它应该有所帮助!
【发布时间】:2011-04-03 04:55:47
【问题描述】:

我制作了一个基于客户端和服务器套接字的 gui 应用程序,在该应用程序中捕获屏幕并通过套接字将其传输到所需的侦听器,但我得到一个中断图像接收端或客户端图像在服务器上但无法显示它在我的客户端应用程序中只显示黑色图片?

代码为:服务器

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Drawing.Imaging;
using System.Net.Sockets;
using System.IO;
using System.Net;

namespace LanMonitoring
{
    public partial class Form1 : Form
    {
      private static Bitmap bmpScreenshot;
      bool start = false;
      private static Graphics gfxScreenshot;

      public Form1()
      {
        InitializeComponent();
        button2.Enabled = false;
      }

      private void button1_Click(object sender, EventArgs e)
      {           
        button1.Enabled = false;
        button2.Enabled = true;
        start = true;

        fillpic();
      }
      public void fillpic()
      {
        bmpScreenshot = new Bitmap(Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height, PixelFormat.Format32bppArgb);
        gfxScreenshot = Graphics.FromImage(bmpScreenshot);
        gfxScreenshot.CopyFromScreen(Screen.PrimaryScreen.Bounds.X, Screen.PrimaryScreen.Bounds.Y, 0, 0, Screen.PrimaryScreen.Bounds.Size, CopyPixelOperation.SourceCopy);
        pictureBox1.Image = bmpScreenshot;
        sendbmp(bmpScreenshot);
      }


      private void button2_Click(object sender, EventArgs e)
      {

        button1.Enabled = true;
        button2.Enabled = false;
        start = false;
      }
      public void sendbmp(Bitmap bmp)
      {
        Socket mm = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
        IPEndPoint remoteEP = new IPEndPoint(IPAddress.Parse(textBox1.Text), 5002);
        try
        {
          mm.Connect(remoteEP);
        }
        catch (Exception)
        {
          sendbmp(bmpScreenshot);
        }
        Image temp = bmp;
        byte[] buf = imageToByteArray(temp);
        mm.Send(buf);
        mm.Close();
     }
      public byte[] imageToByteArray(System.Drawing.Image imageIn)
      {
        MemoryStream ms = new MemoryStream();
        imageIn.Save(ms, System.Drawing.Imaging.ImageFormat.Gif);
        return ms.ToArray();
      }


      public Image byteArrayToImage(byte[] byteArrayIn)
      {
        MemoryStream ms = new MemoryStream(byteArrayIn);
        Image returnImage = Image.FromStream(ms);
        return returnImage;
      }

    }
}

客户为:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Net.Sockets;
using System.IO;
using System.Net;
namespace LanReciver
{
    public partial class Client : Form
    {

      byte[] buf = new byte[5000];
      public Client()
      {
        InitializeComponent();

      }

      private void button1_Click(object sender, EventArgs e)
      {
        backgroundWorker1.RunWorkerAsync();
      }
      public void call()
      {
        Socket mm = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
        mm.Bind(new IPEndPoint(0, 5002));
        mm.Listen(100);
        Socket acc = mm.Accept();
        buf = new byte[acc.SendBufferSize];
        int byteread = acc.Receive(buf);
        byte[] rev = new byte[byteread];
        for (int i = 0; i < byteread; i++)
        {
          rev[i] = buf[i];
        }
        byteArrayToImage(rev);
        mm.Close();
        acc.Close();
        call();
      }

      public Image byteArrayToImage(byte[] byteArrayIn)
      {
        MemoryStream ms = new MemoryStream(byteArrayIn);
        Image returnImage = Image.FromStream(ms);
        return returnImage;
      }

      private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
      {
        call();
      }

      private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
      {
        MessageBox.Show("Recieved");
      }


    }
}

【问题讨论】:

标签: c# sockets


【解决方案1】:

这是一个相当完整(尽管快速而肮脏)的解决方案:

服务器:

using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.Windows.Forms;

namespace ImageServer
{
    static class Program
    {
        static void Main()
        {
            using (var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp))
            {
                socket.Bind(new IPEndPoint(IPAddress.Any, 23456));
                socket.Listen(100);
                while (true)
                {
                    using (var client = socket.Accept())
                    {
                        var bounds = Screen.PrimaryScreen.Bounds;
                        var bitmap = new Bitmap(bounds.Width, bounds.Height);
                        try
                        {
                            while (true)
                            {
                                using (var graphics = Graphics.FromImage(bitmap))
                                {
                                    graphics.CopyFromScreen(bounds.X, 0, bounds.Y, 0, bounds.Size);
                                }
                                byte[] imageData;
                                using (var stream = new MemoryStream())
                                {
                                    bitmap.Save(stream, ImageFormat.Png);
                                    imageData = stream.ToArray();
                                }
                                var lengthData = BitConverter.GetBytes(imageData.Length);
                                if (client.Send(lengthData) < lengthData.Length) break;
                                if (client.Send(imageData) < imageData.Length) break;
                                Thread.Sleep(1000);
                            }
                        }
                        catch
                        {
                            break;
                        }
                    }
                }
            }
        }
    }
}

客户端(带有单个按钮的表单:“button1”和单个图片框:“pictureBox1”):

using System;
using System.Drawing;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.Windows.Forms;

namespace ImageClient
{
    public partial class Form1 : Form
    {
        private Bitmap _buffer;

        public Form1()
        {
            InitializeComponent();
        }

        private void Button1Click(object sender, EventArgs e)
        {
            button1.Enabled = false;
            ThreadPool.QueueUserWorkItem(GetSnapshots);
        }

        private void GetSnapshots(object state)
        {
            using (var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp))
            {
                socket.Connect(new IPEndPoint(IPAddress.Loopback, 23456));
                while (true)
                {
                    var lengthData = new byte[4];
                    var lengthBytesRead = 0;
                    while (lengthBytesRead < lengthData.Length)
                    {
                        var read = socket.Receive(lengthData, lengthBytesRead, lengthData.Length - lengthBytesRead, SocketFlags.None);
                        if (read == 0) return;
                        lengthBytesRead += read;
                    }
                    var length = BitConverter.ToInt32(lengthData, 0);
                    var imageData = new byte[length];
                    var imageBytesRead = 0;
                    while (imageBytesRead < imageData.Length)
                    {
                        var read = socket.Receive(imageData, imageBytesRead, imageData.Length - imageBytesRead, SocketFlags.None);
                        if (read == 0) return;
                        imageBytesRead += read;
                    }
                    using (var stream = new MemoryStream(imageData))
                    {
                        var bitmap = new Bitmap(stream);
                        Invoke(new ImageCompleteDelegate(ImageComplete), new object[] { bitmap });
                    }
                }
            }
        }

        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 PictureBox1Paint(object sender, PaintEventArgs e)
        {
            if (_buffer == null) return;
            e.Graphics.DrawImage(_buffer, 0, 0);
        }
    }
}

【讨论】:

  • 您的发送函数需要检查您打算发送的字节数是否实际发送。 client.send 可能会发送您的部分数据,您需要循环发送,直到发送完所有字节。你对睡眠的使用不清楚。
  • @aaaabbbb - 不,它没有。套接字处于阻塞模式(默认),并且我们使用面向连接的协议 (TCP),因此根据 Socket.Send 的 MSDN 文档:“如果您使用面向连接的协议,Send 将阻塞,直到缓冲区中的所有字节都已发送..."
  • @aaaabbbb - 睡眠用于提供每个“帧”发送之间的延迟。
  • @BrowJr 图像在发送时已经经过 PNG 压缩,但如果您对接收到的图像不是源的完美复制感到高兴,您可以使用有损图像压缩格式来压缩图像(例如 JPEG),然后调整质量以实现您的场景所需的带宽减少。除此之外,您还可以增加帧之间的时间(通过增加Thread.Sleep(...)),或者只捕获屏幕的一部分而不是整个画面。
  • @BrowJr 这是一个示例,说明如何按照原始提问者的要求进行操作,它是“相当完整”的,因为它可以工作,但不打算成为一个产品- 质量屏幕查看应用程序。就“缺少”的内容而言,根据个人要求,可能会有无限多的东西,例如配置端口、主机、身份验证、授权、更新频率、图像压缩、屏幕区域、显示鼠标指针位置、查看器交互等的能力。这些可能会使它更“完整”,但方式 超出了 SO 答案中一个简单示例的范围。
【解决方案2】:

仅仅因为您通过一次调用Send(...) 发送图像,就不太可能(除非图像很小,如果它是屏幕截图,则可能不是)它会通过一次完整接收在客户端调用Receive(...)

你需要反复调用Receive(...),建立接收图像缓冲区,直到Receive(...)返回0,表示套接字已经关闭。然后,您应该可以显示完整的图像。

【讨论】:

    【解决方案3】:

    不确定您的实际问题是什么,但我注意到byteArrayToImage() 中存在潜在错误;您必须在图像的整个生命周期内保持流打开(是的,这不是最有用的 .NET API 方法!):http://msdn.microsoft.com/en-us/library/93z9ee4x.aspx

    【讨论】:

      【解决方案4】:

      我使用了 tcp 客户端和 listner 方法,它工作得很好,但是在接收端有一个问题,它只显示图像的 1/4 并抛出异常连接未建立.. 现在什么是 prb

      公共部分类 Form1 : Form { 私有静态位图 bmpScreenshot; 布尔开始 = 假; 私有静态图形 gfxScreenshot;

          public Form1()
          {
              InitializeComponent();
              button2.Enabled = false;
          }
      
          private void button1_Click(object sender, EventArgs e)
          {                   
              button1.Enabled = false;
              button2.Enabled = true;
              start = true;
              fillpic();
              backgroundWorker1.RunWorkerAsync();
          }
          public void fillpic()
          {
              bmpScreenshot = new Bitmap(Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height, PixelFormat.Format32bppArgb);
              gfxScreenshot = Graphics.FromImage(bmpScreenshot);
              gfxScreenshot.CopyFromScreen(Screen.PrimaryScreen.Bounds.X, Screen.PrimaryScreen.Bounds.Y, 0, 0, Screen.PrimaryScreen.Bounds.Size, CopyPixelOperation.SourceCopy);
              pictureBox1.Image = bmpScreenshot;
           }
      
      
          private void button2_Click(object sender, EventArgs e)
          {
      
              button1.Enabled = true;
              button2.Enabled = false;
              start = false;
          }
      
          public byte[] imageToByteArray(System.Drawing.Image imageIn)
          {
              MemoryStream ms = new MemoryStream();
              imageIn.Save(ms, System.Drawing.Imaging.ImageFormat.Jpeg);
              return ms.ToArray();
          }
      
      
      
          public void caal()
          {
              TcpClient TCPC = new TcpClient();
              TCPC.Connect("127.0.0.1", 5002);
              if (TCPC.Connected)
              {
                  NetworkStream ns = TCPC.GetStream();
                  while (ns.CanWrite)
                  {
                      fillpic();
                      byte[] data = imageToByteArray(bmpScreenshot);
                      ns.Write(data, 0, data.Length);
                  }
              }   
          }
      
      
          private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
          {
              caal();
          }
      
          }
      

      【讨论】:

      • 你是如何在这个版本中划分不同图像的?据我所见,您只是在屏幕截图后发送屏幕截图,但接收者无法确定一张图片的结束位置和下一张图片的开始位置。
      • 这就是我需要的,但不知道怎么做?
      • 一种常用的方法是先写入一个整数,表示下一个图像使用的字节数,然后写入图像,然后对后面的每个图像重复该过程。然后客户端将读取该数字,然后继续读取,直到它读取指定的字节数,一旦读取,将缓冲区转换为图像并显示它。然后它也会为每个后续帧重复该过程(读取字节数,然后是图像)。
      【解决方案5】:

      就像@Iridium 提到的那样,您需要循环 Read()-ing 直到不再接收到数据。

      另外,查看 MemoryStream 以替换所有数组元素混洗。 一个简单的 Array.CopyTo 已经是对复制字节数组的代码的巨大改进

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2011-11-23
        • 1970-01-01
        • 2012-01-26
        • 2015-05-06
        • 1970-01-01
        • 1970-01-01
        • 2015-10-12
        • 1970-01-01
        相关资源
        最近更新 更多