微笑吧少年

导航

C#Winform窗体实现服务端和客户端通信例子(TCP, 直接Socket方式)

C#Winform窗体实现服务端和客户端通信例子(TCP, 直接Socket方式)

进行了一些异常处理,提示信息的补充,还有新增获取本地IP的方法

1、通信原理

1)服务端与客户端

 启动服务端后,服务端通过持续监听客户端发来的请求,一旦监听到客户端传来的信息(请求),两端便可以互发信息了.

服务端需要绑定一个IP,用于客户端在网络中寻找并建立连接(支持局域网内部客户端与服务端之间的互相通信)

2)信息发送原理

将手动输入字符串信息转换成机器可以识别的字节数组,然后调用套接字的Send()方法将字节数组发送出去

3)信息接收原理

调用套接字的Receive()方法,获取对端传来的字节数组,然后将其转换成人可以读懂的字符串信息

2、界面设计

1)服务端

文本框类

IP地址, name:txtIP ; 本地端口,name:txtPort;

聊天信息,name:txtMsg;发送消息:txtSendMsg

按钮类

获取IP, name :btnGetLocalIP, 获取本地的IP的方法

启动服务,name:btnServerConn, 支持服务端与客户端通信的前提

发送消息,name:btnSendMsg ,发送消息到客户端的方法

 

2)客户端

文本框类

IP地址, name:txtIP ; 本地端口,name:txtPort;

聊天信息,name:txtMsg;发送消息:txtClientSendMsg

按钮类

连接服务端,name:btnListenServer, 客户端连接服务端的方法

发送消息,name:btnSendMsg ,发送消息到服务端的方法

 

3、源码例子

