• C#Winform窗体实现服务端和客户端通信(TCP/IP)


    Winform窗体实现服务端和客户端通信的例子

    进行了一些异常处理,提示信息的补充,还有新增获取本地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)服务端代码

    复制代码
    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Drawing;
    using System.Linq;
    using System.Net;
    using System.Net.Sockets;
    using System.Text;
    using System.Threading;
    using System.Threading.Tasks;
    using System.Windows.Forms;
    
    namespace TcpMsgServer
    {
        public partial class FrmServer : Form
        {
            public FrmServer()
            {
                InitializeComponent();
                //关闭对文本框的非法线程操作检查
                TextBox.CheckForIllegalCrossThreadCalls = false;
            }
    
            Thread threadWatch = null; //负责监听客户端的线程
            Socket socketWatch = null;  //负责监听客户端的套接字     
            
            /// <summary>
            /// 启动服务
            /// </summary>
            /// <param name="sender"></param>
            /// <param name="e"></param>
            private void btnServerConn_Click(object sender, EventArgs e)
            {
                try
                {
                    //定义一个套接字用于监听客户端发来的信息  包含3个参数(IP4寻址协议,流式连接,TCP协议)
                    socketWatch = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                    //服务端发送信息 需要1个IP地址和端口号
                    IPAddress ipaddress = IPAddress.Parse(this.txtIP.Text.Trim()); //获取文本框输入的IP地址
                    //将IP地址和端口号绑定到网络节点endpoint上 
                    IPEndPoint endpoint = new IPEndPoint(ipaddress, int.Parse(this.txtPort.Text.Trim())); //获取文本框上输入的端口号
                    //监听绑定的网络节点
                    socketWatch.Bind(endpoint);
                    //将套接字的监听队列长度限制为20
                    socketWatch.Listen(20);
                    //创建一个监听线程 
                    threadWatch = new Thread(WatchConnecting);
                    //将窗体线程设置为与后台同步
                    threadWatch.IsBackground = true;
                    //启动线程
                    threadWatch.Start();
                    //启动线程后 txtMsg文本框显示相应提示
                    txtMsg.AppendText("开始监听客户端传来的信息!" + "
    ");
                    this.btnServerConn.Enabled = false;
                }
                catch (Exception ex) {
                    txtMsg.AppendText("服务端启动服务失败!" + "
    ");
                    this.btnServerConn.Enabled = true;
                }
            }
    
            //创建一个负责和客户端通信的套接字 
            Socket socConnection = null;
    
            /// <summary>
            /// 监听客户端发来的请求
            /// </summary>
            private void WatchConnecting()
            {
                while (true)  //持续不断监听客户端发来的请求
                {
                    socConnection = socketWatch.Accept();
                    txtMsg.AppendText("客户端连接成功! " + "
    ");
                    //创建一个通信线程 
                    ParameterizedThreadStart pts = new ParameterizedThreadStart(ServerRecMsg);
                    Thread thr = new Thread(pts);
                    thr.IsBackground = true;
                    //启动线程
                    thr.Start(socConnection);
                }
            }
    
            /// <summary>
            /// 发送信息到客户端的方法
            /// </summary>
            /// <param name="sendMsg">发送的字符串信息</param>
            private void ServerSendMsg(string sendMsg)
            {
                try
                {
                    //将输入的字符串转换成 机器可以识别的字节数组
                    byte[] arrSendMsg = Encoding.UTF8.GetBytes(sendMsg);
                    //向客户端发送字节数组信息
                    socConnection.Send(arrSendMsg);
                    //将发送的字符串信息附加到文本框txtMsg上
                    txtMsg.AppendText("服务器 " + GetCurrentTime() + "
    " + sendMsg + "
    ");
                }
                catch (Exception ex) {
                    txtMsg.AppendText("客户端已断开连接,无法发送信息!" + "
    ");
                }
            }
    
            /// <summary>
            /// 接收客户端发来的信息 
            /// </summary>
            /// <param name="socketClientPara">客户端套接字对象</param>
            private void ServerRecMsg(object socketClientPara)
            {
                Socket socketServer = socketClientPara as Socket;
                while (true)
                {
                    //创建一个内存缓冲区 其大小为1024*1024字节  即1M
                    byte[] arrServerRecMsg = new byte[1024 * 1024];
                    try
                    {
                        //将接收到的信息存入到内存缓冲区,并返回其字节数组的长度
                        int length = socketServer.Receive(arrServerRecMsg);
                        //将机器接受到的字节数组转换为人可以读懂的字符串
                        string strSRecMsg = Encoding.UTF8.GetString(arrServerRecMsg, 0, length);
                        //将发送的字符串信息附加到文本框txtMsg上  
                        txtMsg.AppendText("天涯 " + GetCurrentTime() + "
    " + strSRecMsg + "
    ");
                    }
                    catch (Exception ex) {
                        txtMsg.AppendText("客户端已断开连接!" + "
    ");
                        break;
                    }
                }
            }
    
            /// <summary>
            /// 发送消息到客户端
            /// </summary>
            /// <param name="sender"></param>
            /// <param name="e"></param>
            private void btnSendMsg_Click(object sender, EventArgs e)
            {
                //调用 ServerSendMsg方法  发送信息到客户端
                ServerSendMsg(this.txtSendMsg.Text.Trim());
                this.txtSendMsg.Clear();
            }
    
            /// <summary>
            /// 快捷键 Enter 发送信息
            /// </summary>
            /// <param name="sender"></param>
            /// <param name="e"></param>
            private void txtSendMsg_KeyDown(object sender, KeyEventArgs e)
            {
                //如果用户按下了Enter键
                if (e.KeyCode == Keys.Enter)
                {
                    //则调用 服务器向客户端发送信息的方法
                    ServerSendMsg(txtSendMsg.Text.Trim());
                    this.txtSendMsg.Clear();
                }
            }
    
            /// <summary>
            /// 获取当前系统时间的方法
            /// </summary>
            /// <returns>当前时间</returns>
            private DateTime GetCurrentTime()
            {
                DateTime currentTime = new DateTime();
                currentTime = DateTime.Now;
                return currentTime;
            }
    
            /// <summary>
            /// 获取本地IPv4地址
            /// </summary>
            /// <returns></returns>
            public IPAddress GetLocalIPv4Address() {
                IPAddress localIpv4 = null;
                //获取本机所有的IP地址列表
                IPAddress[] IpList = Dns.GetHostAddresses(Dns.GetHostName());
                //循环遍历所有IP地址
                foreach (IPAddress IP in IpList) {
                    //判断是否是IPv4地址
                    if (IP.AddressFamily == AddressFamily.InterNetwork)
                    {
                        localIpv4 = IP;
                    }
                    else {
                        continue;
                    }
                }
                return localIpv4;
            }
    
            /// <summary>
            /// 获取本地IP事件
            /// </summary>
            /// <param name="sender"></param>
            /// <param name="e"></param>
            private void btnGetLocalIP_Click(object sender, EventArgs e)
            {
                //接收IPv4的地址
                IPAddress localIP = GetLocalIPv4Address();
                //赋值给文本框
                this.txtIP.Text = localIP.ToString();
               
            }
        }
    }
    复制代码

    2)客户端代码

    复制代码
    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Drawing;
    using System.Linq;
    using System.Net;
    using System.Net.Sockets;
    using System.Text;
    using System.Threading;
    using System.Threading.Tasks;
    using System.Windows.Forms;
    
    namespace TcpMsgClient
    {
        public partial class FrmClient : Form
        {
            public FrmClient()
            {
                InitializeComponent();
                //关闭对文本框的非法线程操作检查
                TextBox.CheckForIllegalCrossThreadCalls = false;
            }
            //创建 1个客户端套接字 和1个负责监听服务端请求的线程  
            Socket socketClient = null;
            Thread threadClient = null;
    
            /// <summary>
            /// 连接服务端事件
            /// </summary>
            /// <param name="sender"></param>
            /// <param name="e"></param>
            private void btnListenServer_Click(object sender, EventArgs e)
            {
                //定义一个套字节监听  包含3个参数(IP4寻址协议,流式连接,TCP协议)
                socketClient = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                //需要获取文本框中的IP地址
                IPAddress ipaddress = IPAddress.Parse(this.txtIP.Text.Trim());
                //将获取的ip地址和端口号绑定到网络节点endpoint上
                IPEndPoint endpoint = new IPEndPoint(ipaddress, int.Parse(this.txtPort.Text.Trim()));
                //这里客户端套接字连接到网络节点(服务端)用的方法是Connect 而不是Bind
                try
                {
                    socketClient.Connect(endpoint);
                    this.txtMsg.AppendText("客户端连接服务端成功!" + "
    ");
                    this.btnListenServer.Enabled = false;
                    //创建一个线程 用于监听服务端发来的消息
                    threadClient = new Thread(RecMsg);
                    //将窗体线程设置为与后台同步
                    threadClient.IsBackground = true;
                    //启动线程
                    threadClient.Start();
                }
                catch (Exception ex) {
                    this.txtMsg.AppendText("远程服务端断开,连接失败!" + "
    ");
                }         
            }
    
            /// <summary>
            /// 接收服务端发来信息的方法
            /// </summary>
            private void RecMsg()
            {
                while (true) //持续监听服务端发来的消息
                {
                    try
                    {
                        //定义一个1M的内存缓冲区 用于临时性存储接收到的信息
                        byte[] arrRecMsg = new byte[1024 * 1024];
                        //将客户端套接字接收到的数据存入内存缓冲区, 并获取其长度
                        int length = socketClient.Receive(arrRecMsg);
                        //将套接字获取到的字节数组转换为人可以看懂的字符串
                        string strRecMsg = Encoding.UTF8.GetString(arrRecMsg, 0, length);
                        //将发送的信息追加到聊天内容文本框中
                        txtMsg.AppendText("服务端 " + GetCurrentTime() + "
    " + strRecMsg + "
    ");
                    }
                    catch (Exception ex) {
                        this.txtMsg.AppendText("远程服务器已中断连接!"+"
    ");
                        this.btnListenServer.Enabled = true;
                        break;
                    }
                }
            }
    
            /// <summary>
            /// 发送字符串信息到服务端的方法
            /// </summary>
            /// <param name="sendMsg">发送的字符串信息</param>
            private void ClientSendMsg(string sendMsg)
            {
                try { 
                     //将输入的内容字符串转换为机器可以识别的字节数组
                    byte[] arrClientSendMsg = Encoding.UTF8.GetBytes(sendMsg);
                    //调用客户端套接字发送字节数组
                    socketClient.Send(arrClientSendMsg);
                    //将发送的信息追加到聊天内容文本框中
                    txtMsg.AppendText("天涯 " + GetCurrentTime() + "
    " + sendMsg + "
    ");
                }
                catch(Exception ex){
                    this.txtMsg.AppendText("远程服务器已中断连接,无法发送消息!" + "
    ");
                }
            }
    
            private void btnSendMsg_Click(object sender, EventArgs e)
            {
                //调用ClientSendMsg方法 将文本框中输入的信息发送给服务端
                ClientSendMsg(this.txtClientSendMsg.Text.Trim());
                this.txtClientSendMsg.Clear();
            }
    
            private void txtClientSendMsg_KeyDown(object sender, KeyEventArgs e)
            {
                //当光标位于文本框时 如果用户按下了键盘上的Enter键 
                if (e.KeyCode == Keys.Enter)
                {
                    //则调用客户端向服务端发送信息的方法
                    ClientSendMsg(this.txtClientSendMsg.Text.Trim());
                    this.txtClientSendMsg.Clear();
                }
            }
    
            /// <summary>
            /// 获取当前系统时间的方法
            /// </summary>
            /// <returns>当前时间</returns>
            private DateTime GetCurrentTime()
            {
                DateTime currentTime = new DateTime();
                currentTime = DateTime.Now;
                return currentTime;
            }
        }
    }
    复制代码

    4、程序演示

    1)正常情况

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

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

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

    表示连接服务端成功

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

    2)异常处理

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

    当客户端异常处理

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

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

    服务端异常处理

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

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

  • 相关阅读:
    分布式系统中的限流与熔断
    迷你MVVM框架 avalonjs 0.99发布
    将一个符合URL格式的字符串变成链接
    用CSS开启硬件加速来提高网站性能(转)
    通过window.crypto.getRandomValues获得一个大于零的随机数
    jquery slideDown效果
    五个你必须知道的javascript和web debug技术
    迷你MVVM框架 avalonjs 0.98发布
    使用avalon实现SKU组合查询功能
    angular与avalon对复杂对象的修改
  • 原文地址:https://www.cnblogs.com/kcwang/p/14844950.html
Copyright © 2020-2023  润新知