用戶端持續傳送訊息

接下來修改上一小節的伺服器端程式碼,讓伺服器能夠持續接收用戶端傳送過來的訊息,同時開啟多執行緒功能。

using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Windows.Forms;

namespace TcpServerDemo
{
    public partial class Form1 : Form
    {
        private TcpListener listener;
        private TcpClient tcpClient;

        public Form1()
        {
            InitializeComponent();
            Control.CheckForIllegalCrossThreadCalls = false;
        }

        private void btnStartServer_Click(object sender, EventArgs e)
        {
            Thread acceptThread = new Thread(() =>
            {
                IPAddress ip = IPAddress.Parse(this.textBoxIP.Text);//建立IP物件
                listener = new TcpListener(ip, Convert.ToInt32(this.textBoxPort.Text));//建立TCP監聽執行個體
                listener.Start();
                this.textBoxInfo.Text = "伺服器啟動‑" + DateTime.Now.ToShortTimeString() + "\r\n" + this.textBoxInfo.Text;
                tcpClient = listener.AcceptTcpClient();//封鎖等待用戶端連線
                this.textBoxInfo.Text = "連線成功‑" + DateTime.Now.ToShortTimeString() + "\r\n" + this.textBoxInfo.Text;

                NetworkStream stream = tcpClient.GetStream();
                byte[] byteArray = new byte[1024];
                while (true)
                {
                    int length = stream.Read(byteArray, 0, 1024);//將串流資料讀取至byteArray緩衝區
                    //length代表用戶端實際傳送的位元組長度
                    string receiveMessage = Encoding.Unicode.GetString(byteArray, 0, length);
                    this.textBoxInfo.Text = "收到訊息:" + receiveMessage + "-" +
                        DateTime.Now.ToShortTimeString() + "\r\n" + this.textBoxInfo.Text;
                }
            });
            acceptThread.IsBackground = true;
            acceptThread.Start();
        }
    }
}Code language: C# (cs)

採用 WinForms + TcpClient / TcpListener 實作:
用戶端可持續輸入自訂文字傳送至伺服器,輸入【離開】就中斷連線並結束程式

本範例使用簡易方式處理多執行緒造成的UI凍結問題,但標準寫法應如下所述:
Control.CheckForIllegalCrossThreadCalls = false; 屬於不建議使用的強行寫法,正式專案建議使用 Invoke/BeginInvoke 完成跨執行緒更新控制項。

一、伺服器端

using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Windows.Forms;

namespace TcpServerDemo
{
    public partial class Form1 : Form
    {
        private TcpListener listener;
        private TcpClient tcpClient;

        public Form1()
        {
            InitializeComponent();
            // 僅供學習演示,正式專案不建議使用
            Control.CheckForIllegalCrossThreadCalls = false;
        }

        private void btnStartServer_Click(object sender, EventArgs e)
        {
            // 開啟新執行緒,避免UI介面卡住
            new Thread(() =>
            {
                IPAddress ip = IPAddress.Parse(textBoxIP.Text);
                listener = new TcpListener(ip, Convert.ToInt32(textBoxPort.Text));
                listener.Start();
                AppendLog($"伺服器啟動‑{DateTime.Now.ToShortTimeString()}");

                tcpClient = listener.AcceptTcpClient();
                AppendLog($"用戶端連線成功‑{DateTime.Now.ToShortTimeString()}");

                NetworkStream stream = tcpClient.GetStream();
                byte[] byteArray = new byte[1024];
                while (true)
                {
                    int length = stream.Read(byteArray, 0, 1024);
                    if (length <= 0)
                    {
                        AppendLog("用戶端已斷開連線");
                        break;
                    }
                    string receiveMessage = Encoding.Unicode.GetString(byteArray, 0, length);
                    AppendLog($"收到訊息:{receiveMessage}{DateTime.Now.ToShortTimeString()}");
                }
                tcpClient.Close();
                listener.Stop();
            }).Start();
        }

        // 把日誌附加到文字方塊
        private void AppendLog(string msg)
        {
            textBoxInfo.Text = msg + "\r\n" + textBoxInfo.Text;
        }
    }
}Code language: C# (cs)

二、用戶端程式碼

實作功能:可連續輸入,輸入「離開」就結束通訊

用戶端介面建議配置以下元件:

  • textBoxServerIp:伺服器IP位址
  • textBoxPort:通訊連接埠
  • textBoxSend:輸入待傳送訊息
  • btnConnect:連線按鈕
  • btnSend:傳送按鈕
using System;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Windows.Forms;

namespace TcpClientDemo
{
    public partial class ClientForm : Form
    {
        private TcpClient client;
        private NetworkStream stream;
        private bool isConnected = false;

        public ClientForm()
        {
            InitializeComponent();
            Control.CheckForIllegalCrossThreadCalls = false;
        }

        // 連接伺服器
        private void btnConnect_Click(object sender, EventArgs e)
        {
            try
            {
                client = new TcpClient();
                client.Connect(textBoxServerIp.Text, Convert.ToInt32(textBoxPort.Text));
                stream = client.GetStream();
                isConnected = true;
                MessageBox.Show("連線成功,可以開始傳送訊息!");
            }
            catch (Exception ex)
            {
                MessageBox.Show("連線失敗:" + ex.Message);
            }
        }

        // 傳送訊息主要邏輯
        private void btnSend_Click(object sender, EventArgs e)
        {
            if (!isConnected)
            {
                MessageBox.Show("請先連接伺服器!");
                return;
            }
            string content = textBoxSend.Text.Trim();
            if (string.IsNullOrEmpty(content))
                return;

            // 判斷是否觸發離開指令
            if (content == "離開")
            {
                byte[] exitData = Encoding.Unicode.GetBytes(content);
                stream.Write(exitData, 0, exitData.Length);

                // 釋放資源
                stream.Close();
                client.Close();
                isConnected = false;
                MessageBox.Show("已斷開連線");
                return;
            }

            // 傳送一般自訂內容
            byte[] data = Encoding.Unicode.GetBytes(content);
            stream.Write(data, 0, data.Length);
            textBoxSend.Clear();
        }
    }
}Code language: C# (cs)
static void Main(string[] args)
{
    TcpClient client = new TcpClient();
    client.Connect("127.0.0.1", 8899);
    NetworkStream stream = client.GetStream();
    while (true)
    {
        Console.Write("請輸入要傳送的內容:");
        string msg = Console.ReadLine();
        byte[] buf = Encoding.Unicode.GetBytes(msg);
        stream.Write(buf,0,buf.Length);
        if (msg == "離開")
        {
            stream.Close();
            client.Close();
            break;
        }
    }
}Code language: JavaScript (javascript)

如果你有需要,我可以把整套程式改寫成純非同步 async/await 寫法,完全捨棄子執行緒加上關閉跨執行緒檢查這種不規範的實作方式。

用戶端持續傳送訊息

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *