zoukankan      html  css  js  c++  java
  • QTcpSocketQt使用Tcp通讯实现服务端和客户端

    博客地址已更改,文章数量较多不便批量修改,若想访问源文请到 coologic博客 查阅,网址:www.coologic.cn

    如本文记录地址为 techieliang.com/A/B/C/ 请改为 www.coologic.cn/A/B/C/ 即可查阅

    版权声明:若无来源注明,Techie亮博客文章均为原创。 转载请以链接形式标明本文标题和地址:
    本文标题:QTcpSocket-Qt使用Tcp通讯实现服务端和客户端     本文地址:https://www.techieliang.com/2017/12/530/

    1. 基本功能

    详细说明请见官方文档

    1.1. pro文件配置

    使用Qt网络功能需要在pro文件增加网络库

    1. QT += network

    1.2. QTcpServer服务端建立

    1. QTcpServer server = new QTcpServer();
    2. connect(server,
    3. &QTcpServer::newConnection,
    4. this,
    5. &MainWindow::server_New_Connect);//监听
    6. if(!server->listen(QHostAddress::Any, 8000)) {
    7. qDebug()<<server->errorString(); //错误信息
    8. }

    创建server对象以后首先要监听客户端的连接,通过listen函数可以开启监听,需要指定监听的ip和端口号,ip可使用QHostAddress::Any

    QTcpServer当有新客户端连接时会发出QTcpServer::newConnection的信号,只需要关联到自定义的槽即可。

    1. void MainWindow::server_New_Connect() {
    2. //获取客户端连接
    3. auto socket_temp = server->nextPendingConnection();//根据当前新连接创建一个QTepSocket
    4. m_socket=socket_temp;//记录此连接用于后续数据读写
    5. //连接QTcpSocket的信号槽,以读取新数据
    6. QObject::connect(socket_temp, &QTcpSocket::readyRead, this, &MainWindow::socket_Read_Data);
    7. //当断开连接时发出的信号
    8. QObject::connect(socket_temp, &QTcpSocket::disconnected, this, &MainWindow::socket_Disconnected);
    9. }

    上述为一个新连接到来的槽函数范例,利用nextPendingConnection获取到新连接的socket,存储此socket,并关联对应的信号

    主要有两个:接收到新数据的信号以及连接断开的信号。

    当连接断开可以通过响应信号槽机制实现断开后的操作。

    1.3. 客户端建立

    客户端为主动连接方,不需要监听,直接建立QTcpSocket即可

    1. m_socket = new QTcpSocket;
    2. m_socket->connectToHost("127.0.0.1",80100,QTcpSocket::ReadWrite);
    3. connect(m_socket,SIGNAL(connected()),this,SLOT(connected()));

    上述例子使用的是信号槽方式等待连接成功,也可以使用阻塞方式:waitForConnected,等到连接成功才会执行后续代码,不需要建立新的槽函数。

    通过connectToHost连接指定ip和端口,同时将socket的连接成功的信号与对应槽连接,当连接成功可以将自定义的标记位置为true,可进行相应的收发。

    1. void MainWindow::connected() {
    2. m_is_connected = true;
    3. connect(this->socket,SIGNAL(readyRead()),this,SLOT(readyread())); //连接接收消息槽
    4. QObject::connect(socket_temp,?&QTcpSocket::disconnected,?this,?&MainWindow::socket_Disconnected);//断开连接
    5. }

    当连接成功建议将接收和断开连接的信号进行connect

    1.4. 消息收发

    • 不阻塞收发:

    无论客户端还是服务端只有在建立连接时有差异,后续的消息收发都相同。

    首先通过QTcpSocket::close()可以主动断开连接,无论客户端服务端都可以执行主动断开

    通过readyRead()信号可以在接到信息后进行信息操作,在槽中执行QTcpSocket::readAll()可以读取缓冲区所有数据

    QTcpSocket::send()可发送信息,调用flush可立即发送缓冲区的数据,不需等待。

    • 阻塞收发:

    Qt同时提供了阻塞收发及连接、断开连接的函数:

    virtual bool waitForConnected(int msecs = 30000)
    virtual bool waitForDisconnected(int msecs = 30000)
    virtual bool waitForBytesWritten(int msecs = 30000)
    virtual bool waitForReadyRead(int msecs = 30000)

    通过上述函数可以实现阻塞连接、断开连接、发送、接收数据内容

    2. 其他

    2.1. 实现单服务器多客户端通讯

    网上大部分例子都是单服务器通讯,若不做修改连接多个客户端,会出现只有最后一个通讯有效的情况。

    主要原因是在监听到新连接时的处理方式不当:

    1. connect(server, &QTcpServer::newConnection, this, &MainWindow::server_New_Connect);//监听
    2. if(!server->listen(QHostAddress::Any, 8000)) {
    3. qDebug()<<server->errorString(); //错误信息
    4. }

    注意上述代码在服务端收到信连接时会固定的调用一个槽函数,而槽函数往往写成下述样式:

    1. void MainWindow::server_New_Connect() {
    2. //获取客户端连接
    3. auto socket_temp = server->nextPendingConnection();//根据当前新连接创建一个QTepSocket
    4. m_socket=socket_temp;//记录此连接用于后续数据读写
    5. //连接QTcpSocket的信号槽,以读取新数据
    6. QObject::connect(socket_temp, &QTcpSocket::readyRead, this, &MainWindow::socket_Read_Data);
    7. //当断开连接时发出的信号
    8. QObject::connect(socket_temp, &QTcpSocket::disconnected, this, &MainWindow::socket_Disconnected);
    9. }

    m_socket=socket_temp;//记录此连接用于后续数据读写

    这一行等于是每次有一个新的连接都替换了旧的连接记录,自然之友最后一个客户端连接有效,正确的可以建立list存储所有连接的socket,当收发数据时根据需要指定socket进行收发。这时将disconnected信号进行connect就具有了作用,当某个连接断开时应该从所有连接链表中删除此记录。

    由nextPendingConnection创建的QTcpSocket,会有QTcpServer维护,当QTcpServer销毁是会自动销毁所有创建的socket,若想提前释放内容可以在disconnected信号发生时主动delete

    2.2. 关于QTcpServer

    可能考虑到跨平台问题,Qt使用select实现io多路复用,连接数量限制是1024,若需要poll,epoll等可使用其他库,比如libevent

    2.3. 关于数据收发

    可以通过setReadBufferSize设置接收缓冲区大小(Qt内部缓冲区大小)

    当发送端发送的数据超过buffer大小时会触发readyread信号,需要注意对此情况的处理方法,可以考虑在消息头增加消息长度

    未验证:Qt有自己内部的缓冲区,消息发送到系统缓冲区,Qt会读取出来,而调用的Qt函数readall等实际上读取的是Qt内部缓冲区而非系统缓冲区

  • 相关阅读:
    结合<span id="outer"><span id="inter">text</span></span>这段结构,谈谈innerHTML、outerHTML、innerText之间的区别
    字符串的方法slice、substr、substring对比
    为什么两个一样的对象,用===打印是false
    this指向
    复制对象的方法
    Promise以及async和await的用法
    前端性能优化&&网站性能优化
    P1510 精卫填海
    分解质因数
    P2648 赚钱
  • 原文地址:https://www.cnblogs.com/techiel/p/7978039.html
Copyright © 2011-2022 走看看