局域网之间的通讯实现
局域网之间通讯有很多种方法,比如可以用数据库存储的方式实现,这里是用Socket来实现的,用Socket来通讯的话可以说是有点麻烦的,因为要保持各个用户与服务器之间的连接,连接一断便不能通讯了,所以个人觉得这个地方比较麻烦;
要实现Socket通讯的话首先可以把它分成两个项目来区别,一个是服务器项目(Server),一个是客户端项目(Client)。
服务器项目的实现可以大致的分成 部分,1:创建一个总连接点,这个连接点负责与客户端的连接,我们不能把连接和通讯都引用到一个连接上,这样服务器会崩溃的,而每个用户都是独立的,所以我们要利用线程来创建一个新的通讯实例,这个连接和通讯各个用户之间都是互不干扰的了,还有一个要注意的就是跨线程调用控件的话是不行的,所以我们又要利用委托存放要调用的功能在用this.Invoke()的方法来跨线程引用控件,接下来就是具体实现响应通讯了;
客户端相对来说要更容易些,创建一个客户端连接实例,与远程服务器连接起来,然后就是大致的通讯实现功能了。
下面是我个人的实现代码和实现的心得,附加了大致的注释,实现了群聊,私聊功能。
通讯类(Communication):
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Server { [Serializable] public class Communication//通讯 { public string Name { get; set; } public string Ip { get; set; }//Ip地址 public string Port { get; set; }//端口号 public string Message { get; set; }//发送的消息 public int Types { get; set; } //1代表聊天信息 2代表客户端人物信息 public bool IsFistLogin { get; set; } public string ToEndPoint { get; set; } } }
using System; using System.Collections.Generic; using System.Linq; using System.Net.Sockets; using System.Text; using System.Threading.Tasks; namespace Server { [Serializable] public class ServerReturnInfo { public Communication comm { get; set; }//通讯类 public Socket client { get; set; } } }
服务器类(Server):
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.IO; using System.Linq; using System.Net; using System.Net.Sockets; using System.Runtime.Serialization.Formatters.Binary; using System.Text; using System.Threading; using System.Threading.Tasks; using System.Windows.Forms; namespace Server { public partial class forServer : Form { public forServer() { InitializeComponent(); } private delegate void ListDelegate(Communication comm); private ListDelegate listInfo = null;//添加成员 private delegate void MessageDelegate(Communication comm); private MessageDelegate MessageInfo = null;//添加消息 private delegate void SendDelegate(ServerReturnInfo sri); private SendDelegate SendInfo = null; private delegate void EndDelegate(Communication commun); private EndDelegate EndInfo = null; private List<Socket> socketList = new List<Socket>();//成员集合 private Socket server = null; private void 开启服务ToolStripMenuItem_Click(object sender, EventArgs e) { int port = 0; server = new Socket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp); if (!int.TryParse(txtPort.Text, out port)) port = 8080; EndPoint endPoint = new IPEndPoint(IPAddress.Any,port); server.Bind(endPoint); server.Listen(1000); rtbMessage.Text = "服务器已经打开!"; Thread t = new Thread(GetClient); t.Start(server); } public void GetClient(object s)//获取客户端实例 { Socket server = s as Socket; Socket client = null; while(true) { try { client = server.Accept(); } catch (Exception) { return; } socketList.Add(client);//把客户端实例添加到集合中 DisMantle(client); Thread t2 = new Thread(ReceiveInfo); t2.Start(client); } } //客户端人物信息绑定与上线提醒 public void DisMantle(Socket client) { string endPoint = client.RemoteEndPoint.ToString(); string ip = endPoint.Split(':')[0]; string port = endPoint.Split(':')[1]; string name = Dns.GetHostEntry(ip).HostName; Communication comm = new Communication() { Name = name, Ip = ip, Port = port, Message = name + "上线了!" }; //通过委托来进行对控件的跨线程修改 //绑定客户端人物信息到ListView控件上 listInfo = new ListDelegate(BindListView); this.Invoke(listInfo, comm); //添加聊天消息 MessageInfo = new MessageDelegate(AddMessage); this.Invoke(MessageInfo, comm); ServerReturnInfo sri = new ServerReturnInfo() { comm = comm, client = client }; SendInfo = new SendDelegate(SendInfos); this.Invoke(SendInfo, sri); } public void SendInfos(object c) //发送列表成员信息到客户端 { ServerReturnInfo sri = c as ServerReturnInfo; byte[] b = null; foreach (Socket s in socketList) { sri.comm.Types = 2; sri.comm.IsFistLogin = true; for (int i = 0; i < lvInfo.Items.Count; i++) { sri.comm.Name = lvInfo.Items[i].SubItems[0].Text; sri.comm.Ip = lvInfo.Items[i].SubItems[1].Text; sri.comm.Port = lvInfo.Items[i].SubItems[2].Text; b = SerializeObject(sri.comm); s.Send(b); sri.comm.IsFistLogin = false; } } } public void EndClient(Communication commun) //移除成员列表里离线的成员 { foreach (ListViewItem items in lvInfo.Items) { //如果成员列表上有此用户,则移除 if (items.SubItems[0].Text.Equals(commun.Name)) { items.Remove(); break; } } } public void ReceiveInfo(object o) //获取客户端信息 { Socket client = o as Socket; byte[] b = new byte[1024]; Communication commun = null; while (true) { try { client.Receive(b); commun = DeserializeObject(b) as Communication; } catch (Exception) { return; } if (commun.Types == 1)//客户端发送过来的信息 { MessageInfo = new MessageDelegate(AddMessage); this.Invoke(MessageInfo, commun); byte[] bb = SerializeObject(commun); foreach (Socket c in socketList) { c.Send(bb);//把客户端的消息发送到其它的客户端 } } else if (commun.Types == 3) //私聊,判断终结点是否一致 { MessageInfo = new MessageDelegate(AddMessage); this.Invoke(MessageInfo, commun); byte[] bb = SerializeObject(commun); foreach (Socket c in socketList) { string ClientEndPoint = c.RemoteEndPoint.ToString(); if (commun.ToEndPoint.Equals(ClientEndPoint)) { c.Send(bb); break; } } }else if(commun.Types == 4) { MessageInfo = new MessageDelegate(AddMessage); this.Invoke(MessageInfo, commun); byte[] bb = SerializeObject(commun); try { foreach (Socket c in socketList) { if (c.Connected) c.Send(bb);//把客户端的离线消息发送到其它的客户端 } } catch (Exception) { socketList.Remove(client);//从集合中删除该用户 EndInfo = new EndDelegate(EndClient); this.Invoke(EndInfo, commun); client.Dispose();//关闭端口 return; } } } } public void ClearList() { lvInfo.Items.Clear();//清除ListView控件上的数据 } public void BindListView(Communication comm) //绑定客户端人物信息到ListView控件上 { ListViewItem items = new ListViewItem(comm.Name); items.SubItems.Add(comm.Ip); items.SubItems.Add(comm.Port); lvInfo.Items.Add(items); } public void AddMessage(Communication comm)//添加聊天消息 { rtbMessage.Text += "\n"+comm.Name + ":" + comm.Message; } public object DeserializeObject(byte[] pBytes)//反序列化二进制为对象 { object newOjb = null; if (pBytes == null) return newOjb; MemoryStream memory = new MemoryStream(pBytes); memory.Position = 0; BinaryFormatter formatter = new BinaryFormatter(); newOjb = formatter.Deserialize(memory); memory.Close(); return newOjb; } public byte[] SerializeObject(object pObj)//序列化对象为二进制的数 { if (pObj == null) return null; MemoryStream memory = new MemoryStream(); BinaryFormatter formatter = new BinaryFormatter(); formatter.Serialize(memory, pObj); memory.Position = 0; byte[] read = new byte[memory.Length]; memory.Read(read, 0, read.Length); memory.Close(); return read; } private void 退出程序ToolStripMenuItem_Click(object sender, EventArgs e) { Application.Exit(); } private void forServer_FormClosing(object sender, FormClosingEventArgs e) { //检查服务是否还在工作 } private void forServer_Load(object sender, EventArgs e) { txtPort.Text = "8080"; } } }
客户端类(Client):
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.IO; using System.Linq; using System.Net; using System.Net.Sockets; using System.Runtime.Serialization.Formatters.Binary; using System.Text; using System.Threading; using System.Threading.Tasks; using System.Windows.Forms; using Server; namespace Client { public partial class forClient : Form { public forClient() { InitializeComponent(); } private delegate void ListDelegate(Communication comm); private ListDelegate listInfo = null;//用于绑定成员 private delegate void MessageDelegate(Communication comm); private MessageDelegate MessageInfo = null;//用于添加聊天消息 private delegate void ClearDelegate(); private ClearDelegate ClearInfo = null;//用于清除成员列表 private delegate void EndDelegate(Communication commun); private EndDelegate EndInfo = null; Socket client = null; EndPoint endPoint = null; private void 开启服务ToolStripMenuItem_Click(object sender, EventArgs e) { int port = 0; client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); if (!int.TryParse(txtPort.Text, out port)) port = 8080; endPoint = new IPEndPoint(IPAddress.Parse(txtIP.Text), port); client.Connect(endPoint); rtbMessage.Text = "连接服务器成功!"; Thread t = new Thread(ReceiveInfo); t.Start(client); } public void ClearList() //清除成员列表 { this.lvInfo.Clear(); } public void EndClient(Communication commun) //移除成员列表里离线的成员 { foreach (ListViewItem items in lvInfo.Items) { //如果成员列表上有此用户,则移除 if (items.SubItems[0].Text.Equals(commun.Name)) { items.Remove(); break; } } } public void ReceiveInfo(object c)//接收服务器的信息 { byte[] b = new byte[1024]; Socket client = c as Socket; ClearInfo = new ClearDelegate(ClearList); while(true) { try { client.Receive(b); } catch (Exception) { client.Dispose(); return; } Communication comm = DeserializeObject(b) as Communication; if (comm.Types == 2)//服务器回发过来的信息 { listInfo = new ListDelegate(BindListView); this.Invoke(listInfo, comm); } else if (comm.Types == 1 || comm.Types == 3) { MessageInfo = new MessageDelegate(AddMessage); this.Invoke(MessageInfo, comm); } else //下线执行 { MessageInfo = new MessageDelegate(AddMessage); this.Invoke(MessageInfo, comm); EndInfo = new EndDelegate(EndClient); this.Invoke(EndInfo,comm); } } } public void BindListView(Communication comm) //绑定客户端人物信息到ListView控件上 { if(comm.IsFistLogin) { lvInfo.Items.Clear();//第一次添加的时候(判定为true时)清除原有的旧成员 } ListViewItem items = new ListViewItem(comm.Name); items.SubItems.Add(comm.Ip); items.SubItems.Add(comm.Port); lvInfo.Items.Add(items); } public void AddMessage(Communication comm)//添加聊天消息 { rtbMessage.Text += "\n"+comm.Name + ":" + comm.Message; } public object DeserializeObject(byte[] pBytes)//反序列化二进制为对象 { object newOjb = null; if (pBytes == null) return newOjb; MemoryStream memory = new MemoryStream(pBytes); memory.Position = 0; BinaryFormatter formatter = new BinaryFormatter(); newOjb = formatter.Deserialize(memory); memory.Close(); return newOjb; } public byte[] SerializeObject(object pObj)//序列化对象为二进制的数 { if (pObj == null) return null; MemoryStream memory = new MemoryStream(); BinaryFormatter formatter = new BinaryFormatter(); formatter.Serialize(memory, pObj); memory.Position = 0; byte[] read = new byte[memory.Length]; memory.Read(read, 0, read.Length); memory.Close(); return read; } private void btnSend_Click(object sender, EventArgs e) { Communication comm = new Communication() { Name = Dns.GetHostName(), Message = txtMessage.Text, Types = 1 }; if (lvInfo.SelectedItems.Count > 0)//私聊 { comm.Types = 3; string ePoint = lvInfo.SelectedItems[0].SubItems[1].Text +":"+ lvInfo.SelectedItems[0].SubItems[2].Text; comm.ToEndPoint = ePoint; } byte[] b = SerializeObject(comm); client.Send(b); lvInfo.SelectedItems.Clear();//清除选中的个数 } private void 退出程序ToolStripMenuItem_Click(object sender, EventArgs e) { Communication comm = new Communication() { Name = Dns.GetHostName(), Message = "下线了!", Types = 4//用户下线类型 }; byte[] b = SerializeObject(comm); client.Send(b); client.Dispose(); Application.Exit(); } private void forClient_Load(object sender, EventArgs e) { //127.0.0.1 用于获取本机IP txtIP.Text = "192.168.1.100"; txtPort.Text = "8080"; } } }