练习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发送和接收二进制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,界面使用WPFthis
代码:spa
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方法可方便转换成二进制。code
上面只是简单的实现了传输图片的代码,实现中应加入错误判断等异常处理。orm