C#Winform實時更新數據庫信息Demo(使用Scoket)
最近在貼吧上看到有個提問就是關於怎麽在Winform上實時的更新數據
提問者提到的是利用Timer去輪詢,但最後經過網上查了下資料,感覺Socket也是可行的,
於是就寫了這個Demo
這個Demo的思路很簡單:
有一個Socket服務端,只負責接收多個客戶端傳過來的訊息,根據訊息內容去判斷是否廣播
這裏每一個winform窗體程序就是一個Socket客戶端,如果窗體上對數據庫做了更新(例如增,刪,改)操作
就會調用一個方法,該方法主要是向Socket服務端發送一個字符串"1"
當Socket服務端接收到了字符串為"1"時,則廣播給所有客戶端一個字符串"1"
而當客戶端接收到服務端傳過來一個"1"時,則立即執行數據綁定的方法(重新將界面的DataGridVeiw數據綁定)
這樣就實現了一有數據改變就實時刷新的效果
下面貼出各部分代碼
================服務端====================================================
服務端的界面,比較簡單
public partial class Server : Form { public Server() { InitializeComponent(); } /// <summary> /// 已連接上的客戶端集合 /// </summary>List<Socket> clinetSockets; /// <summary> /// 服務端主Socket /// </summary> Socket socket; /// <summary> /// 設置數據緩沖區 /// </summary> private byte[] result = new byte[1024]; /// <summary> /// 開啟偵聽按鈕點擊事件/// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void button1_Click(object sender, EventArgs e) { //初始化 clinetSockets = new List<Socket>(); //創建socket對象 socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); //獲取ip地址和端口(其實應該把它放在配置文件中,客戶端的ip和port都放在配置文件中) IPAddress ip = IPAddress.Parse(txtIPAddress.Text.Trim()); int port = Convert.ToInt32(txtPort.Text.Trim()); IPEndPoint point = new IPEndPoint(ip, port); //綁定ip和端口 socket.Bind(point); //設置最大連接數 socket.Listen(10); listBox1.Items.Add("服務器已開啟,等待客戶端連接中....."); //開啟新線程監聽 Thread serverThread = new Thread(ListenClientConnect); serverThread.IsBackground = true; serverThread.Start(socket); } /// <summary> /// 監聽傳入 /// </summary> /// <param name="ar"></param> private void ListenClientConnect(object ar) { //設置標誌 bool flag = true; //獲得服務器的Socket Socket serverSocket = ar as Socket; //輪詢 while (flag) { //獲得連入的客戶端socket Socket clientSocket = serverSocket.Accept(); //將新加入的客戶端加入列表中 clinetSockets.Add(clientSocket); //向listbox中寫入消息 listBox1.Invoke(new Action(() => { listBox1.Items.Add(string.Format("客戶端{0}已成功連接到服務器\r\n", clientSocket.RemoteEndPoint)); })); //開啟新的線程,進行監聽客戶端消息 var mReveiveThread = new Thread(ReceiveClient); mReveiveThread.IsBackground = true; mReveiveThread.Start(clientSocket); } } /// <summary> /// 接收客戶端傳過來的數據 /// </summary> /// <param name="obj"></param> private void ReceiveClient(object obj) { //獲取當前客戶端 //因為每次發送消息的可能並不是同一個客戶端,所以需要使用var來實例化一個新的對象 //可是我感覺這裏用局部變量更好一點 var mClientSocket = (Socket)obj; // 循環標誌位 bool flag = true; while (flag) { try { //獲取數據長度 int receiveLength = mClientSocket.Receive(result); //獲取客戶端消息 string clientMessage = Encoding.UTF8.GetString(result, 0, receiveLength); //服務端負責將客戶端的消息分發給各個客戶端 //判斷客戶端發來的消息是否是預定的標誌 if (clientMessage=="1") { //通知各客戶端 this.SendMessage("1"); } //向listbox中寫入消息 listBox1.Invoke(new Action(() => { listBox1.Items.Add(string.Format("客戶端{0}發來消息{1}", mClientSocket.RemoteEndPoint, clientMessage)); })); } catch(Exception e) { //從客戶端列表中移除該客戶端 clinetSockets.Remove(mClientSocket); //顯示客戶端下線消息 listBox1.Invoke(new Action(() => { listBox1.Items.Add(string.Format("服務器發來消息:客戶端{0}從服務器斷開,斷開原因:{1}\r\n", mClientSocket.RemoteEndPoint, e.Message)); })); //斷開連接 mClientSocket.Shutdown(SocketShutdown.Both); mClientSocket.Close(); break; } } } /// <summary> /// 向所有的客戶端群發消息 /// </summary> /// <param name="msg">message</param> public void SendMessage(string msg) { //確保消息非空以及客戶端列表非空 if (msg == string.Empty || clinetSockets.Count <= 0) return; //向每一個客戶端發送消息 foreach (Socket s in this.clinetSockets) { (s as Socket).Send(Encoding.UTF8.GetBytes(msg)); } } /// <summary> /// 窗體關閉後釋放資源 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void Server_FormClosing(object sender, FormClosingEventArgs e) { } }
======================================客戶端=================================
public partial class Clinet : Form { public Clinet() { InitializeComponent(); } //創建數據對象 Data get = new 客戶端.Data(); /// <summary> /// 客戶端的Socket /// </summary> Socket clinet; private void Clinet_Load(object sender, EventArgs e) { RefTable(); //創建socket CreateSocket(); } /// <summary> /// 創建客戶端的Socket /// </summary> private void CreateSocket() { // 創建socket clinet = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); //連接 //獲得ip和端口(讀取配置文件) var app = System.Configuration.ConfigurationManager.AppSettings; IPEndPoint point = new IPEndPoint(IPAddress.Parse(app["ip"]), Convert.ToInt32(app["port"])); //連接服務器 clinet.Connect(point); //開啟新線程獲取服務器端消息 Thread thClinet = new Thread(new ThreadStart(CallRec)); thClinet.IsBackground = true; thClinet.Start(); } /// <summary> /// 接收消息 /// </summary> private void CallRec() { bool flag = true; while (flag) { byte[] recBuf = new byte[1024]; //獲取返回數據的長度 int length = clinet.Receive(recBuf); //獲取監聽到的數據 string reslut = Encoding.UTF8.GetString(recBuf, 0, length); if (reslut == "1") { //刷新表格數據 RefTable(); } } } /// <summary> /// 刷新表格 /// </summary> private void RefTable() { dataGridView1.Invoke(new Action(() => { dataGridView1.DataSource = get.GetPersonList(); })); } /// <summary> /// 添加數據事件 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void btnAdd_Click(object sender, EventArgs e) { //獲取用戶輸入 string name = txtAddName.Text; int age = Convert.ToInt32(txtAgeAdd.Text); string phone = txtPhoneAdd.Text; //實例化一個數據對象 Person p = new Person() { Name = name, Age = age, Phone = phone }; //寫入數據 AddList(p); } /// <summary> /// 寫入數據 /// </summary> /// <param name="p"></param> private void AddList(Person p) { //獲取數據集合 List<Person> list = dataGridView1.DataSource as List<Person>; //加入數據 list.Add(p); //加入數據 bool b = get.Add(list); if (b) { MessageBox.Show("增加成功"); //增加成功後發送socket信息 //向服務器發送消息 clinet.Send(Encoding.UTF8.GetBytes("1")); } else { MessageBox.Show("增加失敗"); } } private void Clinet_FormClosing(object sender, FormClosingEventArgs e) { } }
客戶端是讀取的本地Json數據創建的對象集合用以模擬數據庫
class Person { public string Name { get; set; } public int Age { get; set; } public string Phone { get; set; } }
class Data { System.Web.Script.Serialization.JavaScriptSerializer js = new System.Web.Script.Serialization.JavaScriptSerializer(); public List<Person> GetPersonList() { //讀取json數據 string jsonStr = File.ReadAllText("Data.json"); List<Person> list = js.Deserialize<List<Person>>(jsonStr); return list; } public bool Add(List<Person> list) { //向數據中覆蓋追加 string strJson = js.Serialize(list); try { File.WriteAllText("Data.json", strJson); return true; } catch { return false; } } }
Data.json數據
[ { "name": "張三", "age": 14, "phone": "13888888888" }, { "name": "張三", "age": 14, "phone": "13888888888" }, { "name": "張三", "age": 14, "phone": "13888888888" }, { "name": "張三", "age": 14, "phone": "13888888888" }, { "name": "張三", "age": 14, "phone": "13888888888" }, { "name": "張三", "age": 14, "phone": "13888888888" }, { "name": "張三", "age": 14, "phone": "13888888888" } ]
====================================運行效果圖==============================
=========================================分割線==================================
其實個人覺得可以把服務端做成一個Windows服務更好
代碼打包地址(內含windows服務代碼)
關於如何將windows服務如何安裝到服務中,網上教程很多,請自行百度
https://pan.baidu.com/s/1J6SIGxwzzvG-B1ACyDWPKw
個人拙作,敬請諒解.
C#Winform實時更新數據庫信息Demo(使用Scoket)