文档章节

Qt网络服务端程序

小果汁儿
 小果汁儿
发布于 2016/10/04 17:04
字数 2248
阅读 72
收藏 1
点赞 0
评论 0

  Qt中的各种技术都是异步的,所以像socket通信的这些玩意都是异步的,不需要自己开线程处理。包括串口,网口通信,只需要将接收数据的函数和信号连接上就可以了,当有数据时,会自动触发执行函数。
  这篇文章主要为刚入门的新手做个教程,非常简单的一个教程,也是一个功能比较完整的demo。新建一个工程,这里起名叫service,是一个控制台应用程序,由于不需要界面,所以就直接建控制台程序。创建好之后修改项目文件Service.pro,在"QT += "这一行的后面,空格,添加network,表示该项目中钥匙用网络模块。如下:

QT += core network                 //这里添加 network表示使用网络模块
QT -= gui

CONFIG += c++11

TARGET = Service
CONFIG += console
CONFIG -= app_bundle

TEMPLATE = app

SOURCES += main.cpp \
    userclient.cpp \
    dataserver.cpp

HEADERS += \
    userclient.h \
    dataserver.h

1、先建一个类,用来表示一个连接上来的客户端(可以没有,只需要socket描述符创建连接即可,但是抽象成一个客户端用户,更符合面向对象的思想),起个类名叫UserClient,文件名userclient.h和userclient.cpp,注意类名单词首字母大写,文件名自动小写,C++文件名一般都小写。

/*************************************************************************
*文件名:userclent.h
*类名:UserClient
*描述:表示一个客户端连接
*************************************************************************/
#ifndef USERCLIENT_H
#define USERCLIENT_H

#include <QObject>
#include <QTcpSocket>
//该类是客户端的抽象和描述,用来创建客户端连接收发和处理客户端数据
class UserClient : public QObject
{
  Q_OBJECT
public:
  //构造函数,需要有socket描述符才能创建客户端
  explicit UserClient(qintptr des,QObject *parent = 0);
  //创建客户端,建立连接,并连接需要的信号和槽
  void Start();
private:
  //socket客户端
  QTcpSocket *client;
  //socket描述付符
  qintptr socketDescriptor;
  //用户客户端名称,不是必须的
  QString QClientName;
signals:
  //当需要销毁该客户连接时,发送给服务,将其从列表中删除并释放空间
  void Del(qintptr desc);
public slots:
  //槽函数,用来连接Readyread信号,当客户端有数据发来时,会触发该槽函数
  void ReadyRead();
  //该槽函数用来给当前客户端发送数据,如果有信号连接该槽,调用信号即可发送给该客户端
  void WriteData(QByteArray data);
  //该槽函数用来连接socket client的断开信号,如果该客户端断开,则会触发该槽函数
  void DisConSoket();
  //该槽函数用来连接socketclient的error信号,当连接出现错误时,会触发执行该函数
  void Error(QAbstractSocket::SocketError);
};
#endif // USERCLIENT_H

下面是源文件,对应上面的头文件。

//文件名:userclient.cpp
#include "userclient.h"
#include <QAbstractSocket>
UserClient::UserClient(qintptr des, QObject *parent) : QObject(parent)
{
    this->socketDescriptor=des;
}
//通过 socket描述符创建连接
void UserClient::Start()
{
    //创建客户端
    client=new QTcpSocket();
    if (!client->setSocketDescriptor(socketDescriptor))
    {
        qDebug()<<"chuang jian shi bai !!";
        return;
    }
    //连接信号和槽
    connect(client,SIGNAL(disconnected()),this,SLOT(DisConSoket()));
 connect(client,SIGNAL(error(QAbstractSocket::SocketError)),this,SLOT(Error(QAbstractSocket::SocketError)));
    connect(client,SIGNAL(readyRead()),this,SLOT(ReadyRead()));
    //打开客户端
    client->open(QIODevice::ReadWrite);
}
//当客户端断开,会触发执行这个槽函数
void UserClient::DisConSoket()
{
    qDebug()<<"net work disconnected !!";
    Del(this->socketDescriptor);
}
//当连接出现错误,会触发执行这个槽函数
void UserClient::Error(QAbstractSocket::SocketError)
{
    qDebug()<<"net work error,closed !!!";
    qDebug()<<client->errorString();
    Del(this->socketDescriptor);

}
//When there is data to be received, the function will be executed
void UserClient::ReadyRead()
{
    //读取数据
    QByteArray buff= client->readLine();
    QString str(buff);
    //在这里处理数据就可以了
    qDebug()<<"ke hu duan du qu dao :"+str;
}
//Client Send data
void UserClient::WriteData(QByteArray data)
{
    if(client==NULL&&!client->isOpen()&&!client->isWritable())
    {
        qDebug()<<"Network connection is not available!";
        return;
    }
    //发送数据
    client->write(data);
}