1)服务端代码

  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.Net;
  8 using System.Net.Sockets;
  9 using System.Text;
 10 using System.Threading;
 11 using System.Threading.Tasks;
 12 using System.Windows.Forms;
 13 
 14 namespace TcpMsgServer
 15 {
 16     public partial class FrmServer : Form
 17     {
 18         public FrmServer()
 19         {
 20             InitializeComponent();
 21             //关闭对文本框的非法线程操作检查
 22             TextBox.CheckForIllegalCrossThreadCalls = false;
 23         }
 24 
 25         Thread threadWatch = null; //负责监听客户端的线程
 26         Socket socketWatch = null;  //负责监听客户端的套接字     
 27         
 28         /// <summary>
 29         /// 启动服务
 30         /// </summary>
 31         /// <param name="sender"></param>
 32         /// <param name="e"></param>
 33         private void btnServerConn_Click(object sender, EventArgs e)
 34         {
 35             try
 36             {
 37                 //定义一个套接字用于监听客户端发来的信息  包含3个参数(IP4寻址协议,流式连接,TCP协议)
 38                 socketWatch = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
 39                 //服务端发送信息 需要1个IP地址和端口号
 40                 IPAddress ipaddress = IPAddress.Parse(this.txtIP.Text.Trim()); //获取文本框输入的IP地址
 41                 //将IP地址和端口号绑定到网络节点endpoint上 
 42                 IPEndPoint endpoint = new IPEndPoint(ipaddress, int.Parse(this.txtPort.Text.Trim())); //获取文本框上输入的端口号
 43                 //监听绑定的网络节点
 44                 socketWatch.Bind(endpoint);
 45                 //将套接字的监听队列长度限制为20
 46                 socketWatch.Listen(20);
 47                 //创建一个监听线程 
 48                 threadWatch = new Thread(WatchConnecting);
 49                 //将窗体线程设置为与后台同步
 50                 threadWatch.IsBackground = true;
 51                 //启动线程
 52                 threadWatch.Start();
 53                 //启动线程后 txtMsg文本框显示相应提示
 54                 txtMsg.AppendText("开始监听客户端传来的信息!" + "\r\n");
 55                 this.btnServerConn.Enabled = false;
 56             }
 57             catch (Exception ex) {
 58                 txtMsg.AppendText("服务端启动服务失败!" + "\r\n");
 59                 this.btnServerConn.Enabled = true;
 60             }
 61         }
 62 
 63         //创建一个负责和客户端通信的套接字 
 64         Socket socConnection = null;
 65 
 66         /// <summary>
 67         /// 监听客户端发来的请求
 68         /// </summary>
 69         private void WatchConnecting()
 70         {
 71             while (true)  //持续不断监听客户端发来的请求
 72             {
 73                 socConnection = socketWatch.Accept();
 74                 txtMsg.AppendText("客户端连接成功! " + "\r\n");
 75                 //创建一个通信线程 
 76                 ParameterizedThreadStart pts = new ParameterizedThreadStart(ServerRecMsg);
 77                 Thread thr = new Thread(pts);
 78                 thr.IsBackground = true;
 79                 //启动线程
 80                 thr.Start(socConnection);
 81             }
 82         }
 83 
 84         /// <summary>
 85         /// 发送信息到客户端的方法
 86         /// </summary>
 87         /// <param name="sendMsg">发送的字符串信息</param>
 88         private void ServerSendMsg(string sendMsg)
 89         {
 90             try
 91             {
 92                 //将输入的字符串转换成 机器可以识别的字节数组
 93                 byte[] arrSendMsg = Encoding.UTF8.GetBytes(sendMsg);
 94                 //向客户端发送字节数组信息
 95                 socConnection.Send(arrSendMsg);
 96                 //将发送的字符串信息附加到文本框txtMsg上
 97                 txtMsg.AppendText("服务器 " + GetCurrentTime() + "\r\n" + sendMsg + "\r\n");
 98             }
 99             catch (Exception ex) {
100                 txtMsg.AppendText("客户端已断开连接,无法发送信息!" + "\r\n");
101             }
102         }
103 
104         /// <summary>
105         /// 接收客户端发来的信息 
106         /// </summary>
107         /// <param name="socketClientPara">客户端套接字对象</param>
108         private void ServerRecMsg(object socketClientPara)
109         {
110             Socket socketServer = socketClientPara as Socket;
111             while (true)
112             {
113                 //创建一个内存缓冲区 其大小为1024*1024字节  即1M
114                 byte[] arrServerRecMsg = new byte[1024 * 1024];
115                 try
116                 {
117                     //将接收到的信息存入到内存缓冲区,并返回其字节数组的长度
118                     int length = socketServer.Receive(arrServerRecMsg);
119                     //将机器接受到的字节数组转换为人可以读懂的字符串
120                     string strSRecMsg = Encoding.UTF8.GetString(arrServerRecMsg, 0, length);
121                     //将发送的字符串信息附加到文本框txtMsg上  
122                     txtMsg.AppendText("天涯 " + GetCurrentTime() + "\r\n" + strSRecMsg + "\r\n");
123                 }
124                 catch (Exception ex) {
125                     txtMsg.AppendText("客户端已断开连接!" + "\r\n");
126                     break;
127                 }
128             }
129         }
130 
131         /// <summary>
132         /// 发送消息到客户端
133         /// </summary>
134         /// <param name="sender"></param>
135         /// <param name="e"></param>
136         private void btnSendMsg_Click(object sender, EventArgs e)
137         {
138             //调用 ServerSendMsg方法  发送信息到客户端
139             ServerSendMsg(this.txtSendMsg.Text.Trim());
140             this.txtSendMsg.Clear();
141         }
142 
143         /// <summary>
144         /// 快捷键 Enter 发送信息
145         /// </summary>
146         /// <param name="sender"></param>
147         /// <param name="e"></param>
148         private void txtSendMsg_KeyDown(object sender, KeyEventArgs e)
149         {
150             //如果用户按下了Enter键
151             if (e.KeyCode == Keys.Enter)
152             {
153                 //则调用 服务器向客户端发送信息的方法
154                 ServerSendMsg(txtSendMsg.Text.Trim());
155                 this.txtSendMsg.Clear();
156             }
157         }
158 
159         /// <summary>
160         /// 获取当前系统时间的方法
161         /// </summary>
162         /// <returns>当前时间</returns>
163         private DateTime GetCurrentTime()
164         {
165             DateTime currentTime = new DateTime();
166             currentTime = DateTime.Now;
167             return currentTime;
168         }
169 
170         /// <summary>
171         /// 获取本地IPv4地址
172         /// </summary>
173         /// <returns></returns>
174         public IPAddress GetLocalIPv4Address() {
175             IPAddress localIpv4 = null;
176             //获取本机所有的IP地址列表
177             IPAddress[] IpList = Dns.GetHostAddresses(Dns.GetHostName());
178             //循环遍历所有IP地址
179             foreach (IPAddress IP in IpList) {
180                 //判断是否是IPv4地址
181                 if (IP.AddressFamily == AddressFamily.InterNetwork)
182                 {
183                     localIpv4 = IP;
184                 }
185                 else {
186                     continue;
187                 }
188             }
189             return localIpv4;
190         }
191 
192         /// <summary>
193         /// 获取本地IP事件
194         /// </summary>
195         /// <param name="sender"></param>
196         /// <param name="e"></param>
197         private void btnGetLocalIP_Click(object sender, EventArgs e)
198         {
199             //接收IPv4的地址
200             IPAddress localIP = GetLocalIPv4Address();
201             //赋值给文本框
202             this.txtIP.Text = localIP.ToString();
203            
204         }
205     }
206 }
207 2)客户端代码
208 
209 using System;
210 using System.Collections.Generic;
211 using System.ComponentModel;
212 using System.Data;
213 using System.Drawing;
214 using System.Linq;
215 using System.Net;
216 using System.Net.Sockets;
217 using System.Text;
218 using System.Threading;
219 using System.Threading.Tasks;
220 using System.Windows.Forms;
221 
222 namespace TcpMsgClient
223 {
224     public partial class FrmClient : Form
225     {
226         public FrmClient()
227         {
228             InitializeComponent();
229             //关闭对文本框的非法线程操作检查
230             TextBox.CheckForIllegalCrossThreadCalls = false;
231         }
232         //创建 1个客户端套接字 和1个负责监听服务端请求的线程  
233         Socket socketClient = null;
234         Thread threadClient = null;
235 
236         /// <summary>
237         /// 连接服务端事件
238         /// </summary>
239         /// <param name="sender"></param>
240         /// <param name="e"></param>
241         private void btnListenServer_Click(object sender, EventArgs e)
242         {
243             //定义一个套字节监听  包含3个参数(IP4寻址协议,流式连接,TCP协议)
244             socketClient = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
245             //需要获取文本框中的IP地址
246             IPAddress ipaddress = IPAddress.Parse(this.txtIP.Text.Trim());
247             //将获取的ip地址和端口号绑定到网络节点endpoint上
248             IPEndPoint endpoint = new IPEndPoint(ipaddress, int.Parse(this.txtPort.Text.Trim()));
249             //这里客户端套接字连接到网络节点(服务端)用的方法是Connect 而不是Bind
250             try
251             {
252                 socketClient.Connect(endpoint);
253                 this.txtMsg.AppendText("客户端连接服务端成功!" + "\r\n");
254                 this.btnListenServer.Enabled = false;
255                 //创建一个线程 用于监听服务端发来的消息
256                 threadClient = new Thread(RecMsg);
257                 //将窗体线程设置为与后台同步
258                 threadClient.IsBackground = true;
259                 //启动线程
260                 threadClient.Start();
261             }
262             catch (Exception ex) {
263                 this.txtMsg.AppendText("远程服务端断开,连接失败!" + "\r\n");
264             }         
265         }
266 
267         /// <summary>
268         /// 接收服务端发来信息的方法
269         /// </summary>
270         private void RecMsg()
271         {
272             while (true) //持续监听服务端发来的消息
273             {
274                 try
275                 {
276                     //定义一个1M的内存缓冲区 用于临时性存储接收到的信息
277                     byte[] arrRecMsg = new byte[1024 * 1024];
278                     //将客户端套接字接收到的数据存入内存缓冲区, 并获取其长度
279                     int length = socketClient.Receive(arrRecMsg);
280                     //将套接字获取到的字节数组转换为人可以看懂的字符串
281                     string strRecMsg = Encoding.UTF8.GetString(arrRecMsg, 0, length);
282                     //将发送的信息追加到聊天内容文本框中
283                     txtMsg.AppendText("服务端 " + GetCurrentTime() + "\r\n" + strRecMsg + "\r\n");
284                 }
285                 catch (Exception ex) {
286                     this.txtMsg.AppendText("远程服务器已中断连接!"+"\r\n");
287                     this.btnListenServer.Enabled = true;
288                     break;
289                 }
290             }
291         }
292 
293         /// <summary>
294         /// 发送字符串信息到服务端的方法
295         /// </summary>
296         /// <param name="sendMsg">发送的字符串信息</param>
297         private void ClientSendMsg(string sendMsg)
298         {
299             try { 
300                  //将输入的内容字符串转换为机器可以识别的字节数组
301                 byte[] arrClientSendMsg = Encoding.UTF8.GetBytes(sendMsg);
302                 //调用客户端套接字发送字节数组
303                 socketClient.Send(arrClientSendMsg);
304                 //将发送的信息追加到聊天内容文本框中
305                 txtMsg.AppendText("天涯 " + GetCurrentTime() + "\r\n" + sendMsg + "\r\n");
306             }
307             catch(Exception ex){
308                 this.txtMsg.AppendText("远程服务器已中断连接,无法发送消息!" + "\r\n");
309             }
310         }
311 
312         private void btnSendMsg_Click(object sender, EventArgs e)
313         {
314             //调用ClientSendMsg方法 将文本框中输入的信息发送给服务端
315             ClientSendMsg(this.txtClientSendMsg.Text.Trim());
316             this.txtClientSendMsg.Clear();
317         }
318 
319         private void txtClientSendMsg_KeyDown(object sender, KeyEventArgs e)
320         {
321             //当光标位于文本框时 如果用户按下了键盘上的Enter键 
322             if (e.KeyCode == Keys.Enter)
323             {
324                 //则调用客户端向服务端发送信息的方法
325                 ClientSendMsg(this.txtClientSendMsg.Text.Trim());
326                 this.txtClientSendMsg.Clear();
327             }
328         }
329 
330         /// <summary>
331         /// 获取当前系统时间的方法
332         /// </summary>
333         /// <returns>当前时间</returns>
334         private DateTime GetCurrentTime()
335         {
336             DateTime currentTime = new DateTime();
337             currentTime = DateTime.Now;
338             return currentTime;
339         }
340     }
341 }

 

4、程序演示

1)正常情况

运行服务端程序,输入本地的IP地址,不知道可以点击获取本地IP按钮,输入本地端口,点击启动服务按钮,

消息框会出现开始监听客户端信息,表示服务端启动服务成功

 

运行客户端程序,输入服务端的IP和端口,点击连接服务端按钮,当服务端的消息框出现客户端连接成功,

表示连接服务端成功

 

 

服务端启动服务成功,客户端连接服务端成功,可以两端进行通信发消息

 

2)异常处理

原先参考的源码未做异常处理,导致退出程序报错,提示未友好等

当客户端异常处理

服务端中断开连接,自动提示

服务端中断连接,无法发送消息

 

服务端异常处理

客户端断开连接,自动提示

客户端断开连接,无法发送消息

 

Winform窗体实现服务端和客户端通信的例子,转载参考:

http://www.cnblogs.com/longwu/archive/2011/08/25/2153636.html

https://blog.csdn.net/weixin_34216196/article/details/85876202

 

Ps: 这个例子经过原作者测试,下面是源码下载路径:

http://pan.baidu.com/s/1nvRrCV3

posted on 2021-09-23 05:13  微笑吧少年  阅读(4043)  评论(0)    收藏  举报