Socket 传一幅图片给另一个终端

练习Socket传文件,先添加一个组件,简化socket发送和接收文件,

获取IP和端口的类

    public static class AddressHelper
    {
        /// <summary>
        /// 获取本机IPv4地址的集合
        /// </summary>
        /// <returns></returns>
        public static IPAddress[] GetLocalhostIPv4Addresses()
        {
            String LocalhostName = Dns.GetHostName();

            IPHostEntry host = Dns.GetHostEntry(LocalhostName);
            List<IPAddress> addresses=new List<IPAddress>();
            foreach (IPAddress ip in host.AddressList)
            {
                if (ip.AddressFamily == AddressFamily.InterNetwork) 
                    addresses.Add(ip);
            }
            return addresses.ToArray();
        }


        /// <summary>
        /// 以交互方式生成有效的远程主机访问终结点,适用于控制台程序
        /// </summary>
        /// <returns></returns>
        public static IPEndPoint GetRemoteMachineIPEndPoint()
        {
            IPEndPoint iep = null;
            try
            {
                Console.Write("请输入远程主机的IP地址:");
                IPAddress address = IPAddress.Parse(Console.ReadLine());
                Console.Write("请输入远程主机打开的端口号:");
                int port = Convert.ToInt32(Console.ReadLine());
                if (port > 65535 || port < 1024)
                    throw new Exception("端口号应该为[1024,65535]范围内的整数");
                iep = new IPEndPoint(address, port);

            }
            catch (ArgumentNullException)
            {
                Console.WriteLine("输入的数据有误!");
            }
            catch (FormatException)
            {
                Console.WriteLine("输入的数据有误!");
            }

            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
            return iep;
        }

        /// <summary>
        /// 获取本机当前可用的端口号,此方法是线程安全的
        /// </summary>
        /// <returns></returns>
        public static int GetOneAvailablePortInLocalhost()
        {
            Mutex mtx = new Mutex(false, "MyNetworkLibrary.AddressHelper.GetOneAvailablePort");
            try
            {
                mtx.WaitOne();
                IPEndPoint ep = new IPEndPoint(IPAddress.Any, 0);
                using (Socket tempSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp))
                {
                    tempSocket.Bind(ep);
                    IPEndPoint ipep = tempSocket.LocalEndPoint as IPEndPoint;
                    return ipep.Port;
                }
            }
            finally
            {
                mtx.ReleaseMutex();
            }
        }
    }

socket发送和接收二进制

 public static class SocketHelper
    {
        /// <summary>
        /// 接收变长的数据,要求其打头的4个字节代表有效数据的长度
        /// </summary>
        /// <param name="s"></param>
        /// <returns></returns>
        public static byte[] ReceiveVarData(Socket s)
        {
            if (s == null)
                throw new ArgumentNullException("s");
            int total = 0;  //已接收的字节数
            int recv;
            //接收4个字节,得到“消息长度”
            byte[] datasize = new byte[4];
            recv = s.Receive(datasize, 0, 4, 0);
            int size = BitConverter.ToInt32(datasize, 0);
            //按消息长度接收数据
            int dataleft = size;
            byte[] data = new byte[size];
            while (total < size)
            {
                recv = s.Receive(data, total, dataleft, 0);
                if (recv == 0)
                {
                    break;
                }
                total += recv;
                dataleft -= recv;
            }
            return data;
        }

        /// <summary>
        /// 发送变长的数据,将数据长度附加于数据开头
        /// </summary>
        /// <param name="s"></param>
        /// <param name="data"></param>
        /// <returns></returns>
        public static int SendVarData(Socket s, byte[] data)
        {
            int total = 0;
            int size = data.Length;  //要发送的消息长度
            int dataleft = size;     //剩余的消息
            int sent;
            //将消息长度(int类型)的,转为字节数组
            byte[] datasize = BitConverter.GetBytes(size);
            //将消息长度发送出去
            sent = s.Send(datasize);
            //发送消息剩余的部分
            while (total < size)
            {
                sent = s.Send(data, total, dataleft, SocketFlags.None);
                total += sent;
                dataleft -= sent;
            }
            return total;
        }
    }

 好,现在开始制作socket,界面使用WPF

代码:

   public partial class MainWindow : Window
    {
        Socket server = null;
        string picPath = "";
        public MainWindow()
        {
            InitializeComponent();
            Init();
        }

        private void Init()
        {
            //初始化
            var ip = AddressHelper.GetLocalhostIPv4Addresses().First();
            var port = AddressHelper.GetOneAvailablePortInLocalhost();
            txtIp.Text = ip.ToString();
            txtPort.Text = port.ToString();

            IPEndPoint iep = new IPEndPoint(ip, port);
            server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            server.Bind(iep);
            server.Listen(1);
            //启动线程开始接收数据
            DoWork();
            tbinfo.Text = iep.ToString() + "已启动监听";
        }

        private Task DoWork()
        {
            return Task.Run(() =>
              {
                  //创建委托,更新图片控件
                  Action<byte[]> action = buf =>
                  {
                      img.Source = ByteToBitmapImage(buf);
                  };
                  while (true)
                  {
                      var client = server.Accept();
                      var data = SocketHelper.ReceiveVarData(client);

                      this.Dispatcher.BeginInvoke(action, data);
                      client.Shutdown(SocketShutdown.Both);
                      client.Close();
                  }
              });

        }
        //二进制转图片
        public BitmapImage ByteToBitmapImage(byte[] buf)
        {
            BitmapImage bmp = new BitmapImage();
            MemoryStream ms = new MemoryStream(buf);
            ms.Position = 0;
            bmp.BeginInit();
            bmp.StreamSource = ms;
            bmp.EndInit();
            return bmp;
        }
        //选择图片
        private void Button_Click(object sender, RoutedEventArgs e)
        {
            System.Windows.Forms.OpenFileDialog ofd = new System.Windows.Forms.OpenFileDialog();
            if (ofd.ShowDialog() == System.Windows.Forms.DialogResult.OK)
            {
                picPath = ofd.FileName;
                img.Source = new BitmapImage(new Uri(picPath));
            }
        }
        //取远程终端的IPEndPoint
        private IPEndPoint getRemIEP()
        {
            return new IPEndPoint(IPAddress.Parse(txtIp.Text), int.Parse(txtPort.Text));
        }
        //发送
        private void Button_Click_1(object sender, RoutedEventArgs e)
        {
            var newSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            using (newSocket)
            {
                IPEndPoint remIep = getRemIEP();
                newSocket.Connect(remIep);
                var buf = File.ReadAllBytes(picPath);
                SocketHelper.SendVarData(newSocket, buf);
            }
        }
    }

上面代码要添加两个引用,一个是System.Windows.Forms,另一个就是最上面所建的类,这时可以打开两个应用,在端口处输入远程的端口,相互就可以发送图片,并能显示图片,如下图

相互传输使用二进制传输,文件转二进制,使用File.ReadAllBytes方法可方便转换成二进制。

上面只是简单的实现了传输图片的代码,实现中应加入错误判断等异常处理。

原文地址:https://www.cnblogs.com/lunawzh/p/6694138.html