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("開始監聽客戶端傳來的信息!" + "\r\n");
                this.btnServerConn.Enabled = false;
            }
            catch (Exception ex) {
                txtMsg.AppendText("服務端啟動服務失敗!" + "\r\n");
                this.btnServerConn.Enabled = true;
            }
        }

        //創建一個負責和客戶端通信的套接字 
        Socket socConnection = null;

        /// <summary>
        /// 監聽客戶端發來的請求
        /// </summary>
        private void WatchConnecting()
        {
            while (true)  //持續不斷監聽客戶端發來的請求
            {
                socConnection = socketWatch.Accept();
                txtMsg.AppendText("客戶端連接成功! " + "\r\n");
                //創建一個通信線程 
                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() + "\r\n" + sendMsg + "\r\n");
            }
            catch (Exception ex) {
                txtMsg.AppendText("客戶端已斷開連接,無法發送信息!" + "\r\n");
            }
        }

        /// <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() + "\r\n" + strSRecMsg + "\r\n");
                }
                catch (Exception ex) {
                    txtMsg.AppendText("客戶端已斷開連接!" + "\r\n");
                    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("客戶端連接服務端成功!" + "\r\n");
                this.btnListenServer.Enabled = false;
                //創建一個線程 用於監聽服務端發來的消息
                threadClient = new Thread(RecMsg);
                //將窗體線程設置為與后台同步
                threadClient.IsBackground = true;
                //啟動線程
                threadClient.Start();
            }
            catch (Exception ex) {
                this.txtMsg.AppendText("遠程服務端斷開,連接失敗!" + "\r\n");
            }         
        }

        /// <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() + "\r\n" + strRecMsg + "\r\n");
                }
                catch (Exception ex) {
                    this.txtMsg.AppendText("遠程服務器已中斷連接!"+"\r\n");
                    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() + "\r\n" + sendMsg + "\r\n");
            }
            catch(Exception ex){
                this.txtMsg.AppendText("遠程服務器已中斷連接,無法發送消息!" + "\r\n");
            }
        }

        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)異常處理

原先參考的源碼未做異常處理,導致退出程序報錯,提示未友好等

當客戶端異常處理

服務端中斷開連接,自動提示

服務端中斷連接,無法發送消息

服務端異常處理

客戶端斷開連接,自動提示

客戶端斷開連接,無法發送消息


免責聲明!

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



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