カテゴリー: 何故かC#
C#で受信スレッドを使ったTCP/IP受信オブジェクト
久しぶりにC#でTCP/IPの通信オブジェクトを作成することになった。
クライアントから数値が飛んでくるので、それを受信したらイベントで渡すプログラム。
メインはWindowsフォームで、内部は2段スレッド処理。
イベントは別スレッドになるので、ちょっとてこずったがこんなもんかいという感じ。
もう少しいい方法があったら誰かおしえてちょ!
接続待ちスレッドクラス
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Net.Sockets; using System.Net; using System.Threading; namespace TcpClass { public class TcpReceiveEventArgs : EventArgs { public TcpReceiveEventArgs(long Temperature) { this.Temperature = Temperature; } private long temperature; public long Temperature { get { return temperature; } set { temperature = value; } } } public delegate void TcpReceiveEventHandler(long temperature); public class TcpReceive { public event TcpReceiveEventHandler onTcpReceive; private int portNo; // スレッド停止命令用 private bool stop_flg = false; public TcpReceive(int portNo) { this.portNo = portNo; // 接続待ちのスレッドを開始 Thread thread = new Thread(new ThreadStart(ListenStart)); // スレッドをスタート thread.Start(); } public void Dispose() { stop_flg = true; GC.SuppressFinalize(this); } /** * 接続待ちスレッド本体 */ private void ListenStart() { // Listenerの生成 TcpListener listener = new TcpListener(IPAddress.Any, this.portNo); // 接続要求受け入れ開始 listener.Start(); while (!stop_flg) { // 接続待ちがあるか? if (listener.Pending() == true) { // 接続要求を受け入れる TcpClient tcp = listener.AcceptTcpClient(); TcpReceiveWorker rcv = new TcpReceiveWorker(tcp, this); Thread thread = new Thread(new ThreadStart(rcv.TCPClientProc)); // スレッドをスタート thread.Start(); } else { Thread.Sleep(0); } } // 接続待ち終了 listener.Stop(); } protected virtual void OnReceiveEvent(long temperature) { if (onTcpReceive != null) { onTcpReceive(temperature); } } internal void RiaseEvent(long temperature) { OnReceiveEvent(temperature); } } }
データ受信スレッドクラス
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Net.Sockets; using System.Net; using System.Threading; using System.Collections; namespace TcpClass { class TcpReceiveWorker { private TcpClient tcp = null; TcpReceive rcv = null; public TcpReceiveWorker(TcpClient tcp,TcpReceive rcv) { this.tcp = tcp; this.rcv = rcv; } public void TCPClientProc() { NetworkStream st = tcp.GetStream(); ArrayList aryList = new ArrayList(); long count = 0; do { int nret = st.ReadByte(); if (nret < '0' || nret > '9') { break; } if (nret != -1) { aryList.Add((byte)nret); } count++; if (count > 8 ) break; } while (tcp.Connected == true); // ソケットを閉じる tcp.Close(); // 受信したものをbyte配列に変換 byte[] byt = new byte[aryList.Count]; for (int i = 0; i < aryList.Count; i++) { byt[i] = (byte)aryList[i]; } string strFromByte = Encoding.ASCII.GetString(byt); long ret = long.Parse(strFromByte); rcv.RiaseEvent(ret); } } }
データ送信クラス
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Net; using System.Net.Sockets; namespace TcpClass { public class TcpSend { public string hostName; public int portNo; public TcpSend(string hostName,int portNo) { this.hostName = hostName; this.portNo = portNo; } public Boolean send(long value) { try { // クライアント用のソケットを作成する TcpClient cl = new TcpClient(); // サーバーへ接続する cl.Connect(hostName, portNo); // 接続したソケットからNetworkStreamを取得 NetworkStream stream = cl.GetStream(); Encoding encode = Encoding.Default; // 送信する文字列をバイト配列に変換 // この際に、エンコードも同時に行う。 string s = value.ToString(); byte[] bytData = encode.GetBytes(s); // 書き出しを行う。 stream.Write(bytData, 0, bytData.Length); // フラッシュ(強制書き出し) // これを行わないと、確実にネットワークに流れない。 stream.Flush(); // ソケットをクローズ cl.Close(); } catch (SocketException eSocket) { System.Diagnostics.Debug.Write(eSocket.Message); return false; } catch (Exception ex) { System.Diagnostics.Debug.Write(ex.Message); return false; } return true; } } }
イベントがクラスなのでフォームのデータを変更するときに注意が必要なんでこんな感じ
void c_onTcpReceive(object sender, TcpReceiveEventArgs eventCode) { CheckForIllegalCrossThreadCalls = false; /* 別スレッドでのアクセスを許可する */ this.textTemperature.Text += eventCode.Temperature; this.textTemperature.Text += "\n"; }
実はイベントを受け取るとき別スレッドで受け取るため下のやり方が正しい かなりややこしいが、昔ならウィンドウメッセージを使っていたから、この方が簡単化もしれない。でもややこしい!
private void button1_Click(object sender, EventArgs e) { c = new TcpReceive(10000); c.onTcpReceive += new TcpReceiveEventHandler(c_onTcpReceive); } void c_onTcpReceive(long temperature) { if (textTemperature.InvokeRequired) { Invoke(new delSetTemperature(setTemperature), new object[] { temperature }); } else { setTemperature(temperature); } } delegate void delSetTemperature(long temperature); void setTemperature(long temperature) { textTemperature.Text += temperature; textTemperature.Text += "\n"; }