便宜旅游机票网站建设,wordpress搜索框插件,小程序源码安装,微信平台开发公司成都文章目录 核心APITCP回显服务器TCP回显客户端 核心API
QTcpServer用于监听端口和获取客户端连接
名称类型说明对标原生APIlisten(const QHostAddress, quint16 port)方法绑定指定的地址和端口号#xff0c;并开始监听bind和listennextPendingConnection()方法从系统中获… 文章目录 核心APITCP回显服务器TCP回显客户端 核心API
QTcpServer用于监听端口和获取客户端连接
名称类型说明对标原生APIlisten(const QHostAddress, quint16 port)方法绑定指定的地址和端口号并开始监听bind和listennextPendingConnection()方法从系统中获取到一个建立好的tcp连接返回一个QTcpSocket表示这个客户端的连接通过这个socket对象完成和客户端之间的通信acceptnewCondition()信号有新的客户端建立好连接之后触发无类似IO多路复用的通知机制
QTcpSocket用于客户端和服务器之前的数据交互 tcp读取的数据是字节流因此读取和返回的都是字节数组这和udp的QNetworkDatagram数据报不一样 事件循环可以简单理解为是Qt程序内部一个带有“生物钟”这样的东西周期性执行一些逻辑 名称类型说明对标原生APIreadAll()方法读取当前接收缓冲区的所有数据返回QByteArray对象readwrite(const QByteArray)方法将数据写入socket当中writedeleteLater方法暂时把socket对象标记为无效Qt会在下一个事件循环中构造释放该对象无readyRead信号有数据到达并准备就绪时触发无disconnected信号连接断开时触发无 QByteArray是字节数组可以和QString互相转换 QString的构造函数可以把QByteArray转换成QStringQString的toUtf8函数可以把QString转成QByteArray TCP回显服务器
用这些接口写一个回显服务器。 创建项目之后如果要进行网络编程第一步就是在.pro文件中加入network模块 界面 widget.h
#ifndef WIDGET_H
#define WIDGET_H#include QWidget
#include QTcpServer
QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACEclass Widget : public QWidget
{Q_OBJECTpublic:Widget(QWidget *parent nullptr);~Widget();void processConnection();QString process(const QString request);
private:Ui::Widget *ui;QTcpServer *tcpServer;
};
#endif // WIDGET_Hwidget.cpp 绑定和监听端口号一定是要等准备工作做完之后再进行比如说如何处理连接、如何处理请求等… 需要手动释放clientSocket因为它是每个客户端都有这样一个对象而QTcpServer和QUdpServer都是只有一份。如果不对断开连接的客户端进行释放累计的客户端会越来越多这会导致两个问题文件描述符泄漏和内存泄漏。 也不能直接delete clientSocket因为当前槽函数主要是围绕clientSocket来进行操作的一旦delete其他逻辑就无法使用clientSocket使用要保证delete操作是最后一步而且不会被return或者抛出异常给跳过。 Qt提供了deleteLater不是立即销毁而是告诉Qt下一轮事件循环中再进行上述销毁操作。 槽函数是在事件循环中进行的进入下一轮事件循环表明上一轮事件肯定结束了。
#include widget.h
#include ui_widget.h
#includeQMessageBox
#includeQTcpSocket
Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui-setupUi(this);//修改窗口标题this-setWindowTitle(tcp服务器);//创建QTcpServer实例tcpServer new QTcpServer(this);//连接信号槽(如何处理连接)connect(tcpServer, QTcpServer::newConnection, this, Widget::processConnection);//绑定并监听端口号if(!tcpServer-listen(QHostAddress::Any, 8080)){QMessageBox::critical(this, 服务器启动失败, tcpServer-errorString());return;}
}Widget::~Widget()
{delete ui;
}void Widget::processConnection()
{//拿到socket对象QTcpSocket *clientSocket tcpServer-nextPendingConnection();//peerAddress表示对端的ip地址QString log [ clientSocket-peerAddress().toString() : QString::number(clientSocket-peerPort()) ] online;//显示到界面ui-listWidget-addItem(log);//通过信号槽处理客户端发来的请求connect(clientSocket, QTcpSocket::readyRead, this, [](){//读取请求QString request clientSocket-readAll();//处理请求const QString response process(request);//返回响应clientSocket-write(response.toUtf8());//记录日志QString log [ clientSocket-peerAddress().toString() : QString::number(clientSocket-peerPort()) ] req: request , resp: response;ui-listWidget-addItem(log);});//客户端断开连接//disconnected表示已经断开是一个信号connect(clientSocket, QTcpSocket::disconnected, this, [](){QString log [ clientSocket-peerAddress().toString() : QString::number(clientSocket-peerPort()) ] offline;ui-listWidget-addItem(log);//手动释放clientSocketclientSocket-deleteLater();});
}QString Widget::process(const QString request)
{return request;
}此段代码从某种意义上来说不够严谨因为tcp是面向字节流的可能会分为多段。 更好的做法是将收到的数据放到一个较大字节的缓冲区当中然后按照约定好的协议进行数据解析提取。 TCP回显客户端
ui界面 对于客户端使用的就是QTcpSocketQTcpServer只是在服务端使用的 调用connectToHost函数此时系统就开始和对方服务器三次握手三次握手也是需要时间的而这个函数并不会阻塞等待握手完毕是一个非阻塞的函数。 所以需要搭配waitForConnected等待连接建立成功
#include widget.h
#include ui_widget.h
#includeQMessageBox
Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui-setupUi(this);//设置窗口标题this-setWindowTitle(客户端);//创建socket对象实例socket new QTcpSocket(this);//和服务器建立连接socket-connectToHost(127.0.0.1, 8080);//连接信号槽connect(socket, QTcpSocket::readyRead, this, [](){//读取响应内容QString response socket-readAll();//响应内容显示到界面ui-listWidget-addItem(server say# response);});//等待连接建立结果if(!socket-waitForConnected()){QMessageBox::critical(this, 连接服务器失败, socket-errorString());return;}}Widget::~Widget()
{delete ui;
}void Widget::on_pushButton_clicked()
{//获取输入框内容const QString text ui-lineEdit-text();//发送到服务器socket-write(text.toUtf8());//将发送的消息显示到界面ui-listWidget-addItem(client say# text);//清空输入框内容ui-lineEdit-setText();
}Linux当中写Tcp服务器的时候如果多个客户端同时访问就只会生效一个然后引入线程每个客户端一个线程 而这里并没有出现这类情况这是因为之前是写的两层循环里面的循环没有结束导致外层循环不能快速调用到accpet导致第二个客户端无法进行处理。 引入多线程本质上就是将双重循环化简成两个独立的循环。 Qt里面的信号槽机制就无需写这些循环比较方便。