【问题标题】:Connecting Qt with SSL to a jetty server使用 SSL 将 Qt 连接到码头服务器
【发布时间】:2013-11-23 01:32:42
【问题描述】:

我在将 qt 客户端连接到嵌入式码头服务器时遇到了一些问题。

首先,我使用以下组件:

Qt 4.4.3(编译时带有活动的 openssl 支持) 码头 8.8.1 java 6

我知道,这些版本不是最新的,但由于许可问题和客户的意愿,我不能使用较新的版本。

所以,这个场景是 qt 客户端必须向码头服务器发送 http GET 和 POST 请求。只要我使用简单的 http 和 QHttp 对象就可以正常工作,当我切换到 SSL 时问题就开始了。

我的第一次尝试是使用 QSslSocket 对象进行 GET 请求:

// Load certs + private key to socket   
_pSocket = new QSslSocket(this);
_pSocket->setLocalCertificate(_certificate);
_pSocket->setPrivateKey(_privatekey);
_pSocket->addDefaultCaCertificate(_cacertificate);

connect (_pSocket, SIGNAL(encrypted()), this, SLOT(_encrypted()));
_pSocket->connectToHostEncrypted("localhost", 8000);

加密状态具有以下槽函数:

void TestClient::_encrypted() {
    QString _path("/testpath/list");
    QByteArray buffer("GET ");
    buffer.append(_path).append(" HTTP/1.1\r\n");
    _pSocket->write(buffer);
}

这是我的第一个问题:

这会产生以下字符串,据我所知,它符合 RFC 2616:

"GET /testpath/list HTTP/1.1\r\n"

由于某种原因,码头服务器存在问题,一直循环直到客户端由于超时而关闭连接。

但是如果我使用下面的字符串,它就完美了:

"GET /testpath/list\r\n"

这是我的第一个问题:您现在对​​这种行为有解释吗?我可以忍受,但我想知道原因

我的第二个问题是 POST 请求,这总是失败。

这些例子我已经试过了:

"POST /testpath/receive/\r\n{"data":"hello world ?!"}\r\n"
"POST /testpath/receive/ HTTP/1.1\r\n{"data":"hello world ?!"}\r\n"
"POST /testpath/receive/\r\n\r\n{"data":"hello world ?!"}\r\n"
"POST /testpath/receive/ HTTP/1.1\r\n\r\n{"data":"hello world ?!"}\r\n"

我有一种感觉,body 每次都是空的,所以我的服务器崩溃了,因为他试图将一个空字符串解析为 json。 至少,以下日志显示:

2013-11-19 17:11:51.671, INFO, foo.bar.RepositoryHandler, qtp11155366-16 - /testpath/receive request type : /receive
2013-11-19 17:11:51.811, ERROR, foo.bar.RepositoryHandler, qtp11155366-16 - /testpath/receive missing or unknown elements in JSON request. Check JSON against documentation
2013-11-19 17:11:51.874, WARN, org.eclipse.jetty.server.AbstractHttpConnection, qtp11155366-16 - /testpath/receive /testpath/receive
java.lang.NullPointerException: null
    at foo.bar.RepositoryHandler.decodeViewingRequest(RepositoryHandler.java:366) ~[MyServer.jar:na]
    at foo.bar.RepositoryHandler.handle(RepositoryHandler.java:182) ~[MyServer.jar:na]

所以,总的来说,我认为我的请求中有几个重大错误。但是哪个?

我的第二次尝试是使用 QHttp 对象并将它使用的 QSocket 更改为我已经启动的 QSslSocket。

下面是main函数的代码:

QSslSocket* _pSocket;
QHttp* _pHttp;
int _id;
QBuffer* _pBuffer;
QByteArray _data;   

_pSocket = new QSslSocket(this);
_pSocket->setLocalCertificate(_certificate);
_pSocket->setPrivateKey(_privatekey);
_pSocket->addDefaultCaCertificate(_cacertificate);

QUrl url;
url.setScheme("https");
url.setHost("localhost");
url.setPort(8001);
url.setPath("/testpath/receive");

connect (_pSocket, SIGNAL(encrypted()), this, SLOT(_encrypted()));
connect(_pHttp,SIGNAL(requestFinished(int,bool)),this,SLOT(_requestFinished(int,bool))); 
connect(_pHttp,SIGNAL(done(bool)),this,SLOT(_done(bool)));

_pBuffer = new QBuffer(&_data);
_pHttp->setSocket(_pSocket);
_pSocket->connectToHostEncrypted(strHost, strPort.toInt());
_id = _pHttp->get(url.toString(),_pBuffer);

还有回调:

void _requestFinished(int id, bool error) {
    if(id = _id)
        qDebug() << "data=" << _data;
}

void _encrypted() {
    qDebug() << "encrypted";
}

void _done(bool error) {
    logInfo() << "_done";

    if(_pHttp) {
        _pHttp->abort();
        delete _pHttp;
        _pHttp = 0;
    }
    if(_pBuffer) {
        delete _pBuffer;
        _pBuffer = 0;
    }    

    if(_pSocket) {
        _pSocket->disconnectFromHost();
        delete _pSocket;
        _pSocket = 0;
    }
}

我想,我只需要改变 _pHttp->get 调用的位置,也许在 _encrypted 回调中,但我不确定。

有什么好的建议吗?

谢谢, 罗伯特

【问题讨论】:

    标签: qt ssl jetty embedded-jetty


    【解决方案1】:

    根据RFC2616,您的 HTTP 请求不完整。

    "GET /testpath/list HTTP/1.1\r\n"
    

    这是无效的。

    试试这个。

    "GET /testpath/list HTTP/1.1\r\n" + /* request line (required) */
    "Host: localhost\r\n" + /* host header (required minimum) */
    "\r\n" /* terminating CR + LF (required) */
    

    Section 5.1.2中所述

       The most common form of Request-URI is that used to identify a
       resource on an origin server or gateway. In this case the absolute
       path of the URI MUST be transmitted (see section 3.2.1, abs_path) as
       the Request-URI, and the network location of the URI (authority) MUST
       be transmitted in a Host header field. For example, a client wishing
       to retrieve the resource above directly from the origin server would
       create a TCP connection to port 80 of the host "www.w3.org" and send
       the lines:
    
           GET /pub/WWW/TheProject.html HTTP/1.1
           Host: www.w3.org
    

    Request-URI 行 Host header Header 是强制性的。

    【讨论】:

    • 谢谢,这就是为什么 POST 请求正文总是空的原因。
    • 对于POST 请求,您还应该包含Content-Length 标头。
    • GETPOST 请求中包含Connection: close 标头也会有所帮助。 (这样服务器就知道在响应后关闭打开的连接)。
    猜你喜欢
    • 2018-08-31
    • 2018-12-12
    • 2016-04-27
    • 2012-09-06
    • 2011-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-09-13
    • 2013-09-30
    相关资源
    最近更新 更多