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 | QHostAddress address(url().host()); |
49 | if (address.isNull()) { |
50 | const QList<QHostAddress> addresses = QHostInfo::fromName(name: url().host()).addresses(); |
51 | Q_ASSERT_X(addresses.size() >= 1, Q_FUNC_INFO, url().toString().toLatin1().data()); |
52 | address = addresses.first(); |
53 | } |
54 | |
55 | m_socket->connectToHost(address, port: quint16(url().port())); |
56 | } |
57 | |
58 | bool TcpClientIo::isOpen() const |
59 | { |
60 | return (!isClosing() && (m_socket->state() == QAbstractSocket::ConnectedState |
61 | || m_socket->state() == QAbstractSocket::ConnectingState)); |
62 | } |
63 | |
64 | void TcpClientIo::onError(QAbstractSocket::SocketError error) |
65 | { |
66 | qCDebug(QT_REMOTEOBJECT) << "onError" << error; |
67 | |
68 | switch (error) { |
69 | case QAbstractSocket::HostNotFoundError: //Host not there, wait and try again |
70 | case QAbstractSocket::ConnectionRefusedError: |
71 | case QAbstractSocket::NetworkError: |
72 | emit shouldReconnect(this); |
73 | break; |
74 | case QAbstractSocket::AddressInUseError: |
75 | //... TODO error reporting |
76 | break; |
77 | default: |
78 | break; |
79 | } |
80 | } |
81 | |
82 | void TcpClientIo::onStateChanged(QAbstractSocket::SocketState state) |
83 | { |
84 | if (state == QAbstractSocket::ClosingState && !isClosing()) { |
85 | m_socket->abort(); |
86 | emit shouldReconnect(this); |
87 | } |
88 | if (state == QAbstractSocket::ConnectedState) |
89 | initializeDataStream(); |
90 | } |
91 | |
92 | |
93 | TcpServerIo::TcpServerIo(QTcpSocket *conn, QObject *parent) |
94 | : QtROServerIoDevice(parent), m_connection(conn) |
95 | { |
96 | m_connection->setParent(this); |
97 | connect(sender: conn, signal: &QIODevice::readyRead, context: this, slot: &QtROServerIoDevice::readyRead); |
98 | connect(sender: conn, signal: &QAbstractSocket::disconnected, context: this, slot: &QtROServerIoDevice::disconnected); |
99 | } |
100 | |
101 | QIODevice *TcpServerIo::connection() const |
102 | { |
103 | return m_connection; |
104 | } |
105 | |
106 | void TcpServerIo::doClose() |
107 | { |
108 | m_connection->disconnectFromHost(); |
109 | } |
110 | |
111 | |
112 | |
113 | TcpServerImpl::TcpServerImpl(QObject *parent) |
114 | : QConnectionAbstractServer(parent) |
115 | { |
116 | connect(sender: &m_server, signal: &QTcpServer::newConnection, context: this, slot: &QConnectionAbstractServer::newConnection); |
117 | } |
118 | |
119 | TcpServerImpl::~TcpServerImpl() |
120 | { |
121 | close(); |
122 | } |
123 | |
124 | QtROServerIoDevice *TcpServerImpl::configureNewConnection() |
125 | { |
126 | if (!m_server.isListening()) |
127 | return nullptr; |
128 | |
129 | return new TcpServerIo(m_server.nextPendingConnection(), this); |
130 | } |
131 | |
132 | bool TcpServerImpl::hasPendingConnections() const |
133 | { |
134 | return m_server.hasPendingConnections(); |
135 | } |
136 | |
137 | QUrl TcpServerImpl::address() const |
138 | { |
139 | return m_originalUrl; |
140 | } |
141 | |
142 | bool TcpServerImpl::listen(const QUrl &address) |
143 | { |
144 | QHostAddress host(address.host()); |
145 | if (host.isNull()) { |
146 | if (address.host().isEmpty()) { |
147 | host = QHostAddress::Any; |
148 | } else { |
149 | qCWarning(QT_REMOTEOBJECT) << address.host() << " is not an IP address, trying to resolve it" ; |
150 | QHostInfo info = QHostInfo::fromName(name: address.host()); |
151 | if (info.addresses().isEmpty()) |
152 | host = QHostAddress::Any; |
153 | else |
154 | host = info.addresses().constFirst(); |
155 | } |
156 | } |
157 | |
158 | bool ret = m_server.listen(address: host, port: quint16(address.port())); |
159 | if (ret) { |
160 | m_originalUrl.setScheme(QLatin1String("tcp" )); |
161 | m_originalUrl.setHost(host: m_server.serverAddress().toString()); |
162 | m_originalUrl.setPort(m_server.serverPort()); |
163 | } |
164 | return ret; |
165 | } |
166 | |
167 | QAbstractSocket::SocketError TcpServerImpl::serverError() const |
168 | { |
169 | return m_server.serverError(); |
170 | } |
171 | |
172 | void TcpServerImpl::close() |
173 | { |
174 | m_server.close(); |
175 | } |
176 | |
177 | QT_END_NAMESPACE |
178 | |