赞
踩
关于TCP与UDP的基础这里就不过多介绍了,具体可以查看对应百度百科介绍:
需要知道这两者的区别:
可靠性:
连接性:
效率:
网络通信以上两者都绕不开IP地址与端口这两个。
一般情况需要网络调试助手或者wireshark抓包工具,网络调试助手我用的是NetAssist。关于NetAssist和WireShark怎么使用,后面会介绍。
cmd命令行:ipconfig/all
修改:
设置选中“网络和Internet”
命令行:ifconfig -a:
- static QStringList getIPAddresses() {
- QStringList addresses;
- for (const QHostAddress &address : QNetworkInterface::allAddresses()) {
- if (address.protocol() == QAbstractSocket::IPv4Protocol)
- addresses.append(address.toString());
- }
- return addresses;
- }
或者直接查看网络设置。
Qt要使用网络模块记得工程文件添加:QT += network
- static QStringList getIPAddresses() {
- QStringList addresses;
- for (const QHostAddress &address : QNetworkInterface::allAddresses()) {
- if (address.protocol() == QAbstractSocket::IPv4Protocol)
- addresses.append(address.toString());
- }
- return addresses;
- }
编译运行查看打印:
Windows和Linux都需要借助netstat命令,但是两者稍微有一些不一样。
比如我使用刚刚的调试助手NetAssist,建立一个tcp服务器,然后监听IP:192.168.5.1,端口:8080。
查看一下这个服务器是否监听成功,命令行输入:netstat -antp TCP(‘p’指定对应协议,后面需要接协议类型TCP或UDP)
或者直接输入命令 netstat -antp TCP|findstr 8080:
再起一个调试助手以客户端的形式连接这个服务器:
再次输入: netstat -antp TCP|findstr 8080:
能够查看到刚刚建立的连接。
查看对应链接是哪个应用建立的,先输入: netstat -antpo TCP|findstr 8080:
然后使用tasklist查看对应进程:
如果是UDP改为 netstat -antp UDP|findstr 8080 即可
Linux 的netstat的命令指定对应协议不需要 -p TCP或者-p UDP,而是-t就是TCP,-u就是UDP,如下图所示:
因为TCP是需要建立链接,分客户端和服务器端的,所以需要分别编写。
服务器由QTcpServer来实现,QTcpServer的信号:
需要注意newConnection这个信号,当有客户端连接这个服务器时,会触发这个信号。
所有的方法:
需要注意的几个方法:
bool listen(const QHostAddress &address = QHostAddress::Any, quint16 port = 0) | 监听对应IP和端口,IP为空则默认监听any |
close() | 停止监听 |
bool isListening() const | 是否正在监听 |
QHostAddress serverAddress() const | 监听的IP地址 |
quint16 serverPort() const | 监听的端口 |
void setMaxPendingConnections(int numConnections) | 设置允许建立的最大连接数 |
比如监听IP 127.0.0.1 端口 8080:
QTcpServer server;
server.listen(QHostAddress("127.0.0.1"),8080);
写一个简单的例子。
ui:
头文件:
- #ifndef MAINWINDOW_H
- #define MAINWINDOW_H
- #include <QMainWindow>
- #include <QTcpServer>
- #include <QTcpSocket>
- QT_BEGIN_NAMESPACE
- namespace Ui {
- class MainWindow;
- }
- QT_END_NAMESPACE
- class MainWindow : public QMainWindow
- {
- Q_OBJECT
- public:
- MainWindow(QWidget *parent = nullptr);
- ~MainWindow();
- private slots:
- void on_listen_clicked();
- void on_disconnect_clicked();
- void on_send_clicked();
- void newConnection();
- private:
- Ui::MainWindow *ui;
- QTcpServer *m_Server;
- QTcpServer *m_Server1;
- QList<QTcpSocket *> m_Sockets;
- void showLog(const QString &log);
- };
- #endif // MAINWINDOW_H
-
源文件:
- #include "mainwindow.h"
- #include <QDateTime>
- #include <QHostAddress>
- #include <QNetworkInterface>
- #include "ui_mainwindow.h"
- static QStringList getIPAddresses() {
- QStringList addresses;
- for (const QHostAddress &address : QNetworkInterface::allAddresses()) {
- if (address.protocol() == QAbstractSocket::IPv4Protocol)
- addresses.append(address.toString());
- }
- return addresses;
- }
-
- MainWindow::MainWindow(QWidget *parent)
- : QMainWindow(parent), ui(new Ui::MainWindow) {
- ui->setupUi(this);
- setWindowTitle("TcpServer");
- ui->tableWidget->setEditTriggers(QAbstractItemView::NoEditTriggers);
- ui->localIp->addItems(getIPAddresses());
- ui->tableWidget->setSelectionBehavior(QAbstractItemView::SelectRows);
- m_Server = new QTcpServer;
- connect(m_Server, &QTcpServer::newConnection, this,
- &MainWindow::newConnection);
- for (QTcpSocket *socket : m_Sockets) {
- connect(socket, &QTcpSocket::readyRead, [=]() {
- showLog(QString("%1:%2:%3")
- .arg(socket->peerAddress().toString())
- .arg(socket->peerPort())
- .arg(QString(socket->readAll().toHex())));
- });
- connect(socket, &QTcpSocket::disconnected, [=]() {
- showLog(QString("disconnect:%1:%2:%3")
- .arg(socket->peerAddress().toString())
- .arg(socket->peerPort())
- .arg(QString(socket->readAll().toHex())));
- for (int i = 0; i < ui->tableWidget->rowCount(); i++) {
- QTableWidgetItem *ipItem = ui->tableWidget->item(i, 0);
- QTableWidgetItem *portItem = ui->tableWidget->item(i, 1);
- if (nullptr != ipItem && nullptr != portItem) {
- if (ipItem->text() == socket->peerAddress().toString() &&
- portItem->text() == socket->peerPort()) {
- ui->tableWidget->removeRow(i);
- break;
- }
- }
- }
- });
- connect(
- socket,
- static_cast<void (QTcpSocket::*)(const QAbstractSocket::SocketError)>(
- &QTcpSocket::error),
- [=](QAbstractSocket::SocketError error) {
- qDebug() << "error:" << error;
- showLog("error:" + QString::number(int(error)));
- });
- connect(socket, &QTcpSocket::stateChanged,
- [=](QAbstractSocket::SocketState state) {
- qDebug() << "stateChanged:" << state;
- showLog("stateChanged:" + QString::number(int(state)));
- });
- }
- }
-
- MainWindow::~MainWindow() { delete ui; }
-
- void MainWindow::on_listen_clicked() {
- if (ui->listen->text() == "listen") {
- if (m_Server->listen(QHostAddress(ui->localIp->currentText()),
- ui->localPort->value()))
- ui->listen->setText("listening");
- else
- ui->textEdit->append("listen fail");
- } else {
- for (QTcpSocket *socket : m_Sockets) {
- socket->close();
- socket->disconnectFromHost();
- }
- m_Server->close();
- ui->listen->setText("listen");
- }
- }
-
- void MainWindow::on_disconnect_clicked() {
- int row = ui->tableWidget->currentRow();
- if (-1 != row) {
- QTableWidgetItem *ipItem = ui->tableWidget->item(row, 0);
- QTableWidgetItem *portItem = ui->tableWidget->item(row, 1);
- if (nullptr != ipItem && nullptr != portItem) {
- QString ip = ipItem->text();
- quint16 port = portItem->text().toUShort();
- for (QTcpSocket *socket : m_Sockets) {
- if (ip == socket->peerAddress().toString() &&
- port == socket->peerPort()) {
- socket->close();
- socket->disconnectFromHost();
- break;
- }
- }
- }
- }
- }
-
- void MainWindow::on_send_clicked() {
- QByteArray ba = ui->send->text().toUtf8();
- int row = ui->tableWidget->currentRow();
- if (-1 != row) {
- QTableWidgetItem *ipItem = ui->tableWidget->item(row, 0);
- QTableWidgetItem *portItem = ui->tableWidget->item(row, 1);
- if (nullptr != ipItem && nullptr != portItem) {
- QString ip = ipItem->text();
- quint16 port = portItem->text().toUShort();
- for (QTcpSocket *socket : m_Sockets) {
- if (ip == socket->peerAddress().toString() &&
- port == socket->peerPort()) {
- socket->write(ba);
- break;
- }
- }
- }
- }
- }
-
- void MainWindow::newConnection() {
- QTcpSocket *socket = m_Server->nextPendingConnection();
- m_Sockets.append(socket);
- // m_TcpSocket = socket;
- int row = ui->tableWidget->rowCount();
- ui->tableWidget->insertRow(row);
- QTableWidgetItem *ipItem =
- new QTableWidgetItem(socket->peerAddress().toString());
- QTableWidgetItem *portItem =
- new QTableWidgetItem(QString::number(socket->peerPort()));
- qDebug() << socket->peerAddress().toString() << "," << socket->peerPort()
- << "," << socket->peerName();
- ui->tableWidget->setItem(row, 0, ipItem);
- ui->tableWidget->setItem(row, 1, portItem);
- connect(socket, &QTcpSocket::disconnected, [=]() {
- showLog(QString("disconnected:%1:%2")
- .arg(socket->peerAddress().toString())
- .arg(socket->peerPort()));
- // m_TcpSocket = nullptr;
- for (int i = 0; i < ui->tableWidget->rowCount(); i++) {
- QTableWidgetItem *ipItem = ui->tableWidget->item(i, 0);
- QTableWidgetItem *portItem = ui->tableWidget->item(i, 1);
- if (nullptr != ipItem && nullptr != portItem) {
- if (ipItem->text() == socket->peerAddress().toString() &&
- portItem->text() == QString::number(socket->peerPort())) {
- ui->tableWidget->removeRow(i);
- break;
- }
- }
- }
- });
- }
-
- void MainWindow::showLog(const QString &log) {
- ui->textEdit->append(
- QString("%1:%2")
- .arg(QDateTime::currentDateTime().toString("yyyy/MM/dd hh:mm:ss.zzz"))
- .arg(log));
- }
有客户端连接后,会触发newConnection这个信号,然后在槽函数中使用nextPendingConnection()这个方法获取对应的客户端QTcpSocket 指针对象。客户端发送消息后会触发readyRead这个信号,使用QTcpSocket的readAll获取发送的信息。编译运行,输入监听的ip和端口然后点击listen按钮然后使用命令查看是否开始监听对应ip和端口:
然后使用调试助手作为客户端连接这个服务器:
可以看到触发了newConnection信号,然后获取对应客户端对象将其信息显示到了的表格上面。
实验链接的建立与取消以及消息的互相发送:
可以实现对应的通讯。
以上是使用调试助手,也可以使用WireShark抓包查看发送和接受的数据。需要注意的是如果客户端和服务器都在本地自己发自己收是用WireShark抓不到的。
这里简单说一下wireshark的用法,首先选择需要抓取的网卡:
比如ping就是走的tcp,测试时我是用 虚拟机ping我的主机,如何使vmware虚拟机和主机ping通可以参考这位博主的博客:实现虚拟机(VM15.5.0)与本机相互通信_vmware和主机怎样才能ping通-CSDN博客
然后查看wireshark可以看到ping的报文:
因为我的主机ip192.168.1.3,虚拟机ip是192.168.1.4,可以输入“ip.src==192.168.1.4 && ip.dst==192.168.1.3” 来过滤:
同样,我使用虚拟机和主机建立tcp连接然后发送消息也是可以抓到:
客户端由QTcpSocket实现,QTcpSocket继承自QAbstractSocket,比如上文中在虚拟机中建立一个tcp服务器,监听ip192.168.1.3,端口12345:
- QTcpSocket *socket =new QTcpSocket;
- socket->connectToHost(QHostAddress(),12345);
- if(socket->waitForConnected())
- {
- //TODO 连接成功
- }
- else
- {
- //TODO 连接失败
- }
-
- connect(socket,&QTcpSocket::disconnected,[=](){
- //TODO 处理连接断开
- });
- connect(socket, &QTcpSocket::readyRead, [=]() {
- QByteArray receiveData=socket->readAll();
- //TODO 处理接收的数据
- });
-
- QByteArray sendData;
- //TODO 处理发送数据
- //发送数据
- socket->write(sendData);
- //断开连接,两种方式
- socket->abort;//强制中断连接
- socket->disconnectFromHost();//不会马上关闭连接,等待资源释放后才会中断连接
另外客户端套接字可以绑定bind对应ip和端口,如果没有绑定,则系统会使用之绑定一个随即的可用的ip和端口 :
因为UDP不用建立连接,不用分服务器和客户端,所以对应Qt的UDP部分,只需要使用QUdpSocket一个即可:
QUdpSocket同QTcpSocket一样都继承自QAbstractSocket,使用UDP通信前,对应udp套接字需要绑定对应ip与端口,然后发送数据时需要知道对方的ip与端口(UDP分单播、组播与广播,这里只说单播,组播与广播后面博客再写):
- QUdpSocket *udpSocket = new QUdpSocket(this);
- udpSocket->bind(QHostAddress("192.168.1.3"), 12345);
- connect(udpSocket, &QUdpSocket::readyRead,[=](){
-
- while (udpSocket->hasPendingDatagrams()) {
- QByteArray data;
- QHostAddress host;
- quint16 port;
- data.resize(udpSocket->pendingDatagramSize());
- udpSocket->readDatagram(data.data(), data.size(), &host, &port);
- //TODO 处理接受数据
- }
- });
- QByteArray sendData;
- //TODO 处理发送数据
- udpSocket->writeDatagram(sendData, QHostAddress("192.168.1.4"),
- 12345);
- //取消绑定
- udpSocket->unbind();
使用网络助手模拟udp通信:
使用自己写的udp程序在虚拟机中与之通信:
使用wireshark抓包:
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。