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
浙公网安备 33010602011771号