2、准备好了上面的客户端类,下面开始正式编写服务端,首先创建一个名叫DataServer的类,文件名dataserver.h、dataserver.cpp ,让该类继承Qt的QTcpServer类。

/*************************************************************************
*文件名:dataserver.h
*类名:DataServer
*描述:表示一个服务器端
*************************************************************************/
#ifndef DATASERVER_H
#define DATASERVER_H

#include <QTcpServer>
#include <userclient.h>
#include <QList>
#include <QTimer>
class DataServer : public QTcpServer
{
    Q_OBJECT
public:
    //构造函数
    explicit DataServer(QObject *parent = 0);
    //用来存储连接上来的客户端
    QMap<qintptr,UserClient*> *listclient;
private:
    //这个Timer用来每秒中给所有客户端发送一条数据
    QTimer *tik;
    //重写的QTcpServer槽函数,当有客户端连接时会触发该函数
    void incomingConnection(qintptr socketDescriptor);
signals:
    //这个信号用来给客户端发送数据
    void SendDataToClient(QByteArray buff);
public slots:
    //删除一个客户端
    void DelClient(qintptr socketDes);
    //Timer连接的槽函数,每到Timer的触发时,会调用该函数
    void timetik();
};
#endif // DATASERVER_H

然后编写源文件,对应上面的头文件。

//文件名:dataserver.cpp

#include "dataserver.h"

DataServer::DataServer(QObject *parent) : QTcpServer(parent)
{
    tik=new QTimer();
    //1秒中执行一次
    tik->setInterval(1000);
    //将timeout信号和timetik槽函数连接
    connect(tik,SIGNAL(timeout()),this,SLOT(timetik()));
    listclient=new QMap<qintptr,UserClient*>;
}
//如果有客户端连接,就会触发该函数执行,并传进来一个客户端的socket描述符
void DataServer::incomingConnection(qintptr socketDescriptor)
{
    //UserClient是自定义类型,用来保存用户信息,每当连接上来一个客户端
    //会产生一个用户客户端,并将socket描述符传给用户客户端
    UserClient *client=new UserClient(socketDescriptor);
    //将客户端存起来
    this->listclient->insert(socketDescriptor,client);
    //连接服务端信号和用户客户端的槽函数,当调用该槽函数,会将数据发送给所有客户端
    connect(this,SIGNAL(SendDataToClient(QByteArray)),client,SLOT(WriteData(QByteArray)),Qt::QueuedConnection);
    //连接客户端信号和服务端槽,当客户端连接出错时(断开),用户客户端会调用
    connect(client,SIGNAL(Del(qintptr)),this,SLOT(DelClient(qintptr)));
    client->Start();

}
//删除客户端,在客户端出现错误或者断开时执行
void DataServer::DelClient(qintptr socketDes)
{
    //如果客户端存在,删除之
    if(listclient->contains(socketDes))
    {
        //释放内存
        delete listclient->value(socketDes);
        //删除对象指针
        listclient->remove(socketDes);
    }
}
//发送数据
void DataServer::timetik()
{
    QString str("this is a service !!");
    QByteArray arr=str.toUtf8();
    emit SendDataToClient(arr);
}

3、然后在main函数中启动DataServer即可:

