WebSocket
为什么需要 WebSocket?
初次接触 WebSocket 的人,都会问同样的问题:我们已经有了 HTTP 协议,为什么还需要另一个协议?它能带来什么好处?
答案很简单,因为 HTTP 协议有一个缺陷:通信只能由客户端发起。
举例来说,我们想了解今天的天气,只能是客户端向服务器发出请求,服务器返回查询结果。HTTP 协议做不到服务器主动向客户端推送信息。
WebSocket一种在单个TCP连接上进行全双工通讯的协议。在 WebSocket API 中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并允许数据进行双向传送。——- 《维基百科》
WebSocket 协议使用ws:和wss:URL协议,以分别代表不安全和安全的 WebSocket 请求。
WebSocket 协议在2008年诞生,2011年成为国际标准。所有浏览器都已经支持了。
它的最大特点就是,服务器可以主动向客户端推送信息,客户端也可以主动向服务器发送信息,是真正的双向平等对话,属于服务器推送技术的一种。
其他特点包括:
(1)建立在 TCP 协议之上,服务器端的实现比较容易。
(2)与 HTTP 协议有着良好的兼容性。默认端口也是80和443,并且握手阶段采用 HTTP 协议,因此握手时不容易屏蔽,能通过各种 HTTP 代理服务器。
(3)数据格式比较轻量,性能开销小,通信高效。
(4)可以发送文本,也可以发送二进制数据。
(5)没有同源限制,客户端可以与任意服务器通信。
(6)协议标识符是ws(如果加密,则为wss),服务器网址就是 URL ws://example.com:80/some/path
下面我以Qt c++来介绍
server服务端
用Qt建立服务端用到了类QWebSocketServer、和QWebSocket
(1)监听端口port,即告诉服务器监听地址和端口的传入连接情况,当有连接将新连接信号和处理槽绑定,关闭信号依然,方便后面的处理。
if (m_pWebSocketServer->listen(QHostAddress::Any, port))
{
if (m_debug)
qDebug() << "Echoserver listening on port" << port;
connect(m_pWebSocketServer, &QWebSocketServer::newConnection,
this, &EchoServer::onNewConnection);
connect(m_pWebSocketServer, &QWebSocketServer::closed, this, &EchoServer::closed);
}
在当发出有新连接信号时,槽获取客户端的连接
QWebSocket *pSocket = m_pWebSocketServer->nextPendingConnection();
(2)在创建好连接,可以用pSocket接收数据,发送数据等。
connect(pSocket, &QWebSocket::textMessageReceived, this, &EchoServer::processTextMessage); //接收到文本数据
connect(pSocket, &QWebSocket::binaryMessageReceived, this, &EchoServer::processBinaryMessage); //接收到二进制数据
connect(pSocket, &QWebSocket::disconnected, this, &EchoServer::socketDisconnected);
在实例代码中,看到了QObject的函数 sender()。简单提一句:当某一个目标emit一个信号的时候,它就是一个sender,系统会记录下当前是谁emit出这个signal的,所以在对应的slot里就可以通过 sender()得到当前是谁调用了了这个槽,对应的是QObject->d->sender。
client客户端
使用WebSocket API创建一个简单的客户端。这个过程更简单:根据给的url地址打开连接,从而进行一系列操作。
.cpp
#include "echoclient.h"
#include
QT_USE_NAMESPACE
EchoClient::EchoClient(const QUrl &url, bool debug, QObject *parent) :
QObject(parent),
m_url(url),
m_debug(debug)
{
if (m_debug)
qDebug() << "WebSocket server:" << url;
connect(&m_webSocket, &QWebSocket::connected, this, &EchoClient::onConnected);
connect(&m_webSocket, &QWebSocket::disconnected, this, &EchoClient::closed);
m_webSocket.open(QUrl(url));
}
void EchoClient::onConnected()
{
if (m_debug)
qDebug() << "WebSocket connected";
connect(&m_webSocket, &QWebSocket::textMessageReceived,
this, &EchoClient::onTextMessageReceived);
m_webSocket.sendTextMessage(QStringLiteral("Hello, world!"));
}
void EchoClient::onTextMessageReceived(QString message)
{
if (m_debug)
qDebug() << "Message received:" << message;
m_webSocket.close();
}
.h
#ifndef ECHOCLIENT_H
#define ECHOCLIENT_H
#include
#include
class EchoClient : public QObject
{
Q_OBJECT
public:
explicit EchoClient(const QUrl &url, bool debug = false, QObject *parent = nullptr);
Q_SIGNALS:
void closed();
private Q_SLOTS:
void onConnected();
void onTextMessageReceived(QString message);
private:
QWebSocket m_webSocket;
QUrl m_url;
bool m_debug;
};