zoukankan      html  css  js  c++  java
  • C#开发: 通信篇-TCP客户端

    前言

    关于网络通信:

    每一台电脑都有自己的ip地址,每台电脑上的网络应用程序都有自己的通信端口,

    张三的电脑(ip:192.168.1.110)上有一个网络应用程序A(通信端口5000),

    李四的电脑(ip:192.168.1.220)上有一个网络应用程序B(通信端口8000),

    张三给李四发消息,首先你要知道李四的ip地址,向指定的ip(李四ip:192.168.1.220)发信息,

    信息就发到了李四的电脑。

    再指定一下发送的端口号(通信端口8000),信息就发到了李四电脑的网络应用程序B上。

    TCP--一种网络通信方式而已。分为服务器(网络应用程序)和客户端(网络应用程序).

     

    说明

    这节教给大家用C#写一个TCP客户端程序

    页面

    请用户在学习这节之前务必先学完

    C#开发: 通信篇-串口调试助手

    以上文章涉及到的基础知识不再重复赘述

    编写连接程序

    官方文档:

    https://docs.microsoft.com/en-us/dotnet/api/system.net.sockets.socket?view=netframework-4.8      Socket

    https://docs.microsoft.com/en-us/dotnet/api/system.net.sockets.socket.beginconnect?view=netframework-4.8     beginconnect

    1.定义一个 socket

    private Socket MySocket = null;// Socket

     如果报错,按照下面步骤添加Socket对应的包(命名空间)

     

    2.编写点击按钮连接/断开程序

            /// <连接按钮点击事件>
            /// 
            /// </summary>
            /// <param name="sender"></param>
            /// <param name="e"></param>
            private void button1_Click(object sender, EventArgs e)
            {
                if (button1.Text == "连接"){
                    //IP地址 和 端口号输入不为空
                    if (string.IsNullOrEmpty(textBox1.Text) == false && string.IsNullOrEmpty(textBox2.Text) == false){
                        try{
                            IPAddress ipAddress = IPAddress.Parse(textBox1.Text);//获取IP地址
                            int Port = Convert.ToInt32(textBox2.Text);          //获取端口号
    
                            MySocket = new Socket(AddressFamily.InterNetwork,SocketType.Stream, ProtocolType.Tcp);
    
                            //使用 BeginConnect 异步连接
                            MySocket.BeginConnect(ipAddress, Port, new AsyncCallback(ConnectedCallback), MySocket);
                        }
                        catch (Exception){
                            MessageBox.Show("IP地址或端口号错误!", "提示");
                        }
                    }
                    else{
                        MessageBox.Show("IP地址或端口号错误!", "提示");
                    }
                }
                else
                {
                    try{
                        button1.Text = "连接";
                        MySocket.BeginDisconnect(false,null,null);//断开连接
                    }
                    catch (Exception){}
                }
            }
            /// <连接异步回调函数>
            /// 
            /// </summary>
            /// <param name="ar"></param>
            void ConnectedCallback(IAsyncResult ar)
            {
                Socket socket = (Socket)ar.AsyncState;
                try{
                    socket.EndConnect(ar);
                    
                    Invoke((new Action(() =>
                    {
                        textBox3.AppendText("成功连接服务器
    ");//对话框追加显示数据
                        button1.Text = "断开";
                    })));
                }
                catch (Exception e){
                    Invoke((new Action(() =>
                    {
                        textBox3.AppendText("连接失败:" + e.ToString());//对话框追加显示数据
                    })));
                }
            }

    3.测试

    使用电脑调试助手建立一个TCP服务器

    查看一下自己电脑的IP地址

    或者点击控制面板,按照以下路径进入

    关于同步和异步

    就对于上面的连接而言,其实还有一个连接函数

    首先大家应该知道,有些通信需要点时间才能完成

    上面的 Connect函数执行以后就一直等待,直至连接成功才接着往下执行

    Connect 函数就是个同步函数.

    这样会造成程序卡机!如果卡机时间过长,程序就会报错误

    一般把同步函数放到任务里面去执行,这样就避免了卡机

    异步函数也是底层封装的函数,当执行完以后,不会停在那里

    而是接着往下执行,有相应的事件以后才会跳转到回调函数里面去.

    这是异步的好处

    接收数据

    https://docs.microsoft.com/en-us/dotnet/api/system.net.sockets.socket.beginreceive?view=netframework-4.8

    public IAsyncResult BeginReceive (byte[] buffer, int offset, int size, System.Net.Sockets.SocketFlags socketFlags, AsyncCallback callback, object state);

    1.定义一个用于缓存数据的数组

            public const int TCPBufferSize = 1460;//缓存的最大数据个数
            public byte[] TCPBuffer = new byte[TCPBufferSize];//缓存数据的数组

    2.在连接回调函数里面调用异步接收函数

                    //设置异步读取数据,接收的数据缓存到TCPBuffer,接收完成跳转ReadCallback函数
                    socket.BeginReceive(TCPBuffer, 0, TCPBufferSize, 0,new AsyncCallback(ReadCallback), socket);

    3.接收到数据回调函数

            /// <接收到数据回调函数>
            /// 
            /// </summary>
            /// <param name="ar"></param>
            void ReadCallback(IAsyncResult ar)
            {
                Socket socket = (Socket)ar.AsyncState;//获取链接的Socket
                int CanReadLen = socket.EndReceive(ar);//结束异步读取回调,获取读取的数据个数
    
                if (CanReadLen > 0)
                {
                    string str = Encoding.Default.GetString(TCPBuffer,0, CanReadLen);//Byte值根据ASCII码表转为 String
                    Invoke((new Action(() => //C# 3.0以后代替委托的新方法
                    {
                        textBox3.AppendText(str);//对话框追加显示数据
                    })));
    
                    //设置异步读取数据,接收的数据缓存到TCPBuffer,接收完成跳转ReadCallback函数
                    socket.BeginReceive(TCPBuffer,0, TCPBufferSize, 0, new AsyncCallback(ReadCallback), socket);
                }
                else//异常
                {
                    Invoke((new Action(() => //C# 3.0以后代替委托的新方法
                    {
                        button1.Text = "连接";
                        textBox3.AppendText("
    异常断开
    ");//对话框追加显示数据
                    })));
                    try
                    {
                        MySocket.BeginDisconnect(false, null, null);//断开连接
                    }
                    catch (Exception) { }
                }
            }

     注意

    string str = Encoding.Default.GetString(TCPBuffer,0, CanReadLen);//Byte值根据ASCII码表转为 String

    不能直接  Encoding.Default.GetString(TCPBuffer)

    因为实际接收的数据个数为 CanReadLen

    4.测试

    16进制显示数据

    1.关于16进制显示和前面的串口上位机显示16进制数据是一样的道理

    https://www.cnblogs.com/yangfengwu/p/12382103.html   

    /// <字节数组转16进制字符串>
            /// <param name="bytes"></param>
            /// <returns> String 16进制显示形式</returns>
            public static string byteToHexStr(byte[] bytes)
            {
                string returnStr = "";
                try
                {
                    if (bytes != null)
                    {
                        for (int i = 0; i < bytes.Length; i++)
                        {
                            returnStr += bytes[i].ToString("X2");
                            returnStr += " ";//两个16进制用空格隔开,方便看数据
                        }
                    }
                    return returnStr;
                }
                catch (Exception)
                {
                    return returnStr;
                }
            }

    不过需要改一下,需要传进来数据长度,不能使用 bytes.Length

    2.添加处理

                    Invoke((new Action(() => //C# 3.0以后代替委托的新方法
                    {
                        if (checkBox1.Checked)//16进制显示
                        {
                            textBox3.AppendText(byteToHexStr(TCPBuffer, CanReadLen));//对话框追加显示数据
                        }
                        else
                        {
                            textBox3.AppendText(Encoding.Default.GetString(TCPBuffer, 0, CanReadLen));//对话框追加显示数据
                        }
                    })));


    3.测试

    清除接收数据

    textBox3.Clear();

    发送数据

    1.点击发送按钮发送数据

                String Str = textBox4.Text.ToString();//获取发送文本框里面的数据
                try
                {
                    if (Str.Length > 0)
                    {
                        byte[] byteArray = Encoding.Default.GetBytes(Str);//Str 转为 Byte值
                        MySocket.BeginSend(byteArray,0, byteArray.Length,0,null,null); //发送数据
                    }
                }
                catch (Exception) { }

    2.测试

    3.发送16进制数据

    3.1. 用户参考:

     https://www.cnblogs.com/yangfengwu/p/12382103.html

    3.2转换程序如下

    /// <字符串转16进制格式,不够自动前面补零>
            /// 
            /// </summary>
            /// <param name="hexString"></param>
            /// <returns></returns>
            private static byte[] strToToHexByte(String hexString)
            {
                int i;
                hexString = hexString.Replace(" ", "");//清除空格
                if ((hexString.Length % 2) != 0)//奇数个
                {
                    byte[] returnBytes = new byte[(hexString.Length + 1) / 2];
                    try
                    {
                        for (i = 0; i < (hexString.Length - 1) / 2; i++)
                        {
                            returnBytes[i] = Convert.ToByte(hexString.Substring(i * 2, 2), 16);
                        }
                        returnBytes[returnBytes.Length - 1] = Convert.ToByte(hexString.Substring(hexString.Length - 1, 1).PadLeft(2, '0'), 16);
                    }
                    catch
                    {
                        MessageBox.Show("含有非16进制字符", "提示");
                        return null;
                    }
                    return returnBytes;
                }
                else
                {
                    byte[] returnBytes = new byte[(hexString.Length) / 2];
                    try
                    {
                        for (i = 0; i < returnBytes.Length; i++)
                        {
                            returnBytes[i] = Convert.ToByte(hexString.Substring(i * 2, 2), 16);
                        }
                    }
                    catch
                    {
                        MessageBox.Show("含有非16进制字符", "提示");
                        return null;
                    }
                    return returnBytes;
                }
            }

    4.编写发送16进制数据程序

                        if (checkBox2.Checked)//选择16进制发送
                        {
                            byte[] byteHex = strToToHexByte(Str);
                            MySocket.BeginSend(byteHex, 0, byteHex.Length, 0, null, null); //发送数据
                        }
                        else
                        {
                            byte[] byteArray = Encoding.Default.GetBytes(Str);//Str 转为 Byte值
                            MySocket.BeginSend(byteArray, 0, byteArray.Length, 0, null, null); //发送数据
                        }

    5.测试

    6,清除发送

    textBox4.Clear();
  • 相关阅读:
    Android开发之适配器-ListView适配器的重复数据
    Android开发之TextView的下划线添加
    Android 自定义View修炼-Android开发之自定义View开发及实例详解
    Android 开发之自定义Dialog及UI的实现
    Android开发之ContentProvider(内容提供者)
    XC文件管理器(Android应用)
    高效 告别996,开启java高效编程之门 4-1普通码农与风骚码农资源关闭PK
    高效 告别996,开启java高效编程之门 4-2垃圾回收与物理资源释放
    高效 告别996,开启java高效编程之门 3-29实战案例五:排序
    高效 告别996,开启java高效编程之门 3-28实战案例四:分组
  • 原文地址:https://www.cnblogs.com/yangfengwu/p/12397146.html
Copyright © 2011-2022 走看看