//文件名:main.cpp
#include <QCoreApplication>
#include <dataserver.h>
#include <QNetworkInterface>
int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
    //创建一个服务对象
    DataServer *server=new DataServer();
    //启动监听
    if (!server->listen(QHostAddress::Any,51005))
    {
        //如果没有启动成功,输出错误
        qDebug()<<server->errorString();
    }
    QString ipAddress;
    QList<QHostAddress> ipAddressesList = QNetworkInterface::allAddresses();
    // 使用第一个本地IP地址
    for (int i = 0; i < ipAddressesList.size(); ++i)
    {
        if (ipAddressesList.at(i) != QHostAddress::LocalHost &&
            ipAddressesList.at(i).toIPv4Address())
        {
            ipAddress = ipAddressesList.at(i).toString();
            break;
        }
    }
    if (ipAddress.isEmpty())
    {
        ipAddress = QHostAddress(QHostAddress::LocalHost).toString();
    }
    //输出连接信息
    qDebug()<<"The server is running onIP:"+ ipAddress+" port:"+QString::number(server->serverPort());
    return a.exec();
}

  从上面的程序看到,Qt所有对象间通信都是用信号和槽,信号和槽是智能连接的,自动选择同步连接还是异步连接,用过C#的或许会觉得,C#的delegate委托真是强大……,当用了Qt的信号和槽之后,你会发现其他语言或者框架的对象间消息通信都是渣渣。在这里拿Qt的信号和槽机制和C#的委托做个比较(水平不高,个人见解)。<br> C#的委托:

  • 委托需要需要单独定义,而且delegate定义委托是一种类型,和类一个级别的,使用时需要再实例化,麻烦
  • 委托不安全,如果调用了一个没有委托了函数(或已经释放)的委托,会报空引用异常,所以必须try catch
  • 委托性能较低,用不好会很慢很慢,尤其是当一个委托+=了好几个函数的时候
  • 委托用的最多的时候时是线程间通信(和定义事件),向GUI中发送数据,需要调用Invork或BeginInvork,一个同步,一个异步,而这两个函数是GUI的,需要把GUI控件或窗体穿到发送数据的线程中(双向依赖),增加了模块间的依赖(有解决办法,略麻烦)。<br> Qt的信号和槽
  • 使用方便,只需将信号和槽函数用connect函数连接起来即可,并且一个信号可以连接很多槽和其他信号
  • 非常安全,连不连接都可以调用,即使连接的函数和对象释放了,也没关系
  • 性能非常高(之前被认为性能不高,实际上很高,自从Qt5之后,更是直接和函数指针同样高性能,Qt5基于模板)
  • 发射信号调用槽函数直接操作GUI,没任何问题;单方面依赖,模块分离。
  • C#中动不动开个线程干活,Qt中根本不需要,Qt的各种异步消息循环基本已经做到完美了。

  事实上线程是个很难掌握的技术;在没有GUI的程序中使用线程交互是不明智的行为(除非你能保证线程运行在不同的CPU核上,并能做好同步,当然开发中情况复杂,不可一概而论,新手的话还是别整太复杂的好),GUI中主要是为了能够让界面友好的响应人。

© 著作权归作者所有

共有 人打赏支持
小果汁儿
粉丝 7
博文 7
码字总数 6789
作品 1
济南
程序员
[Qt] 基于Tcp协议的聊天室实现

时间:2017年6月21日 一、写在前面: 平时做图形学的东西多一些,虽然一直对网络编程很感兴趣,但是没有什么机会去尝试一下。最近正好赶上期末的课程实习,然后就参考Qt官方的 Network Prog...

Mahabharata_ ⋅ 2017/06/22 ⋅ 0

嵌入式 linux 智能设备应用中 web 支持的实现

近几年,嵌入式 Linux 在智能设备中的应用发展的非常迅速。可以预见,嵌入式智能设备和我们的生活将会越来越密不可分。 Linux 在嵌入式系统中的应用可以分为两大类:面向服务类和面向应用类。...

红薯 ⋅ 2010/01/11 ⋅ 0

Qt开发的UDP发送端

一、准备 (一)开发环境 系统:Windows 7 专业版(64位) 软件:qt-opensource-windows-x86-mingw530-5.7.0(原来用Qt 5.2.1+完成的工程) 注:Qt下载链接http://download.qt.io/official_...

伊甸流云 ⋅ 2016/12/10 ⋅ 0

Qt的QTcpSocket的readyRead信号使用体会

本文主要介绍readyread函数的触发机制,会设计到一点tcp/ip和流的知识。tcp/ip和流这方面网上一大堆,所以,别人说了的东西我就不再说了。本文不打算使用太过专业的词汇,一方面主要目的是让...

dengdew ⋅ 01/15 ⋅ 0

ubuntu14 软件安装小结

软件安装目录表 软件安装过程 1 sublime Text3 (3216) sudo add-apt-repository ppa:webupd8team/sublime-text-3 sudo apt-get-update sudo apt-get install sublime-text-installer 安装完......

CSDNhuaong ⋅ 2017/10/01 ⋅ 0

MUSCLE 5.91 发布,消息服务器

MUSCLE 5.91 修复了 -DMUSCLEUSEMUTEXESFORATOMIC_OPERATIONS 实现的 bug,改进了 Android 和 Qt 的兼容性。 MUSCLE (Multi User Server Client Linking Environment) 提供一个多路的消息服务......

oschina ⋅ 2013/09/04 ⋅ 0

crazycooler/Fish-MD

Fish-MD Fish-MD是一款非常轻量级的Markdown云笔记工具,可以支持云端的数据同步功能。 和常规的markdown编辑工具相比,增加了图片的粘贴功能,可以将剪贴板中的图片,通过ctrl+v直接将图片添...

