也许这是个愚蠢的问题,实际上它很有吸引力,或者 Qt 对我来说太复杂了。 事情是这样的: 我在编写客户端-服务器应用程序时习惯了 java,它非常简单。我想在 C++ 中做同样的事情(我对 C++ 本身非常熟悉),我选择学习 Qt。我尝试用 qt 编写一些应用程序,但部分成功。
首先困扰我的是信号和槽。我知道如何在 GUI 编程中使用它们,但它让我对网络感到困惑。而且阻塞有问题。当我在 java 中调用 BufferedReader 的 readLine() 方法时,它会阻塞,直到它从套接字连接接收到行。在 Qt 中,我必须确保每次都有行可用,并在没有行时进行处理。
当我将 QSocket 的错误信号连接到我的一些自定义槽时,信号在服务器发送最后一行并关闭连接时发出,而在客户端的槽/函数中读取我从未读过最后一行。这是我目前遇到的一些问题。
当我不得不实现最简单的协议(protocol)时,插槽和检查是否有可用数据让我感到困惑。
重要部分:
我试图在互联网上找到好的例子,但问题是所有的例子都太复杂了。有没有人可以告诉我如何编写简单的客户端-服务器应用程序。服务器只接受一个客户端。客户端发送包含命令的文本行。如果命令是“ADD”或“SUB”,服务器发送“SUP”表示支持该命令。否则它发送“UNS”并关闭连接。如果客户端收到“SUP”,它会发送到更多行,其中包含要减去或添加的数字。服务器响应结果并关闭连接。
我知道 C++ 需要更多编码,但在 Java 中这只需要 5 分钟,所以用 C++ 编写它也不会花很长时间。
我相信这个例子对任何想在 Qt 中学习网络的人都非常有值(value)。
编辑: 这是我尝试制作的应用程序(如上所述): 这是服务器部分:
#ifndef TASK_H
#define TASK_H
#include <QObject>
#include <QTcpServer>
class Task : public QObject
{
Q_OBJECT
public:
Task(QObject *parent = 0) : QObject(parent) {}
public slots:
void run();
void on_newConnection();
void on_error(QAbstractSocket::SocketError);
signals:
void finished();
private:
QTcpServer server;
};
#endif // TASK_H
void Task::run()
{
connect(&server,SIGNAL(newConnection()),this,SLOT(on_newConnection()));
connect(&server,SIGNAL(acceptError(QAbstractSocket::SocketError)),this,SLOT(on_error(QAbstractSocket::SocketError)));
if(server.listen(QHostAddress::LocalHost, 9000)){
qDebug() << "listening";
}else{
qDebug() << "cannot listen";
qDebug() << server.errorString();
}
}
void Task::on_newConnection(){
std::cout << "handeling new connection...\n";
QTcpSocket* socket = server.nextPendingConnection();
QTextStream tstream(socket);
while(!socket->canReadLine()){
socket->waitForReadyRead((-1));
}
QString operation = tstream.readLine();
qDebug() << "dbg:" << operation;
if(operation != "ADD" && operation != "SUB"){
tstream << "UNS\n";
tstream.flush();
socket->disconnect();
return;
}
tstream << "SUP\n";
tstream.flush();
double op1,op2;
while(!socket->canReadLine()){
socket->waitForReadyRead((-1));
}
op1 = socket->readLine().trimmed().toDouble();
qDebug() << "op1:" << op1;
while(!socket->canReadLine()){
socket->waitForReadyRead(-1);
}
op2 = socket->readLine().trimmed().toDouble();
qDebug() << "op2:" << op2;
double r;
if(operation == "ADD"){
r = op1 + op2;
}else{
r = op1 - op2;
}
tstream << r << "\n";
tstream.flush();
qDebug() << "result is: " << r;
socket->disconnect();
}
void Task::on_error(QAbstractSocket::SocketError ){
qDebug() << "server error";
server.close();
}
这是客户端( header 与服务器的 header 类似,所以我不会发布):
void Task::run()
{
QTcpSocket socket;
std::string temp;
socket.connectToHost(QHostAddress::LocalHost,9000);
if(socket.waitForConnected(-1))
qDebug() << "connected";
else {
qDebug() << "cannot connect";
return;
}
QTextStream tstream(&socket);
QString op;
std::cout << "operation: ";
std::cin >> temp;
op = temp.c_str();
tstream << op << "\n";
tstream.flush();
qDebug() << "dbg:" << op << "\n";
while(!socket.canReadLine()){
socket.waitForReadyRead(-1);
}
QString response = tstream.readLine();
qDebug() << "dbg:" << response;
if(response == "SUP"){
std::cout << "operand 1: ";
std::cin >> temp;
op = temp.c_str();
tstream << op + "\n";
std::cout << "operand 2: ";
std::cin >> temp;
op = temp.c_str();
tstream << op + "\n";
tstream.flush();
while(!socket.canReadLine()){
socket.waitForReadyRead(-1);
}
QString result = tstream.readLine();
std::cout << qPrintable("result is: " + result);
}else if(response == "UNS"){
std::cout << "unsupported operatoion.";
}else{
std::cout << "unknown error.";
}
emit finished();
}
谢谢。 对不起我的英语,可能还有愚蠢:)
最佳答案
使用 Qt 联网并不难。
两点之间的通信由一个类处理;在 TCP/IP 的情况下,这将是 QTcpSocket 类。客户端和服务器都将与 QTcpSocket 对象进行通信。
与服务器的唯一区别是您从 QTcpServer 对象开始并调用 listen() 来等待连接...
QTcpServer* m_pTcpServer = new QTcpServer
//create the address that the server will listen on
QHostAddress addr(QHostAddress::LocalHost); // assuming local host (127.0.0.1)
// start listening
bool bListening = m_pServer->listen(addr, _PORT); //_PORT defined as whatever port you want to use
当服务器接收到来自客户端 QTcpSocket 的连接时,它会通过 newConnection 信号通知您,因此假设您已经连接到您自己类中的套接字以接收该信号,我们可以获取服务端的QTcpSocket对象来与客户端通信...
QTcpSocket* pServerSocket = m_pServer->nextPendingConnection();
服务器将为每个建立的连接接收一个 QTcpSocket 对象。服务器套接字现在可用于将数据发送到客户端套接字,使用 write 方法...
pServerSocket->write("Hello!");
当套接字(客户端或服务器)接收到数据时,它会发出readyRead 信号。因此,假设您已经连接到套接字的 readyRead 信号,槽函数可以检索数据...
QString msg = pSocket->readAll();
您需要的其他代码是处理连接、断开连接和错误信号,您应该连接相关插槽以接收这些通知。
确保仅在知道已建立连接时才发送数据。通常,我会让服务器接收连接并将“hello”消息发送回客户端。一旦客户端收到消息,它就知道它可以发送到服务器。
当任何一方断开连接时,剩余的一方将收到断开连接信号并可以采取适当的行动。
对于客户端,它只有一个 QTcpSocket 对象,在调用 connectToHost 之后,如果连接成功,您将收到一个connected 信号,或者错误信号。
最后,如果您只是想在同一台机器上的进程之间进行通信,您可以以相同的方式使用 QLocalServer 和 QLocalSocket。
关于c++ - 如何在Qt中编写客户端-服务器应用程序和实现简单的协议(protocol),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18722925/
出于纯粹的兴趣,我很好奇如何按顺序创建PI,而不是在过程结果之后生成数字,而是让数字在过程本身生成时显示。如果是这种情况,那么数字可以自行产生,我可以对以前看到的数字实现垃圾收集,从而创建一个无限系列。结果只是在Pi系列之后每秒生成一个数字。这是我通过互联网筛选的结果:这是流行的计算机友好算法,类机器算法:defarccot(x,unity)xpow=unity/xn=1sign=1sum=0loopdoterm=xpow/nbreakifterm==0sum+=sign*(xpow/n)xpow/=x*xn+=2sign=-signendsumenddefcalc_pi(digits
我想安装一个带有一些身份验证的私有(private)Rubygem服务器。我希望能够使用公共(public)Ubuntu服务器托管内部gem。我读到了http://docs.rubygems.org/read/chapter/18.但是那个没有身份验证-如我所见。然后我读到了https://github.com/cwninja/geminabox.但是当我使用基本身份验证(他们在他们的Wiki中有)时,它会提示从我的服务器获取源。所以。如何制作带有身份验证的私有(private)Rubygem服务器?这是不可能的吗?谢谢。编辑:Geminabox问题。我尝试“捆绑”以安装新的gem..
如何在buildr项目中使用Ruby?我在很多不同的项目中使用过Ruby、JRuby、Java和Clojure。我目前正在使用我的标准Ruby开发一个模拟应用程序,我想尝试使用Clojure后端(我确实喜欢功能代码)以及JRubygui和测试套件。我还可以看到在未来的不同项目中使用Scala作为后端。我想我要为我的项目尝试一下buildr(http://buildr.apache.org/),但我注意到buildr似乎没有设置为在项目中使用JRuby代码本身!这看起来有点傻,因为该工具旨在统一通用的JVM语言并且是在ruby中构建的。除了将输出的jar包含在一个独特的、仅限ruby
我正在使用的第三方API的文档状态:"[O]urAPIonlyacceptspaddedBase64encodedstrings."什么是“填充的Base64编码字符串”以及如何在Ruby中生成它们。下面的代码是我第一次尝试创建转换为Base64的JSON格式数据。xa=Base64.encode64(a.to_json) 最佳答案 他们说的padding其实就是Base64本身的一部分。它是末尾的“=”和“==”。Base64将3个字节的数据包编码为4个编码字符。所以如果你的输入数据有长度n和n%3=1=>"=="末尾用于填充n%
我想用ruby编写一个小的命令行实用程序并将其作为gem分发。我知道安装后,Guard、Sass和Thor等某些gem可以从命令行自行运行。为了让gem像二进制文件一样可用,我需要在我的gemspec中指定什么。 最佳答案 Gem::Specification.newdo|s|...s.executable='name_of_executable'...endhttp://docs.rubygems.org/read/chapter/20 关于ruby-在Ruby中编写命令行实用程序
exe应该在我打开页面时运行。异步进程需要运行。有什么方法可以在ruby中使用两个参数异步运行exe吗?我已经尝试过ruby命令-system()、exec()但它正在等待过程完成。我需要用参数启动exe,无需等待进程完成是否有任何rubygems会支持我的问题? 最佳答案 您可以使用Process.spawn和Process.wait2:pid=Process.spawn'your.exe','--option'#Later...pid,status=Process.wait2pid您的程序将作为解释器的子进程执行。除
我的瘦服务器配置了nginx,我的ROR应用程序正在它们上运行。在我发布代码更新时运行thinrestart会给我的应用程序带来一些停机时间。我试图弄清楚如何优雅地重启正在运行的Thin实例,但找不到好的解决方案。有没有人能做到这一点? 最佳答案 #Restartjustthethinserverdescribedbythatconfigsudothin-C/etc/thin/mysite.ymlrestartNginx将继续运行并代理请求。如果您将Nginx设置为使用多个上游服务器,例如server{listen80;server
鉴于我有以下迁移:Sequel.migrationdoupdoalter_table:usersdoadd_column:is_admin,:default=>falseend#SequelrunsaDESCRIBEtablestatement,whenthemodelisloaded.#Atthispoint,itdoesnotknowthatusershaveais_adminflag.#Soitfails.@user=User.find(:email=>"admin@fancy-startup.example")@user.is_admin=true@user.save!ende
我正在为一个项目制作一个简单的shell,我希望像在Bash中一样解析参数字符串。foobar"helloworld"fooz应该变成:["foo","bar","helloworld","fooz"]等等。到目前为止,我一直在使用CSV::parse_line,将列分隔符设置为""和.compact输出。问题是我现在必须选择是要支持单引号还是双引号。CSV不支持超过一个分隔符。Python有一个名为shlex的模块:>>>shlex.split("Test'helloworld'foo")['Test','helloworld','foo']>>>shlex.split('Test"
我实际上是在尝试使用RVM在我的OSX10.7.5上更新ruby,并在输入以下命令后:rvminstallruby我得到了以下回复:Searchingforbinaryrubies,thismighttakesometime.Checkingrequirementsforosx.Installingrequirementsforosx.Updatingsystem.......Errorrunning'requirements_osx_brew_update_systemruby-2.0.0-p247',pleaseread/Users/username/.rvm/log/138121