C# WPF上位机实现和下位机TCP通讯的方法
下位机使用北京大华程控电源dh1766-1,上位机使用wpf。实现了电压电流实时采集,曲线显示。上午在公司调试成功,手头没有程控电源,使用tcp服务端模拟。昨天写的tcp服务端正好排上用场。
界面如下:
服务端
服务端实在上篇基础上实现的。需要做如下更改:
while (true) { try { byte[] bufferdate = new byte[1024]; int reallen = psocket.receive(bufferdate); if (reallen <= 0) { this.invoke(addtextdelegate, psocket.remoteendpoint.tostring() + "退出\r\n"); socketlist.remove(psocket); //客户端退出的时候会发送一个空字节 psocket.shutdown(socketshutdown.both); psocket.close(); return; } string receivestr = encoding.default.getstring(bufferdate, 0, reallen); switch (receivestr) { case "meas:voltage:all?\n": proxsocket.send(encoding.default.getbytes(r.next(16,25).tostring()+ ","+r.next(16, 25).tostring()+","+ r.next(16, 25).tostring())); break; case "meas:curr:all?\n": proxsocket.send(encoding.default.getbytes(r.next(2, 5).tostring() + "," + r.next(2, 5).tostring() + "," + r.next(2, 5).tostring())); break; default: break; } this.invoke(addtextdelegate, receivestr + "from" + psocket.remoteendpoint.tostring() + "\r\n"); } catch (exception ex) { this.invoke(addtextdelegate, psocket.remoteendpoint.tostring() + "异常退出\r\n"); socketlist.remove(psocket); psocket.shutdown(socketshutdown.both); psocket.close(); return; } }
在while循环中加入:
switch (receivestr) { case "meas:voltage:all?\n": proxsocket.send(encoding.default.getbytes(r.next(16,25).tostring()+ ","+r.next(16, 25).tostring()+","+ r.next(16, 25).tostring())); break; case "meas:curr:all?\n": proxsocket.send(encoding.default.getbytes(r.next(2, 5).tostring() + "," + r.next(2, 5).tostring() + "," + r.next(2, 5).tostring())); break; default: break; }
模拟电源,当收到电压查询时,发送16~25中随机数,由于电源是三个通道的,因此发送三个随机数,用逗号隔开。同样收到电流查询,发送2~5之间的随机数。
完整的客户端源码:
public partial class form1 : form { public form1() { initializecomponent(); addtextdelegate = new addtextdelegate(addtext); } private addtextdelegate addtextdelegate; private list<socket> socketlist = new list<socket>(); public delegate void addtextdelegate(string text); private void addtext(string text) { txtlog.text += text; } random r = new random(); private void btnstart_click(object sender, eventargs e) { //参数:寻址方式 传输数据方式 通信协议 socket socket = new socket(addressfamily.internetwork,sockettype.stream,protocoltype.tcp); ipaddress ipaddress = ipaddress.parse(txtip.text); //创建endpoint ipendpoint ipendpoint = new ipendpoint(ipaddress, int.parse(txtport.text)); //绑定端口 socket.bind(ipendpoint); //开启侦听 socket.listen(10); txtlog.text += "服务启动开启侦听……\r\n"; thread thread = new thread((s) => { socket sersocket = (socket)s; while (true)//不断接收客户端连接 { this.invoke(addtextdelegate, "服务正在等待客户端连接……\r\n"); //开始接收客户端的连接 //阻塞当前线程,等待客户端连接 //客户端连接上之后,服务端自动生成一个socket和连接的客端通信 socket proxsocket = sersocket.accept(); this.invoke(addtextdelegate, "客户端连接成功!\r\n" + proxsocket.remoteendpoint.tostring()); //proxsocket.send(encoding.default.getbytes("连接成功!")); socketlist.add(proxsocket);//当前通信的socket放到集合中 new thread(p => { socket psocket = (socket)p; while (true) { try { byte[] bufferdate = new byte[1024]; int reallen = psocket.receive(bufferdate); if (reallen <= 0) { this.invoke(addtextdelegate, psocket.remoteendpoint.tostring() + "退出\r\n"); socketlist.remove(psocket); //客户端退出的时候会发送一个空字节 psocket.shutdown(socketshutdown.both); psocket.close(); return; } string receivestr = encoding.default.getstring(bufferdate, 0, reallen); switch (receivestr) { case "meas:voltage:all?\n": proxsocket.send(encoding.default.getbytes(r.next(16,25).tostring()+ ","+r.next(16, 25).tostring()+","+ r.next(16, 25).tostring())); break; case "meas:curr:all?\n": proxsocket.send(encoding.default.getbytes(r.next(2, 5).tostring() + "," + r.next(2, 5).tostring() + "," + r.next(2, 5).tostring())); break; default: break; } this.invoke(addtextdelegate, receivestr + "from" + psocket.remoteendpoint.tostring() + "\r\n"); } catch (exception ex) { this.invoke(addtextdelegate, psocket.remoteendpoint.tostring() + "异常退出\r\n"); socketlist.remove(psocket); psocket.shutdown(socketshutdown.both); psocket.close(); return; } } }) { isbackground = true }.start(proxsocket); } }); thread.isbackground = true; thread.start(socket); } private void btnsend_click(object sender, eventargs e) { string str = txtsend.text; byte[] data = encoding.default.getbytes(str); foreach (var socket in socketlist) { if (socket != null && socket.connected) { socket.send(data); } } } }
上位机实现客户端功能。具体如下:
1、字段和属性
public readonly ipendpoint tagetipep; public bool isconnected { get; set; } = false; private socket socket = new socket(addressfamily.internetwork, sockettype.stream, protocoltype.tcp) { /*receivetimeout=1000,sendtimeout=1000*/}; private thread reclistenthread; public string receivestr { get; set; } public byte[] receivebyte { get; set; }
tagetipep是服务器地址和端口。
isconnected是连接的状态,这个比较重要,在发送和接收时,都要更加isconnected进行,并更新isconnected。
socket用于和客户端通讯。
reclistenthread是监听客户端消息的线程。
receivestr和receivebyte用来存储客户端发来的消息。
2、方法函数连接方法:
public bool connect() { try { socket = new socket(addressfamily.internetwork, sockettype.stream, protocoltype.tcp) { //receivetimeout = 1000, //sendtimeout=1000 }; //iasyncresult connresult = socket.beginconnect(tagetipep.address, tagetipep.port, null, null); //connresult.asyncwaithandle.waitone(5000, true); //if (connresult.iscompleted) //{ socket.connect(tagetipep.address, tagetipep.port); isconnected = true; //开启接收监听 reclistenthread = new thread(() => { while (true) { try { receivebyte = new byte[1024]; int reallen = socket.receive(receivebyte); receivestr = encoding.default.getstring(receivebyte, 0, reallen); receiveevent(); if (reallen <= 0) { if (socket != null && socket.connected) { //服务器退出 isconnected = false; log.writelog("服务器退出!"); socket.shutdown(socketshutdown.both); socket.close(); messagebox.show("连接断开!"); } return; } } catch (exception ex) { if (socket != null && socket.connected) { isconnected = false; log.writelog("服务器异常退出!", ex); socket.shutdown(socketshutdown.both); socket.close(); } return; } } }) { isbackground = true }; reclistenthread.start(); return true; //} } catch (exception ex) { log.writelog(tagetipep.address + "连接失败", ex); } return false; }
连接函数返回值为bool类型,根据返回值判断连接是否成功连接。这里每次连接都实例化了一个socket,因为在执行socket.close()后,重新打开会失败,而断线重连会经常用到,没有找到更好的方法,干脆重新实例化socket。连接成功后,开启监听服务端消息的线程。这里使用了一个receiveevent()事件,在接收到消息时会触发这个事件,刷新ui界面。
发送方法:
public bool send(string msg) { byte[] sendmsg = encoding.default.getbytes(msg); if (sendmsg.length > 0&&isconnected) { if (socket != null && socket.connected) { try { socket.send(sendmsg); return true; } catch (exception ex) { isconnected = false; log.writelog("发送数据失败,目标地址" + tagetipep.address, ex); } } } return false; }
关闭方法:
public void close() { if (socket != null && socket.connected) { isconnected = false; reclistenthread.abort(); log.writelog("关闭连接!"); socket.shutdown(socketshutdown.both); socket.close(); } }
在出现异常时调用
消息接收事件:
public event action receiveevent;
每次接收消息时触发,获取属性receivestr和receivebyte的值,刷新ui界面。
完整代码:
public class tcpclient { public tcpclient(/*ipendpoint localipep,*/ipendpoint targetipep) { //socket.bind(localipep); tagetipep = targetipep; } public readonly ipendpoint tagetipep; public bool isconnected { get; set; } = false; private socket socket = new socket(addressfamily.internetwork, sockettype.stream, protocoltype.tcp) { /*receivetimeout=1000,sendtimeout=1000*/}; public bool connect() { try { socket = new socket(addressfamily.internetwork, sockettype.stream, protocoltype.tcp) { //receivetimeout = 1000, //sendtimeout=1000 }; //iasyncresult connresult = socket.beginconnect(tagetipep.address, tagetipep.port, null, null); //connresult.asyncwaithandle.waitone(5000, true); //if (connresult.iscompleted) //{ socket.connect(tagetipep.address, tagetipep.port); isconnected = true; //开启接收监听 reclistenthread = new thread(() => { while (true) { try { receivebyte = new byte[1024]; int reallen = socket.receive(receivebyte); receivestr = encoding.default.getstring(receivebyte, 0, reallen); receiveevent(); if (reallen <= 0) { if (socket != null && socket.connected) { //服务器退出 isconnected = false; log.writelog("服务器退出!"); socket.shutdown(socketshutdown.both); socket.close(); messagebox.show("连接断开!"); } return; } } catch (exception ex) { if (socket != null && socket.connected) { isconnected = false; log.writelog("服务器异常退出!", ex); socket.shutdown(socketshutdown.both); socket.close(); } return; } } }) { isbackground = true }; reclistenthread.start(); return true; //} } catch (exception ex) { log.writelog(tagetipep.address + "连接失败", ex); } return false; } public bool send(string msg) { byte[] sendmsg = encoding.default.getbytes(msg); if (sendmsg.length > 0&&isconnected) { if (socket != null && socket.connected) { try { socket.send(sendmsg); return true; } catch (exception ex) { isconnected = false; log.writelog("发送数据失败,目标地址" + tagetipep.address, ex); } } } return false; } public event action receiveevent; public string receivestr { get; set; } public byte[] receivebyte { get; set; } public void close() { if (socket != null && socket.connected) { isconnected = false; reclistenthread.abort(); log.writelog("关闭连接!"); socket.shutdown(socketshutdown.both); socket.close(); } } private thread reclistenthread; }
前台调用,声明timer定时器,每个一秒触发一次。触发事件如下:
private string flag = ""; private void querytimer_elapsed(object sender, system.timers.elapsedeventargs e) { now = datetime.now; if (!tcp.send("meas:voltage:all?\n")) { querytimer.enabled = false; startcontent = "开始"; conncontent = "连接"; tcp.isconnected = false; messagebox.show("查询失败!"); return; } flag = "v"; thread.sleep(50); if (!tcp.send("meas:curr:all?\n")) { querytimer.enabled = false; startcontent = "开始"; conncontent = "连接"; tcp.isconnected = false; messagebox.show("查询失败!"); return; } flag = "c"; #region 测试 //angle += 18; //if (angle > 360) //{ // angle = 18; //} #endregion }
刷新ui界面的事件如下:
private void tcp_receiveevent() { task.run(() => { application.current.dispatcher.invoke(() => { remoteip = tcp.tagetipep.tostring(); switch (flag) { case "v": voltvalue = math.round(convert.todouble(tcp.receivestr.split(',')[0]), 3); break; case "c": currentvalue = math.round(convert.todouble(tcp.receivestr.split(',')[0]), 3); break; default: break; } #region 测试 //voltvalue = math.round(math.sin((angle) * pi / 180) * 16 + 16, 3); //currentvalue = math.round(math.sin((angle) * pi / 180) * 2.5 + 2.5, 3); #endregion voltvalues.add(voltvalue); currentvalues.add(currentvalue); if (voltvalues.count > 30) { voltvalues.removeat(0); currentvalues.removeat(0); } }); }); }
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。