局域网之间通讯有很多种方法,比如可以用数据库存储的方式实现,还可以用udp来实现,这里是用tcp来实现的,用tcp来通讯的话可以说是有点麻烦的,因为要保持各个用户与服务器之间的连接,连接一断便不能通讯了,所以个人觉得这个地方比较麻烦;
要实现Socket通讯的话首先可以把它分成两个项目来区别,一个是服务器项目(Server),一个是客户端项目(Client)。
服务器项目的实现可以大致的分成 部分,1:创建一个总连接点,这个连接点负责与客户端的连接,我们不能把连接和通讯都引用到一个连接上,这样服务器会崩溃的,而每个用户都是独立的,所以我们要利用线程来创建一个新的通讯实例,这个连接和通讯各个用户之间都是互不干扰的了,还有一个要注意的就是跨线程调用控件的话是不行的,所以我们又要利用委托存放要调用的功能在用this.Invoke()的方法来跨线程引用控件,接下来就是具体实现响应通讯了;
客户端相对来说要更容易些,创建一个客户端连接实例,与远程服务器连接起来,然后就是大致的通讯实现功能了。
下面是我个人的实现代码和实现的心得,附加了大致的注释,实现了群聊,私聊等功能。
通讯类(Communication)
1 using System;
2 using System.Collections.Generic;
3 using System.Linq;
4 using System.Text;
5 using System.Threading.Tasks;
6
7 namespace Server
8 {
9 [Serializable]
10 public class Communication//通讯
11 {
12 public string Name { get; set; }
13 public string Ip { get; set; }//Ip地址
14 public string Port { get; set; }//端口号
15 public string Message { get; set; }//发送的消息
16 public int Types { get; set; }
17 //1代表聊天信息 2代表客户端人物信息
18 public bool IsFistLogin { get; set; }
19 public string ToEndPoint { get; set; }
20 }
21 }
View Code
1 using System;
2 using System.Collections.Generic;
3 using System.Linq;
4 using System.Net.Sockets;
5 using System.Text;
6 using System.Threading.Tasks;
7
8 namespace Server
9 {
10 [Serializable]
11 public class ServerReturnInfo
12 {
13 public Communication comm { get; set; }//通讯类
14 public Socket client { get; set; }
15 }
16 }
View Code
服务器类(Server)
1 using System;
2 using System.Collections.Generic;
3 using System.ComponentModel;
4 using System.Data;
5 using System.Drawing;
6 using System.IO;
7 using System.Linq;
8 using System.Net;
9 using System.Net.Sockets;
10 using System.Runtime.Serialization.Formatters.Binary;
11 using System.Text;
12 using System.Threading;
13 using System.Threading.Tasks;
14 using System.Windows.Forms;
15
16 namespace Server
17 {
18 public partial class forServer : Form
19 {
20 public forServer()
21 {
22 InitializeComponent();
23 }
24
25 private delegate void ListDelegate(Communication comm);
26 private ListDelegate listInfo = null;//添加成员
27 private delegate void MessageDelegate(Communication comm);
28 private MessageDelegate MessageInfo = null;//添加消息
29 private delegate void SendDelegate(ServerReturnInfo sri);
30 private SendDelegate SendInfo = null;
31 private delegate void EndDelegate(Communication commun);
32 private EndDelegate EndInfo = null;
33 private List<Socket> socketList = new List<Socket>();//成员集合
34 private Socket server = null;
35 private void 开启服务ToolStripMenuItem_Click(object sender, EventArgs e)
36 {
37 int port = 0;
38 server = new Socket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp);
39 if (!int.TryParse(txtPort.Text, out port))
40 port = 8080;
41 EndPoint endPoint = new IPEndPoint(IPAddress.Any,port);
42 server.Bind(endPoint);
43 server.Listen(1000);
44 rtbMessage.Text = "服务器已经打开!";
45 Thread t = new Thread(GetClient);
46 t.Start(server);
47 }
48 public void GetClient(object s)//获取客户端实例
49 {
50 Socket server = s as Socket;
51 Socket client = null;
52 while(true)
53 {
54 try
55 {
56 client = server.Accept();
57 }
58 catch (Exception)
59 {
60 return;
61 }
62 socketList.Add(client);//把客户端实例添加到集合中
63 DisMantle(client);
64 Thread t2 = new Thread(ReceiveInfo);
65 t2.Start(client);
66 }
67 }
68 //客户端人物信息绑定与上线提醒
69 public void DisMantle(Socket client)
70 {
71 string endPoint = client.RemoteEndPoint.ToString();
72 string ip = endPoint.Split(':')[0];
73 string port = endPoint.Split(':')[1];
74 string name = Dns.GetHostEntry(ip).HostName;
75 Communication comm = new Communication()
76 {
77 Name = name,
78 Ip = ip,
79 Port = port,
80 Message = name + "上线了!"
81 };
82 //通过委托来进行对控件的跨线程修改
83 //绑定客户端人物信息到ListView控件上
84 listInfo = new ListDelegate(BindListView);
85 this.Invoke(listInfo, comm);
86 //添加聊天消息
87 MessageInfo = new MessageDelegate(AddMessage);
88 this.Invoke(MessageInfo, comm);
89 ServerReturnInfo sri = new ServerReturnInfo()
90 {
91 comm = comm,
92 client = client
93 };
94 SendInfo = new SendDelegate(SendInfos);
95 this.Invoke(SendInfo, sri);
96 }
97
98 public void SendInfos(object c) //发送列表成员信息到客户端
99 {
100 ServerReturnInfo sri = c as ServerReturnInfo;
101 byte[] b = null;
102 foreach (Socket s in socketList)
103 {
104 sri.comm.Types = 2;
105 sri.comm.IsFistLogin = true;
106 for (int i = 0; i < lvInfo.Items.Count; i++)
107 {
108 sri.comm.Name = lvInfo.Items[i].SubItems[0].Text;
109 sri.comm.Ip = lvInfo.Items[i].SubItems[1].Text;
110 sri.comm.Port = lvInfo.Items[i].SubItems[2].Text;
111 b = SerializeObject(sri.comm);
112 s.Send(b);
113 sri.comm.IsFistLogin = false;
114 }
115 }
116 }
117 public void EndClient(Communication commun) //移除成员列表里离线的成员
118 {
119 foreach (ListViewItem items in lvInfo.Items)
120 {
121 //如果成员列表上有此用户,则移除
122 if (items.SubItems[0].Text.Equals(commun.Name))
123 {
124 items.Remove();
125 break;
126 }
127 }
128 }
129 public void ReceiveInfo(object o) //获取客户端信息
130 {
131 Socket client = o as Socket;
132 byte[] b = new byte[1024];
133 Communication commun = null;
134 while (true)
135 {
136 try
137 {
138 client.Receive(b);
139 commun = DeserializeObject(b) as Communication;
140 }
141 catch (Exception)
142 {
143
144 return;
145 }
146 if (commun.Types == 1)//客户端发送过来的信息
147 {
148 MessageInfo = new MessageDelegate(AddMessage);
149 this.Invoke(MessageInfo, commun);
150 byte[] bb = SerializeObject(commun);
151 foreach (Socket c in socketList)
152 {
153 c.Send(bb);//把客户端的消息发送到其它的客户端
154 }
155 }
156 else if (commun.Types == 3) //私聊,判断终结点是否一致
157 {
158 MessageInfo = new MessageDelegate(AddMessage);
159 this.Invoke(MessageInfo, commun);
160 byte[] bb = SerializeObject(commun);
161 foreach (Socket c in socketList)
162 {
163 string ClientEndPoint = c.RemoteEndPoint.ToString();
164 if (commun.ToEndPoint.Equals(ClientEndPoint))
165 {
166 c.Send(bb);
167 break;
168 }
169 }
170 }else if(commun.Types == 4)
171 {
172 MessageInfo = new MessageDelegate(AddMessage);
173 this.Invoke(MessageInfo, commun);
174 byte[] bb = SerializeObject(commun);
175 try
176 {
177 foreach (Socket c in socketList)
178 {
179 if (c.Connected)
180 c.Send(bb);//把客户端的离线消息发送到其它的客户端
181 }
182 }
183 catch (Exception)
184 {
185 socketList.Remove(client);//从集合中删除该用户
186 EndInfo = new EndDelegate(EndClient);
187 this.Invoke(EndInfo, commun);
188 client.Dispose();//关闭端口
189 return;
190 }
191 }
192 }
193 }
194 public void ClearList()
195 {
196 lvInfo.Items.Clear();//清除ListView控件上的数据
197 }
198 public void BindListView(Communication comm) //绑定客户端人物信息到ListView控件上
199 {
200 ListViewItem items = new ListViewItem(comm.Name);
201 items.SubItems.Add(comm.Ip);
202 items.SubItems.Add(comm.Port);
203 lvInfo.Items.Add(items);
204 }
205 public void AddMessage(Communication comm)//添加聊天消息
206 {
207 rtbMessage.Text += "\n"+comm.Name + ":" + comm.Message;
208 }
209
210 public object DeserializeObject(byte[] pBytes)//反序列化二进制为对象
211 {
212 object newOjb = null;
213 if (pBytes == null)
214 return newOjb;
215 MemoryStream memory = new MemoryStream(pBytes);
216 memory.Position = 0;
217 BinaryFormatter formatter = new BinaryFormatter();
218 newOjb = formatter.Deserialize(memory);
219 memory.Close();
220 return newOjb;
221 }
222 public byte[] SerializeObject(object pObj)//序列化对象为二进制的数
223 {
224 if (pObj == null)
225 return null;
226 MemoryStream memory = new MemoryStream();
227 BinaryFormatter formatter = new BinaryFormatter();
228 formatter.Serialize(memory, pObj);
229 memory.Position = 0;
230 byte[] read = new byte[memory.Length];
231 memory.Read(read, 0, read.Length);
232 memory.Close();
233 return read;
234 }
235
236 private void 退出程序ToolStripMenuItem_Click(object sender, EventArgs e)
237 {
238 Application.Exit();
239 }
240
241 private void forServer_FormClosing(object sender, FormClosingEventArgs e)
242 {
243 //检查服务是否还在工作
244 }
245
246 private void forServer_Load(object sender, EventArgs e)
247 {
248 txtPort.Text = "8080";
249 }
250 }
251 }
View Code
客户端类(Client)
1 using System;
2 using System.Collections.Generic;
3 using System.ComponentModel;
4 using System.Data;
5 using System.Drawing;
6 using System.IO;
7 using System.Linq;
8 using System.Net;
9 using System.Net.Sockets;
10 using System.Runtime.Serialization.Formatters.Binary;
11 using System.Text;
12 using System.Threading;
13 using System.Threading.Tasks;
14 using System.Windows.Forms;
15 using Server;
16
17 namespace Client
18 {
19 public partial class forClient : Form
20 {
21 public forClient()
22 {
23 InitializeComponent();
24 }
25
26 private delegate void ListDelegate(Communication comm);
27 private ListDelegate listInfo = null;//用于绑定成员
28 private delegate void MessageDelegate(Communication comm);
29 private MessageDelegate MessageInfo = null;//用于添加聊天消息
30 private delegate void ClearDelegate();
31 private ClearDelegate ClearInfo = null;//用于清除成员列表
32 private delegate void EndDelegate(Communication commun);
33 private EndDelegate EndInfo = null;
34 Socket client = null;
35 EndPoint endPoint = null;
36 private void 开启服务ToolStripMenuItem_Click(object sender, EventArgs e)
37 {
38 int port = 0;
39 client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
40 if (!int.TryParse(txtPort.Text, out port))
41 port = 8080;
42 endPoint = new IPEndPoint(IPAddress.Parse(txtIP.Text), port);
43 client.Connect(endPoint);
44 rtbMessage.Text = "连接服务器成功!";
45 Thread t = new Thread(ReceiveInfo);
46 t.Start(client);
47 }
48 public void ClearList() //清除成员列表
49 {
50 this.lvInfo.Clear();
51 }
52 public void EndClient(Communication commun) //移除成员列表里离线的成员
53 {
54 foreach (ListViewItem items in lvInfo.Items)
55 {
56 //如果成员列表上有此用户,则移除
57 if (items.SubItems[0].Text.Equals(commun.Name))
58 {
59 items.Remove();
60 break;
61 }
62 }
63 }
64 public void ReceiveInfo(object c)//接收服务器的信息
65 {
66 byte[] b = new byte[1024];
67 Socket client = c as Socket;
68 ClearInfo = new ClearDelegate(ClearList);
69 while(true)
70 {
71 try
72 {
73 client.Receive(b);
74 }
75 catch (Exception)
76 {
77 client.Dispose();
78 return;
79 }
80 Communication comm = DeserializeObject(b) as Communication;
81 if (comm.Types == 2)//服务器回发过来的信息
82 {
83 listInfo = new ListDelegate(BindListView);
84 this.Invoke(listInfo, comm);
85 }
86 else if (comm.Types == 1 || comm.Types == 3)
87 {
88 MessageInfo = new MessageDelegate(AddMessage);
89 this.Invoke(MessageInfo, comm);
90 }
91 else //下线执行
92 {
93 MessageInfo = new MessageDelegate(AddMessage);
94 this.Invoke(MessageInfo, comm);
95 EndInfo = new EndDelegate(EndClient);
96 this.Invoke(EndInfo,comm);
97 }
98 }
99 }
100 public void BindListView(Communication comm) //绑定客户端人物信息到ListView控件上
101 {
102 if(comm.IsFistLogin)
103 {
104 lvInfo.Items.Clear();//第一次添加的时候(判定为true时)清除原有的旧成员
105 }
106 ListViewItem items = new ListViewItem(comm.Name);
107 items.SubItems.Add(comm.Ip);
108 items.SubItems.Add(comm.Port);
109 lvInfo.Items.Add(items);
110 }
111 public void AddMessage(Communication comm)//添加聊天消息
112 {
113 rtbMessage.Text += "\n"+comm.Name + ":" + comm.Message;
114 }
115 public object DeserializeObject(byte[] pBytes)//反序列化二进制为对象
116 {
117 object newOjb = null;
118 if (pBytes == null)
119 return newOjb;
120 MemoryStream memory = new MemoryStream(pBytes);
121 memory.Position = 0;
122 BinaryFormatter formatter = new BinaryFormatter();
123 newOjb = formatter.Deserialize(memory);
124 memory.Close();
125 return newOjb;
126 }
127 public byte[] SerializeObject(object pObj)//序列化对象为二进制的数
128 {
129 if (pObj == null)
130 return null;
131 MemoryStream memory = new MemoryStream();
132 BinaryFormatter formatter = new BinaryFormatter();
133 formatter.Serialize(memory, pObj);
134 memory.Position = 0;
135 byte[] read = new byte[memory.Length];
136 memory.Read(read, 0, read.Length);
137 memory.Close();
138 return read;
139 }
140
141 private void btnSend_Click(object sender, EventArgs e)
142 {
143 Communication comm = new Communication() {
144 Name = Dns.GetHostName(),
145 Message = txtMessage.Text,
146 Types = 1
147 };
148 if (lvInfo.SelectedItems.Count > 0)//私聊
149 {
150 comm.Types = 3;
151 string ePoint = lvInfo.SelectedItems[0].SubItems[1].Text +":"+ lvInfo.SelectedItems[0].SubItems[2].Text;
152 comm.ToEndPoint = ePoint;
153 }
154 byte[] b = SerializeObject(comm);
155 client.Send(b);
156 lvInfo.SelectedItems.Clear();//清除选中的个数
157 }
158
159 private void 退出程序ToolStripMenuItem_Click(object sender, EventArgs e)
160 {
161 Communication comm = new Communication()
162 {
163 Name = Dns.GetHostName(),
164 Message = "下线了!",
165 Types = 4//用户下线类型
166 };
167 byte[] b = SerializeObject(comm);
168 client.Send(b);
169 client.Dispose();
170 Application.Exit();
171 }
172
173 private void forClient_Load(object sender, EventArgs e)
174 {
175 //127.0.0.1 用于获取本机IP
176 txtIP.Text = "192.168.1.100";
177 txtPort.Text = "8080";
178 }
179 }
180 }
View Code