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("成功連接服務器\n");//對話框追加顯示數據
                    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("\n異常斷開\n");//對話框追加顯示數據
                })));
                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();

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM