1 | // Copyright (C) 2017 Ford Motor Company |
2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only |
3 | |
4 | #include "qconnection_tcpip_backend_p.h" |
5 | |
6 | #include <QtNetwork/qhostinfo.h> |
7 | |
8 | QT_BEGIN_NAMESPACE |
9 | |
10 | TcpClientIo::TcpClientIo(QObject *parent) |
11 | : QtROClientIoDevice(parent) |
12 | , m_socket(new QTcpSocket(this)) |
13 | { |
14 | connect(sender: m_socket, signal: &QTcpSocket::readyRead, context: this, slot: &QtROClientIoDevice::readyRead); |
15 | connect(sender: m_socket, signal: &QAbstractSocket::errorOccurred, context: this, slot: &TcpClientIo::onError); |
16 | connect(sender: m_socket, signal: &QTcpSocket::stateChanged, context: this, slot: &TcpClientIo::onStateChanged); |
17 | } |
18 | |
19 | TcpClientIo::~TcpClientIo() |
20 | { |
21 | close(); |
22 | } |
23 | |
24 | QIODevice *TcpClientIo::connection() const |
25 | { |
26 | return m_socket; |
27 | } |
28 | |
29 | void TcpClientIo::doClose() |
30 | { |
31 | if (m_socket->isOpen()) { |
32 | connect(sender: m_socket, signal: &QTcpSocket::disconnected, context: this, slot: &QObject::deleteLater); |
33 | m_socket->disconnectFromHost(); |
34 | } else { |
35 | this->deleteLater(); |
36 | } |
37 | } |
38 | |
39 | void TcpClientIo::doDisconnectFromServer() |
40 | { |
41 | m_socket->disconnectFromHost(); |
42 | } |
43 | |
44 | void TcpClientIo::connectToServer() |
45 | { |
46 | if (isOpen()) |
47 | return; |
48 | const QString &host = url().host(); |
49 | QHostAddress address(host); |
50 | if (address.isNull()) |
51 | address = QHostInfo::fromName(name: host).addresses().value(i: 0); |
52 | |
53 | if (address.isNull()) |
54 | qWarning(msg: "connectToServer(): Failed to resolve host %s" , qUtf8Printable(host)); |
55 | else |
56 | m_socket->connectToHost(address, port: url().port()); |
57 | } |
58 | |
59 | bool TcpClientIo::isOpen() const |
60 | { |
61 | return (!isClosing() && (m_socket->state() == QAbstractSocket::ConnectedState |
62 | || m_socket->state() == QAbstractSocket::ConnectingState)); |
63 | } |
64 | |
65 | void TcpClientIo::onError(QAbstractSocket::SocketError error) |
66 | { |
67 | qCDebug(QT_REMOTEOBJECT) << "onError" << error; |
68 | |
69 | switch (error) { |
70 | case QAbstractSocket::HostNotFoundError: //Host not there, wait and try again |
71 | case QAbstractSocket::ConnectionRefusedError: |
72 | case QAbstractSocket::NetworkError: |
73 | emit shouldReconnect(this); |
74 | break; |
75 | case QAbstractSocket::AddressInUseError: |
76 | //... TODO error reporting |
77 | break; |
78 | default: |
79 | break; |
80 | } |
81 | } |
82 | |
83 | void TcpClientIo::onStateChanged(QAbstractSocket::SocketState state) |
84 | { |
85 | if (state == QAbstractSocket::ClosingState && !isClosing()) { |
86 | m_socket->abort(); |
87 | emit shouldReconnect(this); |
88 | } |
89 | if (state == QAbstractSocket::ConnectedState) |
90 | initializeDataStream(); |
91 | } |
92 | |
93 | |
94 | TcpServerIo::TcpServerIo(QTcpSocket *conn, QObject *parent) |
95 | : QtROServerIoDevice(parent), m_connection(conn) |
96 | { |
97 | m_connection->setParent(this); |
98 | connect(sender: conn, signal: &QIODevice::readyRead, context: this, slot: &QtROServerIoDevice::readyRead); |
99 | connect(sender: conn, signal: &QAbstractSocket::disconnected, context: this, slot: &QtROServerIoDevice::disconnected); |
100 | } |
101 | |
102 | QIODevice *TcpServerIo::connection() const |
103 | { |
104 | return m_connection; |
105 | } |
106 | |
107 | void TcpServerIo::doClose() |
108 | { |
109 | m_connection->disconnectFromHost(); |
110 | } |
111 | |
112 | |
113 | |
114 | TcpServerImpl::TcpServerImpl(QObject *parent) |
115 | : QConnectionAbstractServer(parent) |
116 | { |
117 | connect(sender: &m_server, signal: &QTcpServer::newConnection, context: this, slot: &QConnectionAbstractServer::newConnection); |
118 | } |
119 | |
120 | TcpServerImpl::~TcpServerImpl() |
121 | { |
122 | close(); |
123 | } |
124 | |
125 | QtROServerIoDevice *TcpServerImpl::configureNewConnection() |
126 | { |
127 | if (!m_server.isListening()) |
128 | return nullptr; |
129 | |
130 | return new TcpServerIo(m_server.nextPendingConnection(), this); |
131 | } |
132 | |
133 | bool TcpServerImpl::hasPendingConnections() const |
134 | { |
135 | return m_server.hasPendingConnections(); |
136 | } |
137 | |
138 | QUrl TcpServerImpl::address() const |
139 | { |
140 | return m_originalUrl; |
141 | } |
142 | |
143 | bool TcpServerImpl::listen(const QUrl &address) |
144 | { |
145 | QHostAddress host(address.host()); |
146 | if (host.isNull()) { |
147 | if (address.host().isEmpty()) { |
148 | host = QHostAddress::Any; |
149 | } else { |
150 | qCWarning(QT_REMOTEOBJECT) << address.host() << " is not an IP address, trying to resolve it" ; |
151 | QHostInfo info = QHostInfo::fromName(name: address.host()); |
152 | if (info.addresses().isEmpty()) |
153 | host = QHostAddress::Any; |
154 | else |
155 | host = info.addresses().constFirst(); |
156 | } |
157 | } |
158 | |
159 | bool ret = m_server.listen(address: host, port: quint16(address.port())); |
160 | if (ret) { |
161 | m_originalUrl.setScheme(QLatin1String("tcp" )); |
162 | m_originalUrl.setHost(host: m_server.serverAddress().toString()); |
163 | m_originalUrl.setPort(m_server.serverPort()); |
164 | } |
165 | return ret; |
166 | } |
167 | |
168 | QAbstractSocket::SocketError TcpServerImpl::serverError() const |
169 | { |
170 | return m_server.serverError(); |
171 | } |
172 | |
173 | void TcpServerImpl::close() |
174 | { |
175 | m_server.close(); |
176 | } |
177 | |
178 | QT_END_NAMESPACE |
179 | |