前面学习了Tcp通讯之后听老师同学们讲到Udp也可以通讯,实现还要跟简单,没有繁琐的连接,所以最近学习了一下,记录下来以便忘记,同时也发表出来与大家相互学习,下面是我自己写的一个聊天例子,实现了群聊私聊等功能。
通讯类(UdpInfo)
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 7 namespace Model 8 { 9 [Serializable] 10 public class UdpInfo 11 { 12 public string Name { get; set; } 13 public string Ip { get; set; } 14 public string Port { get; set; } 15 public string Message { get; set; } 16 public int Types { get; set; }//消息的类型 17 public bool IsFist { get; set; }//判断加载的成员是否是第一个 18 public string ToName { get; set; }//与谁私聊 19 public string LoginName { get; set; }//新加入成员的名字 20 } 21 }
服务器(Server)
1 using System; 2 using System.Collections.Generic; 3 using System.ComponentModel; 4 using System.Data; 5 using System.Drawing; 6 using System.Linq; 7 using System.Text; 8 using System.Threading.Tasks; 9 using System.Windows.Forms; 10 using System.Net; 11 using System.Net.Sockets; 12 using System.Threading; 13 using System.IO; 14 using System.Runtime.Serialization.Formatters.Binary; 15 using Model; 16 17 namespace Server 18 { 19 public partial class forUdp : Form 20 { 21 public forUdp() 22 { 23 InitializeComponent(); 24 } 25 UdpClient server = null; 26 IPEndPoint ipEndPoint = null; 27 List<IPEndPoint> pointList = new List<IPEndPoint>();//成员集合 28 private delegate void BindDelegate(UdpInfo u);//(绑定信息) 29 BindDelegate bd = null; 30 private bool flag;//是否为添加的第一个成员 31 private string loginName; 32 private void Form1_Load(object sender, EventArgs e) 33 { 34 txtPort.Text = "9018";//默认的端口 35 } 36 public void Receices()//接收客户端的消息 37 { 38 while (true) 39 { 40 try 41 { 42 byte[] bytes = server.Receive(ref ipEndPoint);//获取消息 43 UdpInfo u = DeserializeObject(bytes) as UdpInfo; 44 bd = new BindDelegate(BindRtb); 45 this.Invoke(bd, u); 46 } 47 catch (Exception) 48 { 49 server.Close();//有异常关闭服务 50 } 51 } 52 } 53 public object DeserializeObject(byte[] pBytes)//反序列化二进制为对象 54 { 55 object newOjb = null; 56 if (pBytes == null) 57 return newOjb; 58 MemoryStream memory = new MemoryStream(pBytes); 59 memory.Position = 0; 60 BinaryFormatter formatter = new BinaryFormatter(); 61 newOjb = formatter.Deserialize(memory); 62 memory.Close(); 63 return newOjb; 64 } 65 public byte[] SerializeObject(object pObj)//序列化对象为二进制的数 66 { 67 if (pObj == null) 68 return null; 69 MemoryStream memory = new MemoryStream(); 70 BinaryFormatter formatter = new BinaryFormatter(); 71 formatter.Serialize(memory, pObj); 72 memory.Position = 0; 73 byte[] read = new byte[memory.Length]; 74 memory.Read(read, 0, read.Length); 75 memory.Close(); 76 return read; 77 } 78 public void BindRtb(UdpInfo u) //添加信息,绑定信息 79 { 80 if(u.Types == 0)//如果消息类型为上线 81 { 82 pointList.Add(ipEndPoint);//添加成员 83 //u.Name = "1"; 84 //获取需要的基本信息 85 loginName = u.Name; 86 u.Ip = ipEndPoint.ToString().Split(':')[0]; 87 u.Port = ipEndPoint.ToString().Split(':')[1]; 88 byte[] bytes = SerializeObject(u); 89 ListViewItem item = new ListViewItem(u.Name);//绑定添加到成员列表上 90 item.SubItems.Add(u.Ip); 91 item.SubItems.Add(u.Port); 92 lvInfo.Items.Add(item);//显示成员信息 93 BindListView(loginName);//成员发生改变的时候,发送成员信息给客户端,刷新客户端的成员信息 94 } 95 else if (u.Types == 1)//如果消息的类型是群聊的话就执行 96 { 97 foreach (ListViewItem items in lvInfo.Items)//循环每个成员 98 { 99 IPAddress ip = IPAddress.Parse(items.SubItems[1].Text); 100 IPEndPoint ipEndPoint = new IPEndPoint(ip, Convert.ToInt32(items.SubItems[2].Text)); 101 byte[] bytes = SerializeObject(u); 102 server.Send(bytes, bytes.Length, ipEndPoint);//给每个用户发送信息 103 } 104 } 105 else if (u.Types == 2)//消息的类型是私聊 106 { 107 foreach(ListViewItem items in lvInfo.Items) 108 { 109 //如果该成员是指定成员才发送信息 110 if(u.ToName.Equals(items.SubItems[0].Text)) 111 { 112 IPAddress ip = IPAddress.Parse(items.SubItems[1].Text); 113 IPEndPoint ipEndPoint = new IPEndPoint(ip, Convert.ToInt32(items.SubItems[2].Text)); 114 byte[] bytes = SerializeObject(u); 115 server.Send(bytes, bytes.Length, ipEndPoint);//发送消息给指定的用户 116 } 117 } 118 } 119 else if(u.Types == 3)//如果消息的类型是下线的话 120 { 121 IPEndPoint ipEndPoint = null; 122 foreach (ListViewItem items in lvInfo.Items)//循环每个成员 123 { 124 IPAddress ip = IPAddress.Parse(items.SubItems[1].Text); 125 ipEndPoint = new IPEndPoint(ip, Convert.ToInt32(items.SubItems[2].Text)); 126 if(items.SubItems[0].Text.Equals(u.Name)) 127 { 128 lvInfo.Items.Remove(items);//从ListView集合中移除 129 pointList.Remove(ipEndPoint);//从List<IPEndPoint>集合中移除 130 continue; 131 } 132 byte[] bytes = SerializeObject(u); 133 server.Send(bytes, bytes.Length, ipEndPoint);//给每个用户发送信息 134 } 135 } 136 //服务器接收所有的消息 137 rtbMessage.Text += "\n" + u.Name + ":" + u.Message; 138 } 139 public void BindListView(string loginName) //有新成员加入的时候就绑定刷新成员列表 140 { 141 flag = true; 142 foreach (ListViewItem items in lvInfo.Items)//循环每个用户 143 { 144 foreach (ListViewItem item in lvInfo.Items)//给每个用户发送全部的成员信息 145 { 146 UdpInfo u = new UdpInfo(); 147 u.Name = item.SubItems[0].Text.ToString(); 148 u.IsFist = flag;//添加第一个成员的时候因为要重新初始化客户端成员列表,而剩下的则不要初始化,所以要设置为true 149 flag = false; 150 u.LoginName = loginName; 151 u.Message = "上线了!"; 152 u.Ip = item.SubItems[1].Text.ToString(); 153 u.Port = item.SubItems[2].Text.ToString(); 154 byte[] bytes = SerializeObject(u); 155 IPAddress ip = IPAddress.Parse(items.SubItems[1].Text); 156 IPEndPoint ipp = new IPEndPoint(ip, Convert.ToInt32(items.SubItems[2].Text));//得到成员的IPEndPoint 157 server.Send(bytes, bytes.Length, ipp); 158 } 159 } 160 } 161 private void 结束程序ToolStripMenuItem_Click(object sender, EventArgs e) 162 { 163 server.Close(); 164 this.Dispose(); 165 Application.Exit(); 166 } 167 168 private void 运行程序ToolStripMenuItem_Click(object sender, EventArgs e) 169 { 170 rtbMessage.Text = "可以发送消息过来了!"; 171 int port = Convert.ToInt32(txtPort.Text); 172 server = new UdpClient(port); 173 ipEndPoint = new IPEndPoint(IPAddress.Any, port); 174 Thread t = new Thread(new ThreadStart(Receices)); 175 t.Start(); 176 } 177 } 178 }
客户端(Client)
1 using System; 2 using System.Collections.Generic; 3 using System.ComponentModel; 4 using System.Data; 5 using System.Drawing; 6 using System.Linq; 7 using System.Text; 8 using System.Threading.Tasks; 9 using System.Windows.Forms; 10 using System.Net; 11 using System.Net.Sockets; 12 using System.Threading; 13 using Model; 14 using System.IO; 15 using System.Runtime.Serialization.Formatters.Binary; 16 17 namespace Client 18 { 19 public partial class forUdp : Form 20 { 21 public forUdp() 22 { 23 InitializeComponent(); 24 } 25 UdpClient client = null; 26 IPEndPoint ipEndPoints = null; 27 private delegate void BindDelegate(UdpInfo u); 28 BindDelegate bd = null; 29 private void forUdp_Load(object sender, EventArgs e) 30 { 31 txtIP.Text = "192.168.1.109"; 32 txtPort.Text = "9018"; 33 txtName.Text = "小哈"; 34 } 35 36 private void 运行程序ToolStripMenuItem_Click(object sender, EventArgs e) 37 { 38 rtbMessage.Text = "可以发送消息过去了!"; 39 int port = Convert.ToInt32(txtPort.Text); 40 ipEndPoints = new IPEndPoint(IPAddress.Parse(txtIP.Text), port); 41 client = new UdpClient(); 42 client.Connect(IPAddress.Parse(txtIP.Text), port);//关联此IP和端口 43 Send(new UdpInfo() {Name = txtName.Text,Message = "上线了!", Types = 0 }); 44 Thread t = new Thread(new ThreadStart(Receices)); 45 t.Start(); 46 } 47 48 public void Receices()//接收客户端的消息 49 { 50 while (true) 51 { 52 try 53 { 54 byte[] bytes = client.Receive(ref ipEndPoints); 55 UdpInfo u = DeserializeObject(bytes) as UdpInfo; 56 bd = new BindDelegate(BindRtb); 57 this.Invoke(bd, u); 58 } 59 catch (Exception) 60 { 61 client.Close(); 62 } 63 } 64 } 65 public object DeserializeObject(byte[] pBytes)//反序列化二进制为对象 66 { 67 object newOjb = null; 68 if (pBytes == null) 69 return newOjb; 70 MemoryStream memory = new MemoryStream(pBytes); 71 memory.Position = 0; 72 BinaryFormatter formatter = new BinaryFormatter(); 73 newOjb = formatter.Deserialize(memory); 74 memory.Close(); 75 return newOjb; 76 } 77 public byte[] SerializeObject(object pObj)//序列化对象为二进制的数 78 { 79 if (pObj == null) 80 return null; 81 MemoryStream memory = new MemoryStream(); 82 BinaryFormatter formatter = new BinaryFormatter(); 83 formatter.Serialize(memory, pObj); 84 memory.Position = 0; 85 byte[] read = new byte[memory.Length]; 86 memory.Read(read, 0, read.Length); 87 memory.Close(); 88 return read; 89 } 90 public void BindRtb(UdpInfo u)//将消息添加到消息框中 91 { 92 if (u.Types == 0) 93 { 94 if (u.IsFist == true) 95 lvInfo.Items.Clear(); 96 ListViewItem item = new ListViewItem(u.Name); 97 item.SubItems.Add(u.Ip); 98 item.SubItems.Add(u.Port); 99 lvInfo.Items.Add(item); 100 } 101 else if (u.Types == 3)//如果消息的类型是下线的话 102 { 103 IPEndPoint ipEndPoint = null; 104 foreach (ListViewItem items in lvInfo.Items)//循环每个成员 105 { 106 if (items.SubItems[0].Text.Equals(u.Name)) 107 { 108 IPAddress ip = IPAddress.Parse(items.SubItems[1].Text); 109 ipEndPoint = new IPEndPoint(ip, Convert.ToInt32(items.SubItems[2].Text)); 110 lvInfo.Items.Remove(items);//从ListView集合中移除 111 continue; 112 } 113 } 114 } 115 foreach (ListViewItem items in lvInfo.Items) 116 { 117 if(items.SubItems[0].Text.Equals(u.LoginName)) 118 rtbMessage.Text += "\n" + u.Name + ":" + u.Message; 119 } 120 } 121 public void Send(UdpInfo u)//发送消息给服务器 122 { 123 byte[] bytes = SerializeObject(u); 124 client.Send(bytes, bytes.Length); 125 } 126 private void 结束程序ToolStripMenuItem_Click(object sender, EventArgs e) 127 { 128 Send(new UdpInfo() { Name = txtName.Text, Message = "下线了!", Types = 3 }); 129 client.Close(); 130 Application.Exit(); 131 } 132 133 private void btnSend_Click(object sender, EventArgs e) 134 { 135 UdpInfo u = new UdpInfo() { 136 Name = txtName.Text, Message = rtbNews.Text, Types = 1 137 }; 138 if (lvInfo.SelectedItems.Count > 0)//如果有选中的成员的话 139 { 140 u.Types = 2; 141 u.ToName = lvInfo.SelectedItems[0].SubItems[0].Text; 142 } 143 Send(u); 144 lvInfo.SelectedItems.Clear();//清除上一次的痕迹 145 } 146 } 147 }