練習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