TTKMusicPlayer  4.2.0.0
TTKMusicPlayer imitates Kugou UI, the music player uses of qmmp core library based on Qt for windows and linux
ttklocalpeer.cpp
Go to the documentation of this file.
1 #include "ttklocalpeer.h"
2 #include "ttklockedfile.h"
3 
4 #include <QCoreApplication>
5 #include <QDataStream>
6 #include <QTime>
7 #include <QLocalServer>
8 #include <QLocalSocket>
9 #include <QDir>
10 
11 #if TTK_QT_VERSION_CHECK(5,0,0)
12 # include <QRegularExpression>
13 #endif
14 
15 #if defined(Q_OS_WIN)
16 # include <QLibrary>
17 # define WIN32_LEAN_AND_MEAN
18 # include <qt_windows.h>
19 typedef BOOL (WINAPI *PProcessIdToSessionId)(DWORD, DWORD*);
20 static PProcessIdToSessionId pProcessIdToSessionId = 0;
21 #endif
22 #if defined(Q_OS_UNIX)
23 # include <sys/types.h>
24 # include <time.h>
25 # include <unistd.h>
26 #endif
27 
28 namespace TTKLockedPrivate {
29 #include "ttklockedfile.cpp"
30 #if defined(Q_OS_WIN)
31 # include "ttklockedfile_win.cpp"
32 #else
33 # include "ttklockedfile_unix.cpp"
34 #endif
35 }
36 
40 class TTKLocalPeerPrivate : public TTKPrivate<TTKLocalPeer>
41 {
42 public:
43  TTKLocalPeerPrivate() noexcept;
44  ~TTKLocalPeerPrivate() noexcept;
45 
46  QString m_id;
47  QString m_socketName;
48  QLocalServer *m_server;
49  TTKLockedPrivate::TTKLockedFile m_lockFile;
50  static const char *m_ack;
51 };
52 const char *TTKLocalPeerPrivate::m_ack = "ack";
53 
55  : m_server(nullptr)
56 {
57 
58 }
59 
61 {
62  delete m_server;
63 }
64 
65 
66 
67 TTKLocalPeer::TTKLocalPeer(QObject *parent, const QString &id)
68  : QObject(parent)
69 {
72 
73  QString prefix = d->m_id = id;
74  if(prefix.isEmpty())
75  {
76  d->m_id = QCoreApplication::applicationFilePath();
77 #if defined(Q_OS_WIN)
78  d->m_id = d->m_id.toLower();
79 #endif
80  prefix = d->m_id.section(QLatin1Char('/'), -1);
81  }
82 #if TTK_QT_VERSION_CHECK(5,0,0)
83  prefix.remove(QRegularExpression("[^a-zA-Z]"));
84 #else
85  prefix.remove(QRegExp("[^a-zA-Z]"));
86 #endif
87  prefix.truncate(6);
88 
89  const QByteArray &idc = d->m_id.toUtf8();
90 #if TTK_QT_VERSION_CHECK(6,0,0)
91  quint16 idNum = qChecksum(QByteArrayView(idc.constData(), idc.length()));
92 #else
93  quint16 idNum = qChecksum(idc.constData(), idc.length());
94 #endif
95  d->m_socketName = QLatin1String("qtsingleapp-") + prefix + QLatin1Char('-') + QString::number(idNum, 16);
96 
97 #if defined(Q_OS_WIN)
98  if(!pProcessIdToSessionId)
99  {
100  QLibrary lib("kernel32");
101  pProcessIdToSessionId = (PProcessIdToSessionId)lib.resolve("ProcessIdToSessionId");
102  }
103  if(pProcessIdToSessionId)
104  {
105  DWORD sessionId = 0;
106  pProcessIdToSessionId(GetCurrentProcessId(), &sessionId);
107  d->m_socketName += QLatin1Char('-') + QString::number(sessionId, 16);
108  }
109 #else
110  d->m_socketName += QLatin1Char('-') + QString::number(::getuid(), 16);
111 #endif
112 
113  d->m_server = new QLocalServer(this);
114  const QString &lockName = QDir(QDir::tempPath()).absolutePath() + QLatin1Char('/') + d->m_socketName + QLatin1String("-lockfile");
115  d->m_lockFile.setFileName(lockName);
116  if(!d->m_lockFile.open(QIODevice::ReadWrite))
117  {
118  TTK_WARN_STREAM("Application: lock file open failed");
119  }
120 }
121 
123 {
125  if(d->m_lockFile.isLocked())
126  {
127  return false;
128  }
129 
130  if(!d->m_lockFile.lock(TTKLockedPrivate::TTKLockedFile::WriteLock, false))
131  {
132  return true;
133  }
134 
135  bool res = d->m_server->listen(d->m_socketName);
136 #if defined(Q_OS_UNIX) && TTK_QT_VERSION_CHECK(4,5,0)
137  // ### Workaround
138  if(!res && d->m_server->serverError() == QAbstractSocket::AddressInUseError)
139  {
140  QFile::remove(QDir::cleanPath(QDir::tempPath()) + QLatin1Char('/') + d->m_socketName);
141  res = d->m_server->listen(d->m_socketName);
142  }
143 #endif
144  if(!res)
145  {
146  TTK_WARN_STREAM("Application: listen on local socket failed, " << qPrintable(d->m_server->errorString()));
147  }
148 
149  connect(d->m_server, SIGNAL(newConnection()), SLOT(receiveConnection()));
150  return false;
151 }
152 
153 bool TTKLocalPeer::sendMessage(const QString &message, int timeout) const
154 {
155  TTK_D(const TTKLocalPeer);
156  if(!isClient())
157  {
158  return false;
159  }
160 
161  QLocalSocket socket;
162  bool connOk = false;
163  for(int i = 0; i < 2; ++i)
164  {
165  // Try twice, in case the other instance is just starting up
166  socket.connectToServer(d->m_socketName);
167  connOk = socket.waitForConnected(timeout / 2);
168  if(connOk || i)
169  {
170  break;
171  }
172 
173  constexpr int ms = 250;
174 #if defined(Q_OS_WIN)
175  Sleep(DWORD(ms));
176 #else
177  struct timespec ts = { ms / 1000, (ms % 1000) * 1000 * 1000 };
178  nanosleep(&ts, nullptr);
179 #endif
180  }
181  if(!connOk)
182  {
183  return false;
184  }
185 
186  const QByteArray uMsg(message.toUtf8());
187  QDataStream ds(&socket);
188  ds.writeBytes(uMsg.constData(), uMsg.length());
189 
190  bool res = socket.waitForBytesWritten(timeout);
191  if(res)
192  {
193  res &= socket.waitForReadyRead(timeout); // wait for ack
194  if(res)
195  {
196  res &= (socket.read(qstrlen(d->m_ack)) == d->m_ack);
197  }
198  }
199  return res;
200 }
201 
203 {
204  TTK_D(const TTKLocalPeer);
205  return d->m_id;
206 }
207 
209 {
211  QLocalSocket *socket = d->m_server->nextPendingConnection();
212  if(!socket)
213  {
214  return;
215  }
216 
217  while(true)
218  {
219  if(socket->state() == QLocalSocket::UnconnectedState)
220  {
221  TTK_WARN_STREAM("QtLocalPeer: Peer disconnected");
222  delete socket;
223  return;
224  }
225 
226  if(socket->bytesAvailable() >= qint64(sizeof(quint32)))
227  {
228  break;
229  }
230  socket->waitForReadyRead();
231  }
232 
233  QDataStream ds(socket);
234  QByteArray uMsg;
235  quint32 remaining;
236  ds >> remaining;
237  uMsg.resize(remaining);
238 
239  int got = 0;
240  char *uMsgBuf = uMsg.data();
241  do
242  {
243  got = ds.readRawData(uMsgBuf, remaining);
244  remaining -= got;
245  uMsgBuf += got;
246  } while(remaining && got >= 0 && socket->waitForReadyRead(2000));
247 
248  if(got < 0)
249  {
250  TTK_WARN_STREAM("TTKLocalPeer: Message reception failed " << socket->errorString().toLatin1().constData());
251  delete socket;
252  return;
253  }
254 
255  const QString message(QString::fromUtf8(uMsg));
256  socket->write(d->m_ack, qstrlen(d->m_ack));
257  socket->waitForBytesWritten(1000);
258  socket->waitForDisconnected(1000); // make sure client reads ack
259  delete socket;
260 
261  Q_EMIT messageReceived(message); //### (might take a long time to return)
262 }
The namespace of locked private.
TTKLocalPeer(QObject *parent=nullptr, const QString &id={})
void receiveConnection()
The class of the ttk local peer.
Definition: ttklocalpeer.h:29
QString applicationId() const noexcept
The class of the ttk local peer private.
~TTKLocalPeerPrivate() noexcept
#define qPrintable(s)
Definition: ttkqtglobal.h:35
static const char * m_ack
#define TTK_WARN_STREAM(msg)
Definition: ttklogger.h:75
TTKLockedPrivate::TTKLockedFile m_lockFile
QLocalServer * m_server
TTKLocalPeerPrivate() noexcept
bool isClient() const
#define TTK_INIT_PRIVATE(Class)
Definition: ttkprivate.h:33
#define const
Definition: zconf.h:233
The class of the ttk private base.
Definition: ttkprivate.h:48
bool sendMessage(const QString &message, int timeout) const
#define TTK_D(Class)
Definition: ttkprivate.h:41