登山-C#-關於TcpListener的AcceptTcpClient()方法造成執行緒阻塞,進而程式無法徹底關閉的問題
阿新 • • 發佈:2019-02-11
在《C#高階程式設計》第7版第24章,有提到使用TCP類。
書中寫了一個例項,兩個winform,其中一個點選按鈕傳送字串,另一個winform進行接收。這個例項有個缺點,只能接收一次。
我將這個例項進行了改造。第一版做好後,可以進行接收和傳送,但是出現一個問題,就是在關閉程式後,在電腦的工作管理員中看到還有程序在跑。
進行了一些嘗試後改了第二版,終於解決了這個問題。
看一眼這個程式
在兩臺電腦上分別執行此程式,注意要設定對方的IP地址。
我直接貼上第二版的程式碼,然後在標明修改的哪兒。
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms; using System.IO; using System.Net; using System.Net.Sockets; using System.Threading; namespace TCPSend { public partial class Form1 : Form { //定義一個委託,用於更新Form1上控制元件。 protected delegate void UpdateDisplayDelegate(string text); public Thread thread = null; public TcpClient tcpClientReceiver = null; TcpListener tcpListener = null; public Boolean boolStop = false; public Form1() { InitializeComponent(); thread = new Thread(new ThreadStart(Listen)); thread.Start(); } public void Listen() { string LocalIp = GetSelfIp(); if (LocalIp == null) { return; } IPAddress localAddr = IPAddress.Parse(LocalIp); Int32 port = 2112; tcpListener = new TcpListener(localAddr, port); tcpClientReceiver = new TcpClient(); tcpListener.Start(); while (true) { if (!tcpListener.Pending()) { //為了避免每次都被tcpListener.AcceptTcpClient()阻塞執行緒,添加了此判斷, //no connection requests have arrived。 //當沒有連線請求時,什麼也不做,有了請求再執行到tcpListener.AcceptTcpClient() } else { tcpClientReceiver = tcpListener.AcceptTcpClient(); NetworkStream ns = tcpClientReceiver.GetStream(); StreamReader sr = new StreamReader(ns); string result = sr.ReadToEnd(); Invoke(new UpdateDisplayDelegate(UpdateDisplay), new object[] { result }); } if (boolStop) { break; } } } public void UpdateDisplay(string text) { string currentContents = textBox4.Text; currentContents += text+"\r\n"; //必須用"\r\n"在視窗中才能體現出換行 textBox4.Text = currentContents; } //send message private void button1_Click(object sender, EventArgs e) { SendMessage(); } public void SendMessage() { TcpClient tcpClient = new TcpClient(textBox1.Text, Int32.Parse(textBox2.Text)); NetworkStream ns = tcpClient.GetStream(); string message = textBox3.Text; byte[] contentBytes = Encoding.GetEncoding("utf-8").GetBytes(message); //將string型別轉換為byte[] for (int i = 0; i < contentBytes.Length; i++) { ns.WriteByte(contentBytes[i]); } ns.Close(); tcpClient.Close(); textBox3.Text = ""; } //獲得本地的IP地址 public string GetSelfIp() { System.Net.IPAddress[] addressList = Dns.GetHostByName(Dns.GetHostName()).AddressList; if (addressList.Length == 1) { return addressList[0].ToString(); } else { MessageBox.Show("當前只支援設定一個IP的電腦,您的電腦設有多個IP地址"); } return null; } //在關閉之前,將boolStop設定為true,thread既可以結束了。 private void Form1_FormClosing(object sender, FormClosingEventArgs e) { boolStop = true; } } }
相對於第一版,主要是添加了變數boolStop,用於控制執行緒中while迴圈結束的時機。第二點就是在while迴圈中增加了一個判斷,if (!tcpListener.Pending()),這樣在對方沒有傳送訊息時,是不會執行到tcpListener.AcceptTcpClient();的。這樣就不會造成執行緒的阻塞了。這樣直接關閉了winform,執行緒thread也會相應的結束。
否則就會造成如下的情況,關閉了程式,但是工作管理員中,仍然能夠看到程序。
這個程式還有很多地方可以改進,後續再寫。