crazycooler ⋅ 2017/12/11 ⋅ 0

我眼中的 Qt for Android

引子 前几天,我分享了一下qt for android,从大家的反应和回馈,我看到两种极端的状态。一个是:“太好了!想做Android开发但是不想转java,这下不用了!” 另一个是:“不要在Qt上浪费时间...

鉴客 ⋅ 2011/10/23 ⋅ 4

Qt Socket简单通信

Qt Socket简单通信 时间:2013 年 03 月 12 日 分类:学习笔记 Linux 嵌入式 目录 1.UDP通信 1.1.UDP发送端 1.2.UDP接收端 2.TCP通信 2.1.TCP client端 2.2.TCP server端 最近要用到Qt的Soc...

ustbgaofan ⋅ 2014/08/13 ⋅ 0

ESRI公司研发GIS产品集合

ESRI公司研发GIS产品集合 ESRI 美国环境系统研究所公司 成立于1969年,总部设ESRI在美国加州RedLands市,是世界最大的地理信息系统技术提供商。 公司主营:软件、地理信息系统(GIS) 主要产...

sinat_34719507 ⋅ 2016/12/22 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

Spring Bean基础

1、Bean之间引用 <!--如果Bean配置在同一个XML文件中,使用local引用--><ref bean="someBean"/><!--如果Bean配置在不同的XML文件中,使用ref引用--><ref local="someBean"/> 其实两种......

霍淇滨 ⋅ 22分钟前 ⋅ 0

05、基于Consul+Upsync+Nginx实现动态负载均衡

1、Consul环境搭建 下载consul_0.7.5_linux_amd64.zip到/usr/local/src目录 cd /usr/local/srcwget https://releases.hashicorp.com/consul/0.7.5/consul_0.7.5_linux_amd64.zip 解压consu......

北岩 ⋅ 25分钟前 ⋅ 0

Webpack 4 api 了解与使用

webpack 最近升级到了 v4.5+版 01 官方不再支持 node4 以下版本 官方不再支持 node4 以下版本官方不再支持 node4 以下的版本,所以如果你的node版本太低,先开始升级node吧!话说node10 ...

NDweb ⋅ 35分钟前 ⋅ 0

使用nodeJs安装Vue-cli

Vue脚手架就是一个Vue框架开发环境 脚手架的意思是帮你快速开始一个vue的项目,也就是给你一套vue的结构,包含基础的依赖库,只需要 npm install就可以安装,让我们不需要为了编辑或者一些其...

木筏笔歆 ⋅ 今天 ⋅ 0

【微信小程序开发实战】0x00.开发前准备工作

写在开始 本人资深后端码农一枚,近期项目需求,接触到了微信小程序,将学习过程整理成文分享给小伙伴们,由于是边学边整理难免有表述不对的地方,望大家及时指正,感谢。 本人微信号: dream...

dreamans ⋅ 今天 ⋅ 0

linux redis的安装和php7下安装redis扩展

安装redis服务器 (1)下载安装包: $ wget http://download.redis.io/releases/redis-2.8.17.tar.gz (2)编译程序: $ tar xzf redis-2.8.17.tar.gz $ cd redis-2.8.17 $ make $ cd src &&......

concat ⋅ 今天 ⋅ 0

Guava EventBus源码解析

一、EventBus使用场景示例 Guava EventBus是事件发布/订阅框架,采用观察者模式,通过解耦发布者和订阅者简化事件(消息)的传递。这有点像简化版的MQ,除去了Broker,由EventBus托管了订阅&...

SaintTinyBoy ⋅ 今天 ⋅ 0

http怎么做自动跳转https

Apache 版本 如果需要整站跳转,则在网站的配置文件的<Directory>标签内,键入以下内容: RewriteEngine on RewriteCond %{SERVER_PORT} !^443$ RewriteRule ^(.*)?$ https://%{SERVER_NAME......

Helios51 ⋅ 今天 ⋅ 0

Python爬虫,抓取淘宝商品评论内容

作为一个资深吃货,网购各种零食是很频繁的,但是能否在浩瀚的商品库中找到合适的东西,就只能参考评论了!今天给大家分享用python做个抓取淘宝商品评论的小爬虫! 思路 我们就拿“德州扒鸡”...

python玩家 ⋅ 今天 ⋅ 0

MySQL 内核深度优化

MYSQL数据库适用场景广泛,相较于Oracle、DB2性价比更高,Web网站、日志系统、数据仓库等场景都有MYSQL用武之地,但是也存在对于事务性支持不太好(MySQL 5.5版本开始默认引擎才是InnoDB事务...

java高级架构牛人 ⋅ 今天 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部