当前位置:   article > 正文

用TCP模拟网页服务器开发记录_模拟tcp服务器

模拟tcp服务器

一、所需要撑握的技术:

1、TCP 下的socket应用
2、多线程动态创建,实现多个客户端的同时访问
3、了解html的语法结构和javaScript的开发
4、了解http数据包格式,包括json、get、post等格式
5、了解http的通讯过程
以下应用介绍,不再介绍基础的知识点,请大家自已通过网络或书籍学习

二、TCP模拟网页服务器的优点

1、不需要专门设计客户端软件。只需网络浏览器即可,实现手机、电脑等平台上查看。
2、客户端显示内容完全由网页服务器决定,不存在客户端软件的升级管理
3、客户也可模拟客户端的方式,按http协议格式与产品进行通讯,实现二次开发。

三、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);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199
  • 200
  • 201
  • 202
  • 203
  • 204
  • 205
  • 206
  • 207
  • 208
  • 209
  • 210
  • 211
  • 212
  • 213
  • 214
  • 215
  • 216
  • 217
  • 218
  • 219
  • 220
  • 221
  • 222
  • 223
  • 224
  • 225
  • 226
  • 227
  • 228
  • 229
  • 230
  • 231
  • 232
  • 233
  • 234
  • 235
  • 236
  • 237
  • 238
  • 239
  • 240
  • 241
  • 242
  • 243
  • 244
  • 245
  • 246
  • 247
  • 248
  • 249
  • 250
  • 251
  • 252
  • 253
  • 254
  • 255
  • 256
  • 257
  • 258
  • 259
  • 260
  • 261
  • 262
  • 263
  • 264
  • 265
  • 266
  • 267
  • 268
  • 269
  • 270
  • 271
  • 272
  • 273
  • 274
  • 275
  • 276
  • 277
  • 278
  • 279
  • 280
  • 281
  • 282
  • 283
  • 284
  • 285
  • 286
  • 287
  • 288
  • 289
  • 290
  • 291
  • 292
  • 293
  • 294
  • 295
  • 296
  • 297
  • 298
  • 299
  • 300
  • 301
  • 302
  • 303
  • 304
  • 305
  • 306
  • 307
  • 308
  • 309
  • 310
  • 311
  • 312
  • 313
  • 314
  • 315
  • 316
  • 317
  • 318
  • 319
  • 320
  • 321
  • 322
  • 323
  • 324
  • 325
  • 326
  • 327
  • 328
  • 329
  • 330
  • 331
  • 332
  • 333
  • 334
  • 335
  • 336
  • 337
  • 338
  • 339
  • 340
  • 341
  • 342
  • 343
  • 344
  • 345
  • 346
  • 347
  • 348
  • 349
  • 350
  • 351
  • 352
  • 353
  • 354
  • 355
  • 356
  • 357
  • 358
  • 359
  • 360
  • 361
  • 362
  • 363
  • 364
  • 365
  • 366
  • 367
  • 368
  • 369
  • 370
  • 371
  • 372
  • 373
  • 374
  • 375
  • 376
  • 377
  • 378
  • 379
  • 380
  • 381
  • 382
  • 383
  • 384
  • 385
  • 386
  • 387
  • 388
  • 389
  • 390
  • 391
  • 392
  • 393
  • 394
  • 395
  • 396
  • 397
  • 398
  • 399
  • 400
  • 401
  • 402
  • 403
  • 404
  • 405
  • 406
  • 407
  • 408
  • 409
  • 410
  • 411
  • 412
  • 413
  • 414
  • 415
  • 416
  • 417
  • 418
  • 419
  • 420
  • 421
  • 422
  • 423
  • 424
  • 425
  • 426
  • 427
  • 428
  • 429
  • 430
  • 431
  • 432
  • 433
  • 434
  • 435
  • 436
  • 437
  • 438
  • 439
  • 440
  • 441
  • 442
  • 443
  • 444
  • 445
  • 446
  • 447
  • 448
  • 449
  • 450
  • 451
  • 452
  • 453
  • 454
  • 455
  • 456
  • 457
  • 458
  • 459
  • 460
  • 461
  • 462
  • 463
  • 464
  • 465
  • 466
  • 467
  • 468
  • 469
  • 470
  • 471
  • 472
  • 473
  • 474
  • 475
  • 476
  • 477
  • 478
  • 479
  • 480
  • 481
  • 482
  • 483
  • 484
  • 485
  • 486
  • 487
  • 488
  • 489
  • 490
  • 491
  • 492
  • 493
  • 494
  • 495
  • 496
  • 497
  • 498
  • 499
  • 500
  • 501
  • 502
  • 503
  • 504
  • 505
  • 506
  • 507
  • 508
  • 509
  • 510
  • 511
  • 512
  • 513
  • 514
  • 515
  • 516
  • 517
  • 518
  • 519

五、效果:

在这里插入图片描述
测试过10多人同时登录,功能正常;通过2天客户端连续在线测试,功能正常。满足将来客户的应用需求。
异常点:苹果手机自带浏览器,只显示界面,而不能刷新,可以需要对JSON数据包格式进行完善,以上代码之所以这么写,也是由于没有找到相应的介绍,完全是本人自已通过多种试验总结的方法。庆幸的是在苹果手机上安装百度浏览器即可解决。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/一键难忘520/article/detail/886219
推荐阅读
相关标签
  

闽ICP备14008679号