赞
踩
1、TCP 下的socket应用
2、多线程动态创建,实现多个客户端的同时访问
3、了解html的语法结构和javaScript的开发
4、了解http数据包格式,包括json、get、post等格式
5、了解http的通讯过程
以下应用介绍,不再介绍基础的知识点,请大家自已通过网络或书籍学习
1、不需要专门设计客户端软件。只需网络浏览器即可,实现手机、电脑等平台上查看。
2、客户端显示内容完全由网页服务器决定,不存在客户端软件的升级管理
3、客户也可模拟客户端的方式,按http协议格式与产品进行通讯,实现二次开发。
以上过程注意事项:
1、服务器可以不主动断开TCP连接,但不建议这么做,原因为JSON格式回复时,网页端会不知何时结束,导致一直连接着,网页端的显示也是不正常。
2、服务器端返回get包时,一定要严格按http的格式,特别是内容长度是按字节数的,如果字节数超了,网页端就不认后面的数据。
以下介绍是通过Qt平台介绍,由于是采用TCP socket模拟,所以在单片机上设计时,完全按此思路设计。服务器代码主要是涉及TCP服务器程序的开发,此处只供参考:
//头文件 #ifndef HTTPSEVERRUN_H #define HTTPSEVERRUN_H #include <QTcpSocket> #include <QTcpServer> #include <QThread> #include <QDebug> #include <QByteArray> #ifdef HTTPSEVERRUN_C #define HTTPSEVERRUN_TEMP #else #define HTTPSEVERRUN_TEMP extern #endif class HttpSeverTcpClientSocket : public QTcpSocket { Q_OBJECT public: explicit HttpSeverTcpClientSocket(QObject *parent = 0); ~HttpSeverTcpClientSocket(); void deleteSelf(void); signals: void updateClients(QByteArray); void disconnected(int); public slots: void dataReceived(); void slotDisconnected(); private: void httpSeverTCP(QByteArray readData); unsigned char m_address;//modbus tcp address }; class HttpServer : public QTcpServer { Q_OBJECT public: explicit HttpServer(QObject *parent = 0 ,QString IP = "",int port= 0); ~HttpServer(); QList<HttpSeverTcpClientSocket*> tcpClientSocketList; signals: void updateServer(QByteArray); public slots: void updateClients(QByteArray); void slotDisconnected(int); protected: void incomingConnection(int socketDescriptor); }; class HttpSeverRun : public QObject { Q_OBJECT public: explicit HttpSeverRun(QObject *parent = 0); ~HttpSeverRun(); signals: void dataArrive(QByteArray ba); //send data to deal public slots: void updateServer(QByteArray); private: QThread *m_thread; HttpServer* server;//服务器任务 }; HTTPSEVERRUN_TEMP class HttpSeverRun *GS_HttpSeverRun; //CPP文件 #define HTTPSEVERRUN_C #include "HttpSeverRun.h" #include "ToolFunc.h" #include "netsetdev.h" #include "manage.h" #include "SerialCommManage.h" #include "SystemFunction.h" #include "autoStepRun.h" HttpSeverRun::HttpSeverRun(QObject *parent):QObject(parent) { m_thread = new QThread(); setNetParaStr temp; readNetPara(&temp); QString str; ToolFunc::UnicodeCharsToQString((INT8U *)(temp.IP),str); server = new HttpServer(this,str,80);//绑定服务器的端口,不一定要求为80,只是80是网页的默认端口号 server->serverError(); this->moveToThread(m_thread); server->moveToThread(m_thread); m_thread->start(); } HttpSeverRun::~HttpSeverRun() { m_thread->quit(); m_thread->wait(); delete m_thread; } void HttpSeverRun::updateServer(QByteArray readData ) { //qDebug()<<"read = "<< readData; } HttpSeverTcpClientSocket::HttpSeverTcpClientSocket(QObject *parent ) :QTcpSocket(parent) { GSPara_lock.lock(); m_address = GSPara.SCOMParaData.address_1; GSPara_lock.unlock(); connect(this,SIGNAL(readyRead()),this,SLOT(dataReceived())); connect(this,SIGNAL(disconnected()),this,SLOT(slotDisconnected())); } HttpSeverTcpClientSocket::~HttpSeverTcpClientSocket() { //qDebug()<<"~TcpClientSocket()"; } void HttpSeverTcpClientSocket::deleteSelf() { deleteLater(); } void HttpSeverTcpClientSocket::dataReceived() { while(bytesAvailable() > 0) { QByteArray msg = read(1024); if(msg.size()>0) { emit updateClients(msg); httpSeverTCP(msg); } } } void HttpSeverTcpClientSocket::slotDisconnected() { emit disconnected(this->socketDescriptor()); } //以下就是网页服务器端服务的内容 //要点是:根据客户端发来的数据包内容回应数据即可。回应时一定要按结合html和http格式进行回应。 void HttpSeverTcpClientSocket::httpSeverTCP(QByteArray readData) { INT8U readBuff[2000]; INT8U txdTemps[2000]; INT8U txdLength; INT32S L_temp;//发送长度 //接收数据处理 //判断数据 if((checkStopOrIotLockState() != 0)||(checkPowerState() == 0)) { return; } //判定接收数据包 L_temp = readData.size(); //qDebug()<<readData; QString str(readData); int x = str.indexOf("GET / HTTP/"); if(x != -1) { QString content = QString("\ <!DOCTYPE html>\r\n\ <html lang=\"en\">\r\n\ <head>\r\n\ <meta charset=\"UTF-8\">\r\n\ <meta http-equiv='Content-Type' content='text/html'; charset='GB2312'/>\r\n\ <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\r\n\ <title>D38-3实时称重</title>\r\n\ <style type='text/css'>\r\n\ body {text-align:left; background-color:#c0deed;font-family:Verdana;}\r\n\ #main {margin-right:auto;margin-left:auto;margin-top:5px;}\r\n\ .LeftLabel {display:inline-block;width:100px;}\r\n\ .LeftLabelH {display:inline-block;width:200px;}\r\n\ .AutoState{width:260px}\r\n\ h3{color:#66b3ff; text-decoration:underline;height: 10px;margin-top: 0px;}\r\n\ #main p {height: 10px;}\r\n\ </style>\r\n\ </head>\r\n\ <body>\r\n\ <div id='main'>\r\n\ <div style='background:snow; display:block;padding:5px 10px;'>\r\n\ <h3>D38-3实时称重</h3>\r\n\ <p><label id='ctime'></label></p>\r\n\ <form id='frmSetting' method='POST'action='/allconfig' >\r\n\ <p><label class='LeftLabel'>设备名称:</label>\r\n\ <input type='text' id='meterName' name='meterName' size='16' disabled='disabled' />\r\n\ </p>\r\n\ <p> <label for='realWeight' class=\"LeftLabel\">实时重量:</label><input style=\"margin-left: 6px; background-color: #e2e6cc;text-align: right;\" type='text' id='realWeight' disabled='disabled'\r\n\ name='realWeight' size='12' />\r\n\ <label id='unit'>kg</label></p>\r\n\ <p> <label class='LeftLabel'>称重状态:</label>\r\n\ <input type='text' id='realWeightState' disabled='disabled' name='realWeightState' size='16' /> \r\n\ </p>\r\n\ <p><label class='LeftLabel'>仪表ID:</label>\r\n\ <input type='text' id='meterID' name='meterID' size='16' disabled='disabled' />\r\n\ </p>\r\n\ <p> <label class=\"LeftLabelH\">无人值守状态:</label>\r\n\ </p>\r\n\ <div style=\"width:253px; height:110px;background-color:#F5F5F5;border: 1px solid rgb(26, 25, 25);border-radius:3px;\">\r\n\ <label id='atuostate' style='padding: 5px;display:inline-block;width:245px;word-break:break-all'></label>\r\n\ </div>\r\n\ </form>\r\n\ </div>\r\n\ </div>\r\n\ <div style='margin-top: 10px;' >\r\n\ <div style='background:snow; display:block;padding:10px 10px;'>\r\n\ <h3 style='color:#66b3ff; text-decoration:underline;'>操作</h3>\r\n\ <p> <label for='username' class=\"LeftLabel\">账号:</label><input type='text' value='admin' id='username' size='15' />\r\n\ </p>\r\n\ <p> <label for='userpassword' class=\"LeftLabel\">密码:</label><input type='password' id='userpassword' size='15' />\r\n\ </p>\r\n\ <div style=\"width: 300px;background:snow; display:block;padding:0px 10px;height: 170px; \">\r\n\ <div>\r\n\ <button οnclick=\"manulZero()\" id='btn_manulZero' style=\"width: 100px;height: 40px; margin-top: 0px;font-size: 20px; float: left;\">置 零</button>\r\n\ </div> \r\n\ <div>\r\n\ <button οnclick=\"manulStop()\" id='btn_manulStop' style=\"width: 100px;height: 40px; margin-top: 0px;font-size: 20px;float: left; margin-left: 30px;\">停 止</button>\r\n\ </div> \r\n\ <div>\r\n\ <button οnclick=\"manulOpen()\" id='btn_manulOpen' style=\"width: 100px;height: 40px; margin-top: 20px;font-size: 20px;float: left; \">启动</button>\r\n\ </div> \r\n\ <div>\r\n\ <button οnclick=\"manulUp()\" id='btn_manulUp' style=\"width: 100px;height: 40px; margin-top: 20px;font-size: 20px;float: left; margin-left: 30px;\">抬杆</button>\r\n\ </div> \r\n\ <div>\r\n\ <button οnclick=\"restart()\" id='btn_manulrestart' style=\"background:#4CAF50;border:none;color:white; width: 100px;height: 40px; margin-top: 20px;font-size: 20px;float: left;border-radius:3px; \">重启</button>\r\n\ </div> \r\n\ </div>\r\n\ </div>\r\n\ <script>\r\n\ setInterval('updateWeightState()',500);\r\n\ function updateWeightState()\r\n\ {\r\n\ const xhr = new XMLHttpRequest();\r\n\ xhr.open('post','/updateWeightState');\r\n\ xhr.send();\r\n\ xhr.onreadystatechange = function()\r\n\ {\r\n\ if(xhr.readyState == 4)\r\n\ {\r\n\ if(xhr.status>=200 && xhr.status <300)\r\n\ {\r\n\ let data = JSON.parse(xhr.response);\r\n\ document.getElementById('realWeight').value=data.realWeight;\r\n\ document.getElementById('unit').value=data.unit;\r\n\ document.getElementById('realWeightState').value=data.realWeightState;\r\n\ document.getElementById('meterID').value=data.meterID;\r\n\ document.getElementById('meterName').value=data.meterName;\r\n\ document.getElementById('ctime').innerHTML=data.ctime;\r\n\ document.getElementById('atuostate').innerHTML=data.atuostate;\r\n\ }\r\n\ }\r\n\ }\r\n\ }\r\n\ function manulZero()\r\n\ {\r\n\ var r = confirm('是否要【置零】操作?');\r\n\ if(r == false) \r\n\ { \r\n\ return;\r\n\ }\r\n\ var usernameTemp = document.getElementById('username').value;\r\n\ var userpasswordTemp = document.getElementById('userpassword').value;\r\n\ var IDTemp = document.getElementById('meterID').value;\r\n\ var postcontent = '/maul/manulZero'+'?'+'meterID='+IDTemp+'&username='+usernameTemp+'&userpassword='+userpasswordTemp;\r\n\ const xhr = new XMLHttpRequest();\r\n\ xhr.open('get',postcontent);\r\n\ xhr.send();\r\n\ xhr.onreadystatechange = function()\r\n\ {\r\n\ if(xhr.readyState == 4)\r\n\ {\r\n\ if(xhr.status>=200 && xhr.status <300)\r\n\ {\r\n\ //console.log(xhr.response);\r\n\ let data = JSON.parse(xhr.response);\r\n\ alert(data.result);\r\n\ }\r\n\ }\r\n\ }\r\n\ }\r\n\ function manulStop()\r\n\ {\r\n\ var r = confirm('是否要【停止】无人值守工作的操作?');\r\n\ if(r == false) \r\n\ { \r\n\ return;\r\n\ }\r\n\ var usernameTemp = document.getElementById('username').value;\r\n\ var userpasswordTemp = document.getElementById('userpassword').value;\r\n\ var IDTemp = document.getElementById('meterID').value;\r\n\ var postcontent = '/maul/manulStop'+'?'+'meterID='+IDTemp+'&username='+usernameTemp+'&userpassword='+userpasswordTemp;\r\n\ const xhr = new XMLHttpRequest();\r\n\ xhr.open('get',postcontent);\r\n\ xhr.send();\r\n\ xhr.onreadystatechange = function()\r\n\ {\r\n\ if(xhr.readyState == 4)\r\n\ {\r\n\ if(xhr.status>=200 && xhr.status <300)\r\n\ {\r\n\ //console.log(xhr.response);\r\n\ let data = JSON.parse(xhr.response);\r\n\ alert(data.result);\r\n\ }\r\n\ }\r\n\ }\r\n\ btnDelay(2);\r\n\ }\r\n\ function manulOpen()\r\n\ {\r\n\ var r;\r\n\ if(document.getElementById('atuostate').innerHTML == '可按【启动】进入无人值守界面')\r\n\ {\r\n\ r = confirm('是否要进入无人值守界面!');\r\n\ }\r\n\ else\r\n\ {\r\n\ r = confirm('是否要【启动】无人值守操作?操作前请确认栏杆机降杆安全!');\r\n\ }\r\n\ if(r == false) \r\n\ { \r\n\ return;\r\n\ }\r\n\ var usernameTemp = document.getElementById('username').value;\r\n\ var userpasswordTemp = document.getElementById('userpassword').value;\r\n\ var IDTemp = document.getElementById('meterID').value;\r\n\ var postcontent = '/maul/manulOpen'+'?'+'meterID='+IDTemp+'&username='+usernameTemp+'&userpassword='+userpasswordTemp;\r\n\ const xhr = new XMLHttpRequest();\r\n\ xhr.open('get',postcontent);\r\n\ xhr.send();\r\n\ xhr.onreadystatechange = function()\r\n\ {\r\n\ if(xhr.readyState == 4)\r\n\ {\r\n\ if(xhr.status>=200 && xhr.status <300)\r\n\ {\r\n\ //console.log(xhr.response);\r\n\ let data = JSON.parse(xhr.response);\r\n\ alert(data.result);\r\n\ }\r\n\ }\r\n\ }\r\n\ btnDelay(2);\r\n\ }\r\n\ function manulUp()\r\n\ {\r\n\ var r = confirm('是否要【抬杆】操作?此操作在无人值守开启状态下无效!');\r\n\ if(r == false) \r\n\ { \r\n\ return;\r\n\ }\r\n\ var usernameTemp = document.getElementById('username').value;\r\n\ var userpasswordTemp = document.getElementById('userpassword').value;\r\n\ var IDTemp = document.getElementById('meterID').value;\r\n\ var postcontent = '/maul/manulUp'+'?'+'meterID='+IDTemp+'&username='+usernameTemp+'&userpassword='+userpasswordTemp;\r\n\ const xhr = new XMLHttpRequest();\r\n\ xhr.open('get',postcontent);\r\n\ xhr.send();\r\n\ xhr.onreadystatechange = function()\r\n\ {\r\n\ if(xhr.readyState == 4)\r\n\ {\r\n\ if(xhr.status>=200 && xhr.status <300)\r\n\ {\r\n\ //console.log(xhr.response);\r\n\ let data = JSON.parse(xhr.response);\r\n\ alert(data.result);\r\n\ }\r\n\ }\r\n\ }\r\n\ btnDelay(3);\r\n\ }\r\n\ function restart()\r\n\ {\r\n\ var r = confirm('是否要重启仪表!');\r\n\ if(r == false) \r\n\ { \r\n\ return;\r\n\ }\r\n\ var usernameTemp = document.getElementById('username').value;\r\n\ var userpasswordTemp = document.getElementById('userpassword').value;\r\n\ var IDTemp = document.getElementById('meterID').value;\r\n\ var postcontent = '/maul/restart'+'?'+'meterID='+IDTemp+'&username='+usernameTemp+'&userpassword='+userpasswordTemp;\r\n\ const xhr = new XMLHttpRequest();\r\n\ xhr.open('get',postcontent);\r\n\ xhr.send();\r\n\ xhr.onreadystatechange = function()\r\n\ {\r\n\ if(xhr.readyState == 4)\r\n\ {\r\n\ if(xhr.status>=200 && xhr.status <300)\r\n\ {\r\n\ //console.log(xhr.response);\r\n\ let data = JSON.parse(xhr.response);\r\n\ alert(data.result);\r\n\ }\r\n\ }\r\n\ }\r\n\ btnDelay(2);\r\n\ }\r\n\ function btnDelay(delayTimeS)\r\n\ {\r\n\ var btn_zero = document.getElementById('btn_manulZero');\r\n\ var btn_open = document.getElementById('btn_manulOpen');\r\n\ var btn_up = document.getElementById('btn_manulUp');\r\n\ var btn_stop = document.getElementById('btn_manulStop');\r\n\ var btn_restart = document.getElementById('btn_manulrestart');\r\n\ btn_zero.disabled = true;\r\n\ btn_open.disabled = true;\r\n\ btn_up.disabled = true;\r\n\ btn_stop.disabled = true;\r\n\ btn_restart.disabled = true;\r\n\ var time = delayTimeS;\r\n\ var timer = setInterval(countDown,1000);\r\n\ function countDown(){\r\n\ time--;\r\n\ if(time >= 0){\r\n\ btn_zero.innerHTML = '置 零'+time+'S';\r\n\ btn_open.innerHTML = '启 动'+time+'S';\r\n\ btn_up.innerHTML = '抬 杆'+time+'S';\r\n\ btn_stop.innerHTML = '停 止'+time+'S'; \r\n\ btn_restart.innerHTML = '重 启'+time+'S'; \r\n\ }\r\n\ else{\r\n\ btn_zero.innerHTML = '置 零';\r\n\ btn_open.innerHTML = '启 动';\r\n\ btn_up.innerHTML = '抬 杆';\r\n\ btn_stop.innerHTML = '停 止';\r\n\ btn_restart.innerHTML = '重 启';\r\n\ btn_zero.disabled = false;\r\n\ btn_open.disabled = false;\r\n\ btn_up.disabled = false;\r\n\ btn_stop.disabled = false;\r\n\ btn_restart.disabled = false;\r\n\ clearTimeout(timer);\r\n\ }\r\n\ }\r\n\ }\r\n\ </script>\r\n\ </body>\r\n\ </html>\ "); QByteArray contentBytes = content.toUtf8(); QString sendData=QString( "HTTP/1.1 200 OK\r\n\ Date: Mon, 27 Jul 2009 12:28:53 GMT\r\n\ Server: ApacheLast-Modified: Wed, 22 Jul 2009 19:15:56 \r\n\ GMTETag: \"34aa387-d-1568eb00\"\r\n\ Accept-Ranges: bytes\r\n\ Content-Type: text/html;charset=UTF-8\r\n\ Content-Length: %1\r\n\ \r\n").arg(contentBytes.size()); QByteArray bytes = sendData.toUtf8(); bytes.append(contentBytes); write(bytes); flush(); close(); return ; } x = str.indexOf("POST /updateWeightState HTTP/");//读取数据 if(x != -1) { ****** QString content = QString( "{\"realWeight\":\"%1\",\"unit\":\"%2\",\"realWeightState\":\"%3\",\"meterID\":\"%4\",\"ctime\":\"%5\",\"atuostate\":\"%6\",\"meterName\":\"%7\"}")\ .arg(realWeight).arg(unit).arg(realWeightState).arg(meterID).arg(currentTime).arg(P_autoStepRun->readRunStateContent()).arg(bangName); QByteArray bytes = content.toUtf8(); //qDebug()<<"===1==="<<bytes; write(bytes); //发送完成,后断开连接 flush(); close(); return ; } //其它指令处理与上相似,不现重复写 return ; } HttpServer::HttpServer(QObject *parent, QString IP, int port):QTcpServer(parent) { listen(QHostAddress(IP),port); } HttpServer::~HttpServer() { //qDebug()<<"~Server()"; } void HttpServer::updateClients(QByteArray msg) { emit updateServer(msg); } void HttpServer::slotDisconnected(int descriptor) { for(int i = 0; i < tcpClientSocketList.count();i++) { HttpSeverTcpClientSocket *item = tcpClientSocketList.at(i); if(item->socketDescriptor() == descriptor) { tcpClientSocketList.removeAt(i); item->deleteSelf(); return; } } return; } void HttpServer::incomingConnection(int socketDescriptor) { HttpSeverTcpClientSocket * TcpClientSocketTemp = new HttpSeverTcpClientSocket(this); connect(TcpClientSocketTemp,SIGNAL(updateClients(QByteArray)),this,SLOT(updateClients(QByteArray))); connect(TcpClientSocketTemp,SIGNAL(disconnected(int)),this,SLOT(slotDisconnected(int))); TcpClientSocketTemp->setSocketDescriptor(socketDescriptor); tcpClientSocketList.append(TcpClientSocketTemp); }
测试过10多人同时登录,功能正常;通过2天客户端连续在线测试,功能正常。满足将来客户的应用需求。
异常点:苹果手机自带浏览器,只显示界面,而不能刷新,可以需要对JSON数据包格式进行完善,以上代码之所以这么写,也是由于没有找到相应的介绍,完全是本人自已通过多种试验总结的方法。庆幸的是在苹果手机上安装百度浏览器即可解决。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。