我创建了这个服务器类,它在新连接进入时启动一个线程。在某些情况下它可以运行,但它不是很稳定。我想解决它破裂的地方。我的调试器告诉我一些关于qmutex的事情。如果有人能发现问题。 ty
它通过信号和插槽连接父母,并且还获取数据。
以下是标题:
#ifndef FORTUNESERVER_H
#define FORTUNESERVER_H
#include <QStringList>
#include <QTcpServer>
#include <QThread>
#include <QTcpSocket>
#include <string>
using namespace std;
class FortuneServer : public QTcpServer
{
Q_OBJECT
public:
FortuneServer(QObject *parent = 0);
public slots:
void procesServerString(string serverString);
void getStringToThread(string serverString);
protected:
void incomingConnection(int socketDescriptor);
private:
QStringList fortunes;
signals:
void procesServerStringToParent(string serverString);
void getStringToThreadSignal(string serverString);
};
class FortuneThread : public QObject
{
Q_OBJECT
public:
FortuneThread(int socketDescriptor, QObject *parent);
public slots:
void getString();
void sendString(string sendoutString);
signals:
void error(QTcpSocket::SocketError socketError);
void fromThreadString(string serverString);
void finished();
private:
int socketDescriptor;
QString text;
QTcpSocket tcpSocket;
};
#endif
和cc:
#include <stdlib.h>
#include <QtNetwork>
#include "MeshServer.hh"
#include <iostream>
#include "TableView.hh"
using namespace std;
FortuneServer::FortuneServer(QObject *parent)
: QTcpServer(parent)
{
}
void FortuneServer::procesServerString(string serverString){
emit procesServerStringToParent(serverString);
}
void FortuneServer::getStringToThread(string serverString){
emit getStringToThreadSignal(serverString);
}
void FortuneServer::incomingConnection(int socketDescriptor)
{
FortuneThread *serverthread = new FortuneThread(socketDescriptor, this);
//connect(&serverthread, SIGNAL(finished()), &serverthread, SLOT(deleteLater()));
QThread* thread = new QThread;
serverthread->moveToThread(thread);
connect(thread, SIGNAL(started()), serverthread, SLOT(getString()));
connect(serverthread, SIGNAL(fromThreadString(string)), this, SLOT(procesServerString(string)));
connect(this, SIGNAL(getStringToThreadSignal(string)), serverthread, SLOT(sendString(string)));
connect(serverthread, SIGNAL(finished()), thread, SLOT(quit()));
connect(serverthread, SIGNAL(finished()), serverthread, SLOT(deleteLater()));
connect(serverthread, SIGNAL(finished()), thread, SLOT(deleteLater()));
thread->start();
}
FortuneThread::FortuneThread(int socketDescriptor, QObject *parent)
: QObject(parent), socketDescriptor(socketDescriptor)
{
}
void FortuneThread::getString()
{
if (!tcpSocket.setSocketDescriptor(socketDescriptor)) {
emit error(tcpSocket.error());
cout<<"socket error"<<endl;
return;
}
//in part
if(!tcpSocket.waitForReadyRead(10000)){
emit finished();
return;
}
int joj = tcpSocket.bytesAvailable();
char inbuffer[1024];
tcpSocket.read(inbuffer,1024);
string instring;
instring = inbuffer;
instring.resize(joj);
emit fromThreadString(instring);
}
void FortuneThread::sendString(string sendoutString)
{
//out part
char buffer[1024];
int buffer_len = 1024;
int bytecount;
memset(buffer, '\0', buffer_len);
string outstring = sendoutString;
int TempNumOne= (int)outstring.size();
for (int a=0;a<TempNumOne;a++)
{
buffer[a]=outstring[a];
}
QByteArray block;
block = buffer;
tcpSocket.write(block);
tcpSocket.disconnectFromHost();
tcpSocket.waitForDisconnected();
emit finished();
}
这是来自父母:
//server start
QHostAddress adr;
adr.setAddress( QString("127.0.0.1") );
adr.toIPv4Address();
quint16 port = 1101;
if (!server.listen( adr, port)) {
QMessageBox::critical(this, tr("CR_bypasser"),
tr("Unable to start the server: %1.")
.arg(server.errorString()));
close();
return;
}
QString ipAddress;
ipAddress = server.serverAddress().toString();
statusLabel->setText(tr("The server is running on\n\nIP: %1\nport: %2\n\n"
"Run the Fortune Client example now.")
.arg(ipAddress).arg(server.serverPort()));
connect (&server, SIGNAL(procesServerStringToParent(string)), this, SLOT(procesServerString(string)));
connect (this, SIGNAL(StringToServer(string)), &server, SLOT(getStringToThread(string)));
编辑:我想做的事情:
我有一个客户端(游戏引擎(Cryengine)的一部分),我用它来发送一串游戏坐标和一些其他东西,就像我之前给出的链接一样。这没问题。我在“127.0.0.1”端口1101上获取数据。现在我只需要在我自己的程序中评估这些数据,该程序有这个TableView类,我可以在其中收集从字符串中获取的坐标,从坐标调用一些数据,然后将这个新字符串通过服务器返回给gameengine。在游戏中,我将点击对象获取他们的coor。,从中创建一个字符串(包含coor,entityid等等),将此字符串发送到服务器,从TableView返回调用信息。我只需要这种单向流只有一个发送字符串的客户端。我不确定recv(hsock,buffer,buffer_len,0),我想在游戏中负责字符串发送的节点会等待返回字符串吗?这是我的第一个节目之一,我真的很困惑......
答案 0 :(得分:6)
您提供的代码是货物编码的典范:您做了各种不必要的事情,显然是希望解决问题。
代码存在大量问题,但我认为崩溃的原因是:tcpSocket.write(block)
不会在网络上发送一个以零结尾的字符串。该块是零终止的,但是对字节数组的赋值不会将此零终止添加到QByteArray的size()
。下面的代码打印1,即使在字节数组的内容中内部有一个零终止字节。
QByteArray arr = "a";
qDebug("len=%d", arr.size());
接收代码期望零终止,但永远不会收到它。然后,继续为std::string
:
string instring;
instring = inbuffer;
instring.resize(joj);
随后的调整大小是货物崇拜:您std::string & std::string::operator=(const char*)
已经读过您的缓冲区后,您正试图解决问题。
不这意味着修复这是正确的方法。一点也不。正确的方法是删除您编写的代码并正确执行,没有大量不必要的帮助。
你陷入了相信魔法的陷阱,在各种论坛中无休止地延续下去。
线程不是神奇的对象,你可以只应用于那里的任何问题,希望它们有所帮助。我不知道是什么让人们认为线程是神奇的,但经验法则是:如果有人告诉你&#34;哦,你应该尝试线程&#34;,他们很可能是错的。如果他们告诉我们关于网络,他们几乎从不对,他们没有帮助,他们根本不理解你的问题(你似乎也没有)。通常情况下,除非您清楚地了解您的问题,否则线程将不帮助。 Qt的网络系统是异步的:如果你不使用waitxxxx()
功能,它不会阻止代码的执行。顺便说一下,你不应该使用它们,所以这里一切都很好。不需要太多的线程。
因此,完全没必要为每个传入连接启动一个新线程。它会降低服务器的性能 - 特别是如果服务器执行简单的处理,因为您为每个连接添加了上下文切换和线程创建/拆除的开销。您希望系统中每个核心少于2个线程,因此使用QThread::idealThreadCount()
表示池中的线程数将是一个很好的起点。
由于您仅使用网络线程接收数据,然后发出fromThreadString(string)
信号,因此您也剥夺了自己的线程优势。我假设信号被发送到您的应用程序的主线程。现在这很愚蠢,因为从网络套接字接收一堆字节是非常微不足道的。你的线程不做任何工作,他们所做的所有工作都浪费在他们的创建和删除上。
下面的代码是一个简单的示例,说明了如何正确地使用Qt API来实现客户端 - 服务器系统,该系统以循环方式在物理核心上分配工作。它应该表现得很好。 Qt中包含的Fortune客户端示例非常不幸,因为它确实是错误的处理方式。
人们会注意到的是:
这并非完全无足轻重。 Qt可能会更有帮助,但不是。
客户端和发件人都从线程池移入线程。
不会删除已断开连接的客户端,而只会将其返回到客户端列表 保持在胎面池。当客户端被要求时,它们会被重用。
QThread不是源于。 QTcpServer仅用于访问套接字句柄。
不使用名称以wait()
开头的函数。一切都是异步处理的。
ThreadPool为客户端的newConnection(int)
插槽保留一个查找的QMetaMethod。这比使用QMetaObject::invokeMethod()
更快,因为它必须每次查找。
主线程中运行的计时器通过删除第一个发送者来设置信号槽链。每个发件人&#39;删除会触发删除下一个。最后,最后一个发件人会在线程池中设置quit()
个插槽。当所有线程确实完成时,后者会发出finished()
信号。
#include <QtCore/QCoreApplication>
#include <QtNetwork/QTcpServer>
#include <QtNetwork/QTcpSocket>
#include <QtCore/QQueue>
#include <QtCore/QThread>
#include <QtCore/QTimer>
#include <QtCore/QMetaMethod>
// Processes data on a socket connection
class Client : public QObject
{
Q_OBJECT
public:
Client(QObject* parent = 0) : QObject(parent), socket(new QTcpSocket(this))
{
connect(socket, SIGNAL(readyRead()), SLOT(newData()));
connect(socket, SIGNAL(stateChanged(QAbstractSocket::SocketState)),
SLOT(newState(QAbstractSocket::SocketState)));
qDebug("Client()");
}
~Client() { qDebug("~Client()"); }
signals:
void done();
public slots:
void newConnection(int descriptor) {
socket->setSocketDescriptor(descriptor);
}
private slots:
void newData() {
QByteArray data = socket->readAll();
if (0) qDebug("got %d bytes", data.size());
if (0) qDebug("got a string %s", data.constData());
// here we can process the data
}
void newState(QAbstractSocket::SocketState state) {
qDebug("client new state %d", state);
if (state == QAbstractSocket::UnconnectedState) { emit done(); }
}
protected:
QTcpSocket* socket;
int descriptor;
};
// Connects to a client and sends data to it
class Sender : public QObject
{
Q_OBJECT
public:
Sender(const QString & address, quint16 port, QObject * parent = 0) :
QObject(parent), socket(new QTcpSocket(this)),
bytesInFlight(0), maxBytesInFlight(65536*8)
{
connect(socket, SIGNAL(stateChanged(QAbstractSocket::SocketState)),
SLOT(newState(QAbstractSocket::SocketState)));
connect(socket, SIGNAL(bytesWritten(qint64)), SLOT(sentData(qint64)));
socket->connectToHost(address, port);
qDebug("Sender()");
}
~Sender() { qDebug("~Sender()"); }
protected:
// sends enough data to keep a maximum number of bytes in flight
void sendData() {
qint64 n = maxBytesInFlight - bytesInFlight;
if (n <= 0) return;
bytesInFlight += n;
socket->write(QByteArray(n, 44)); // 44 is the answer, after all
}
protected slots:
void sentData(qint64 n) {
bytesInFlight -= n;
Q_ASSERT(bytesInFlight >= 0);
sendData();
}
void newState(QAbstractSocket::SocketState state) {
qDebug("sender new state %d", state);
if (state == QAbstractSocket::ConnectedState) sendData();
}
protected:
QTcpSocket* socket;
qint64 bytesInFlight;
qint64 maxBytesInFlight;
};
// Keeps track of threads and client objects
class ThreadPool : public QTcpServer
{
Q_OBJECT
public:
ThreadPool(QObject* parent = 0) : QTcpServer(parent), nextThread(0) {
for (int i=0; i < QThread::idealThreadCount(); ++i) {
QThread * thread = new QThread(this);
connect(thread, SIGNAL(finished()), SLOT(threadDone()));
thread->start();
threads << thread;
}
const QMetaObject & mo = Client::staticMetaObject;
int idx = mo.indexOfMethod("newConnection(int)");
Q_ASSERT(idx>=0);
method = mo.method(idx);
}
void poolObject(QObject* obj) const {
if (nextThread >= threads.count()) nextThread = 0;
QThread* thread = threads.at(nextThread);
obj->moveToThread(thread);
}
protected:
void incomingConnection(int descriptor) {
Client * client;
if (threads.isEmpty()) return;
if (! clients.isEmpty()) {
client = clients.dequeue();
} else {
client = new Client();
connect(client, SIGNAL(done()), SLOT(clientDone()));
}
poolObject(client);
method.invoke(client, Q_ARG(int, descriptor));
}
signals:
void finished();
public slots:
void quit() {
foreach (QThread * thread, threads) { thread->quit(); }
}
private slots:
void clientDone() {
clients.removeAll(qobject_cast<Client*>(sender()));
}
void threadDone() {
QThread * thread = qobject_cast<QThread*>(sender());
if (threads.removeAll(thread)) delete thread;
if (threads.isEmpty()) emit finished();
}
private:
QList<QThread*> threads;
QQueue<Client*> clients;
QMetaMethod method;
mutable int nextThread;
};
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
ThreadPool server;
if (!server.listen(QHostAddress::Any, 1101)) qCritical("cannot establish a listening server");
const int senderCount = 10;
Sender *prevSender = 0, *firstSender = 0;
for (int i = 0; i < senderCount; ++ i) {
Sender * sender = new Sender("localhost", server.serverPort());
server.poolObject(sender);
if (!firstSender) firstSender = sender;
if (prevSender) sender->connect(prevSender, SIGNAL(destroyed()), SLOT(deleteLater()));
prevSender = sender;
}
QTimer::singleShot(3000, firstSender, SLOT(deleteLater())); // run for 3s
server.connect(prevSender, SIGNAL(destroyed()), SLOT(quit()));
qApp->connect(&server, SIGNAL(finished()), SLOT(quit()));
// Deletion chain: timeout deletes first sender, then subsequent senders are deleted,
// finally the last sender tells the thread pool to quit. Finally, the thread pool
// quits the application.
return a.exec();
}
#include "main.moc"
根据您的解释,您启动游戏引擎并创建与localhost上某个端口的连接。您的Qt程序应该接受端口1101上的连接,接收一些字符串,处理它们,然后将它们发回。
修改代码以接受固定端口号上的连接。所有数据处理(包括发回响应)都必须从newData()
插槽完成。如果计算非常复杂,您还可以将该数据传递给其他线程。复杂的意思是成千上万的操作,如加法和乘法,或成千上万的触发操作。
Sender
类就是一个例子。当然,您的游戏引擎会进行发送,因此您不需要发件人类。
答案 1 :(得分:0)
我得到了旧的“错误的方法”代码工作。我想这部分是错误的地方:
//removed
tcpSocket.disconnectFromHost();
tcpSocket.waitForDisconnected();
emit finished();
...
#include <stdlib.h>
#include <QtNetwork>
#include "MeshServer.hh"
#include <iostream>
#include "TableView.hh"
using namespace std;
FortuneServer::FortuneServer(QObject *parent)
: QTcpServer(parent)
{
}
void FortuneServer::procesServerString(string serverString){
emit procesServerStringToParent(serverString);
}
void FortuneServer::getStringToThread(string serverString){
emit getStringToThreadSignal(serverString);
}
void FortuneServer::incomingConnection(int socketDescriptor)
{
FortuneThread *serverthread = new FortuneThread(socketDescriptor, this);
//connect(&serverthread, SIGNAL(finished()), &serverthread, SLOT(deleteLater()));
QThread* thread = new QThread;
serverthread->moveToThread(thread);
connect(serverthread, SIGNAL(fromThreadString(string)), this, SLOT(procesServerString(string)));
connect(this, SIGNAL(getStringToThreadSignal(string)), serverthread, SLOT(sendString(string)));
connect(serverthread, SIGNAL(finished()), thread, SLOT(quit()));
connect(serverthread, SIGNAL(finished()), serverthread, SLOT(deleteLater()));
connect(serverthread, SIGNAL(finished()), thread, SLOT(deleteLater()));
thread->start();
}
FortuneThread::FortuneThread(int socketDescriptor, QObject *parent): QObject(parent), socketDescriptor(socketDescriptor)
{
if (!tcpSocket.setSocketDescriptor(socketDescriptor)) {
emit error(tcpSocket.error());
cout<<"socket error"<<endl;
emit finished();
return;
}
connect(&tcpSocket, SIGNAL(readyRead()), this, SLOT(getString()));
//connect(&tcpSocket, SIGNAL(disconnected()), this, SLOT(ondisconnected()));
}
void FortuneThread::getString()
{
int joj = tcpSocket.bytesAvailable();
if(joj==0){
tcpSocket.disconnectFromHost();
emit finished();
return;
}
char inbuffer[1024];
int buffer_len = 1024;
memset(inbuffer, '\0', buffer_len);
tcpSocket.read(inbuffer,1024);
string instring;
instring = inbuffer;
instring.resize(joj);
emit fromThreadString(instring);
}
void FortuneThread::sendString(string sendoutString)
{
char buffer2[1024];
int buffer_len = 1024;
memset(buffer2, '\0', buffer_len);
strcat(buffer2,sendoutString.c_str());
tcpSocket.write(buffer2,buffer_len);
}
void FortuneThread::ondisconnected()
{
emit finished();
}