先加入 manuStrip
using System.Net; // 網路通訊協定
using System.Net.Sockets; // 網路插座功能
using System.Threading; // 多執行緒
1. 設計一個Thread class
public class UDPThread
{
ManualResetEvent m_shutdownEvent = new ManualResetEvent(false);
ManualResetEvent m_pauseEvent = new ManualResetEvent(true);
Thread m_thread;
int m_cnt = 0;
public UDPThread() { }
public void Job()
{
while(true)
{
m_pauseEvent.WaitOne(Timeout.Infinite);
if (m_shutdownEvent.WaitOne(0))
break;
m_cnt++;
}
}
public void Start()
{
m_thread = new Thread(Job);
m_thread.Start();
}
public void Pause()
{
m_pauseEvent.Reset();
}
public void Resume()
{
m_pauseEvent.Set();
}
public void Stop()
{
m_shutdownEvent.Set();
m_pauseEvent.Set();
m_thread.Join();
}
}
2. 執行緒中如何更新UI?
2.1 加入Label設定/取值功能
public Label CntLabel
{
set;
get;
}
2.2 加入更新UpdateUI
public void UpdateUI()
{
if (CntLabel.InvokeRequired)
{
CntLabel.BeginInvoke( (Action)( () => { UpdateUI(); }) );
}
else
{
CntLabel.Text = m_cnt.ToString();
}
}
2.3 UI加入對應事件
private void 開始ToolStripMenuItem_Click(object sender, EventArgs e)
{
udp1= new UDPThread();
udp1.CntLabel = label1;
udp1.Start();
}
private void 暫停ToolStripMenuItem_Click(object sender, EventArgs e)
{
udp1.Pause();
}
private void 繼續ToolStripMenuItem_Click(object sender, EventArgs e)
{
udp1.Resume();
}
private void 停止ToolStripMenuItem_Click(object sender, EventArgs e)
{
udp1.Stop();
}
private void 開始ToolStripMenuItem1_Click(object sender, EventArgs e)
{
udp2 = new UDPThread();
udp2.CntLabel = label2;
udp2.Start();
}
private void 暫停ToolStripMenuItem1_Click(object sender, EventArgs e)
{
udp2.Pause();
}
private void 繼續ToolStripMenuItem1_Click(object sender, EventArgs e)
{
udp2.Resume();
}
private void 停止ToolStripMenuItem1_Click(object sender, EventArgs e)
{
udp2.Stop();
}
啟動執行緒1和執行緒2後, 畫面如下:
2.4 在Form Closing加入防呆
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
if (udp1 != null) udp1.Stop();
if (udp2 != null) udp2.Stop();
}
=================================================================
3. UI加入IP和PORT設定,
3.1 新增IP和Port的存取
修改開始執行緒1
同時, 修改開始執行緒2
private void 開始ToolStripMenuItem1_Click(object sender, EventArgs e)
{
udp2 = new UDPThread();
udp2.IP = textBox1.Text;
udp2.Port = Convert.ToInt16(numericUpDown2.Value);
udp2.CntLabel = label2;
udp2.Start();
}
準備加入監聽InitUDP()和接收資料RecvData()功能
3.2 UDP初始化
private void InitUDP()
{
if (m_udp == null)
if (JobType == JOBTYPE.RECVDTA)
m_udp = new UdpClient(Port); // 創建一個UDP實體
else
m_udp = new UdpClient();
if (m_IPEP == null) m_IPEP = new IPEndPoint(IPAddress.Parse(IP), Port); // 創建一個連線插座
}
3.3 接收資料RecvData()
public void RecvData()
{
try
{
if (m_udp.Available > 0) // 很重要, 否則會被咬住
{
if(CntLabel.InvokeRequired)
{
CntLabel.BeginInvoke( (Action)(() => { RecvData(); }) );
}else{
byte[] B = m_udp.Receive(ref m_IPEP);
CntLabel.Text = Encoding.Default.GetString(B);
}
}
}
catch (Exception e)
{
//MessageBox.Show(e.Message);
}
}
3.4 傳送資料SendData()
public void SendData()
{
try
{
if (CntLabel.InvokeRequired)
{
CntLabel.BeginInvoke((Action)(() => { SendData(); }));
}
else
{
string str = m_cnt.ToString();
byte[] B = Encoding.Default.GetBytes(str);
m_udp.Send(B, B.Length, IP, Port);
CntLabel.Text = str;
}
}
catch (Exception e)
{
//MessageBox.Show(e.Message);
}
}
3.5 判定該執行緒執行SendData()或是RecvData()
public enum JOBTYPE { SENDDATA, RECVDTA }; // 傳送, 接收
3.6 修改Job內容
public void Job()
{
InitUDP();
while(true)
{
m_pauseEvent.WaitOne(Timeout.Infinite);
if (m_shutdownEvent.WaitOne(0))
break;
m_cnt++;
if (JobType == JOBTYPE.RECVDTA)
{
RecvData();
//UpdateUI();
}
else
{
SendData();
}
Thread.Sleep(m_timeSleep);
}
}
3.7 修改開始執行緒1
private void 開始ToolStripMenuItem_Click(object sender, EventArgs e)
{
udp1= new UDPThread();
udp1.IP = textBox1.Text;
udp1.Port = Convert.ToInt16(numericUpDown1.Value);
udp1.CntLabel = label1;
udp1.JobType = JOBTYPE.RECVDTA;
udp1.Start();
}
3.7 修改開始執行緒2
private void 開始ToolStripMenuItem1_Click(object sender, EventArgs e)
{
udp2 = new UDPThread();
udp2.IP = textBox1.Text;
udp2.Port = Convert.ToInt16(numericUpDown1.Value);
udp2.CntLabel = label2;
udp2.JobType = JOBTYPE.SENDDATA;
udp2.Start();
}
=================================================================
3.8 測試結果:
執行緒1: 負責接收
執行緒2: 負責傳送
執行緒1如果臨時暫停, 執行緒2送來的資料還是會保留在bufffer區,
等執行緒1繼續執行, 資料會陸續取出
=================================================================
參考資料
1. C# 如何創建, 暫停, 繼續, 終止一個執行緒(Thread)
2. Visual C# 2012 網路程式設計 線上遊戲實作
留言列表