本例中,JT-8290A讀寫器(其資料在百度雲分享中CSharp>捷通開發包目錄下)鏈接到本地路由器以後,可訪問讀寫器的ip地址進行簡單配置(相似路由器第一次設置),主要是將其「服務器地址」參數設置爲實驗的電腦ip地址便可(本機是服務器,讀寫器是客戶,客戶能夠有多個。服務器和客戶必須在同一個子網才能夠通訊)。
數據庫
因此Socket通訊代碼在(http://my.oschina.net/SnifferApache/blog/406563)基礎上修改~~編程
官方文檔中有以下描述:
c#
若是應用程序在執行期間只須要一個線程,請使用下面的方法,這些方法適用於同步操做模式。數組
若是當前使用的是面向鏈接的協議(如 TCP),則服務器可使用 Listen 方法偵聽鏈接。 Accept 方法處理任何傳入的鏈接請求,並返回可用於與遠程主機進行數據通訊的 Socket。 可使用此返回的 Socket 來調用 Send 或 Receive 方法。 若是要指定本地 IP 地址和端口號,請在調用 Listen 方法以前先調用Bind 方法。 若是您但願基礎服務提供程序爲您分配可用端口,請使用端口號 0。 若是但願鏈接到偵聽主機,請調用 Connect 方法。 若要進行數據通訊,請調用 Send 或 Receive 方法。服務器
若是當前使用的是無鏈接協議(如 UDP),則根本不須要偵聽鏈接。 調用 ReceiveFrom 方法可接受任何傳入的數據報。 使用 SendTo 方法可將數據報發送到遠程主機。socket
在本例中Socket通訊流程以下:ide
①服務器端新建Socket實例server_socket(同時綁定指定的IPEndPoint,將server_socket置於偵聽狀態);this
②主程序啓動線程server_thread處理任何傳入的鏈接請求,一旦成功(好比鏈接到客戶R),其返回的Socket實例connSocket即可以看做服務器與客戶R之間的橋樑。spa
③客戶R要作的事情比較簡單,由於只有一個服務器(不用關心RFID讀寫器作了什麼)。那服務器怎麼區分這麼多的客戶呢?接收和發送可能搞混?.net
那咱們就爲每個客戶單獨拋出一個線程來接收消息,發送的時候選擇指定的客戶connSocket就好啦。
好比客戶R的ip地址爲key,該connSocket爲value,添加到Dictionary<string,Socket>實例中,那就能夠方便的操做啦。
廢話不說,上源碼……
C#中Socket編程須要以下名稱空間
using System.Net;
using System.Net.Sockets;
using System.Threading;
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Forms; using System.Diagnostics; using System.Net; using System.Net.Sockets; using System.Threading; using System.Runtime.Serialization.Formatters.Binary; namespace Ex02_wifiServer { public partial class MainForm : Form { private static int server_port = 8899; private static string server_ip = "192.168.3.68"; private static int buffer_size = 1024; private static string data = null; private static byte[] receiveBytes = new byte[buffer_size]; Dictionary<string, Socket> dicSocket = new Dictionary<string, Socket>(); Dictionary<string, Thread> dicThread = new Dictionary<string, Thread>(); //添加指令列表 Dictionary<string, string> cmds = new Dictionary<string, string>(); public MainForm() { InitializeComponent(); this.MaximumSize = this.Size; this.MinimumSize = this.Size; Control.CheckForIllegalCrossThreadCalls = false; label1.Text = "在線讀寫器數:" + Convert.ToString(cb_readerList.Items.Count); cb_readerList.Text = "全部讀寫器"; data = "等待用戶鏈接……"; richTextBox1.AppendText(data); richTextBox1.Focus(); } private void MainForm_Load(object sender, EventArgs e) { //爲cb_cmd添加指令 cmds.Add("設備識別", "A0 03 82 00 DB"); cmds.Add("復位讀頭", "A0 03 65 00 F8"); cmds.Add("中止工做", "A0 03 50 00 0D"); cmds.Add("關閉繼電器", "A0 04 B1 00 00 AB"); cmds.Add("打開繼電器", "A0 04 B1 00 01 AB"); foreach (var i in cmds) { this.cb_cmd.Items.Add(i.Key); } //定義線程開始 Socket server_socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); IPAddress ipadd = IPAddress.Parse(server_ip); IPEndPoint ipe = new IPEndPoint(ipadd, server_port); try { server_socket.Bind(ipe); server_socket.Listen(100); } catch (Exception ee) { MessageBox.Show(ee.Message); return; } Thread a = new Thread(Run); a.IsBackground = true; a.Start(server_socket); } int count = 0; private void Run(object o) { Socket socket = o as Socket; while (count < 100) { try { count++; Debug.WriteLine("客戶鏈接的次數:" + count); //建立一個負責通訊用的socket 阻塞窗體的運行 Socket connSocket = socket.Accept(); string s = connSocket.RemoteEndPoint.ToString(); data = "\n" + s + "已鏈接!"; richTextBox1.AppendText(data); richTextBox1.Focus(); //ShowMsg(s + ":鏈接成功"); //記錄通訊用的socket dicSocket.Add(s, connSocket); cb_readerList.Items.Add(s); label1.Text = "在線讀寫器數:" + Convert.ToString(cb_readerList.Items.Count); //接收消息 Thread th = new Thread(RecMsg); th.IsBackground = true; th.Start(connSocket); dicThread.Add(s, th); } catch (Exception ex) { MessageBox.Show(ex.Message); break; } } } void RecMsg(object o) { Socket connSocket = o as Socket; while (true) { try { //接收客戶端發送過來的消息 byte[] buffer = new byte[1024 * 1024]; //num 接收到的實際有效的字節個數 int num = connSocket.Receive(buffer); byte[] buff = new byte[num]; string str = ""; for (int i = 0; i < num; i++) { buff[i] = buffer[i]; } foreach (byte item in buff) //讀取Buff中存的數據,轉換成顯 示的十六進制數 { str += item.ToString("X2") + " "; } //string s = Encoding.Default.GetString(buffer, 0, num); if (str != "!!!I want to close!!!") { data = "\n" + connSocket.RemoteEndPoint.ToString() + "返回:" + str; richTextBox1.AppendText(data); richTextBox1.Focus(); } else { string m = connSocket.RemoteEndPoint.ToString(); MessageBox.Show(m + "已斷開"); dicSocket.Remove(str); cb_readerList.Items.Remove(m); cb_readerList.Text = "全部讀寫器"; label1.Text = "在線讀寫器數:" + Convert.ToString(cb_readerList.Items.Count); } } catch { //MessageBox.Show(ex.Message); connSocket.Close(); break; } } } //重寫關閉窗體程序 protected override void OnClosing(CancelEventArgs e) { Environment.Exit(0); e.Cancel = true; } private void btn_send_Click(object sender, EventArgs e) { try { string s = cb_readerList.Text; Debug.WriteLine("指令:" + cb_cmd.Text); byte[] buff = new byte[20]; string[] str = tb_cmd.Text.Split(' '); //將byte數組轉化爲16進制 int k = 0; foreach (string item in str) { if (item.Trim() != "") { buff[k++] = byte.Parse(item, System.Globalization.NumberStyles.HexNumber); } } Debug.WriteLine("buff的長度:" + k); //byte[] sendBytes = Encoding.Default.GetBytes(sendStr); if (s != "全部讀寫器") { dicSocket[s].Send(buff, buff.Length, SocketFlags.None);//向客戶端發送信息 data = "\n" + "服務器對" + s + "發送:" + tb_cmd.Text + " " + buff[k].ToString("X2"); Debug.WriteLine("data: " + data); richTextBox1.AppendText(data); richTextBox1.Focus(); } else if (cb_readerList.Items.Count == 0) MessageBox.Show("沒有讀寫器鏈接到!"); else { foreach (string i in cb_readerList.Items) dicSocket[cb_readerList.GetItemText(i)].Send(buff, k, 0); data = "\n" + "服務器對全部讀寫器發送:" + tb_cmd.Text; richTextBox1.AppendText(data); richTextBox1.Focus(); } } catch (Exception ex) { MessageBox.Show(ex.Message); } } private void cb_cmd_SelectedIndexChanged(object sender, EventArgs e) { try { this.tb_cmd.Text = cmds[this.cb_cmd.SelectedItem.ToString()]; } catch (Exception ex) { Debug.WriteLine("下拉列表異常:" + ex); } } } }
①綁定下拉列表和TextBox也是使用Dictionary<T1,T2>泛型類;
②Server線程運行Run()方法到Accept()處會等待客戶請求,新用戶鏈接以後及時更新用戶下拉列表;
③用戶看到的和數據庫存儲的是16進制數字,每一個字節兩個16進制數字,字節之間空格隔開,發送以前要轉成byte數組,byte數組中每一個元素有8bit,恰好容納一個字節,也就是2個16進制數字。
通訊協議採用奇偶校驗,目前用到的指令最後一個字節就是校驗值,不須要再計算校驗結果。因此沒有實現「添加校驗」按鈕的功能。