一、结构
1.1 套接字
应用层通过传输层进行数据通信时,TCP和UDP会遇到同时为多个应用程序进程提供并发服务的问题。多个TCP连接或多个应用程序进程可能需要 通过同一个TCP协议端口传输数据。为了区别不同的应用程序进程和连接,许多计算机操作系统为应用程序与TCP/IP协议交互提供了称为套接字 (Socket)的接口,区分不同应用程序进程间的网络通信和连接。
实际上套接字做的事情就是为我们通信的两端做一个连接
1.2 socket通信流程
对于TCP而言,socket通信的流程大概如下:
1.3 QTcpsocket
对于客户端我们就使用的这个QTcpsocket 类去请求服务器端,我们先看官方给的文档可以知道: 使用该类需要#include <QTcpSocket> 头文件,并且该类是继承QAbstractSocket 类的,而且我们发现对于这个类没有新增很多的函数,那么我们就应该去看它的父类,果不其然,父类中有很多的函数,我们后面进行TCP 通信其实也主要是用到父类的一些函数,所以看一下文档还是有必要的,对于每一个函数,你都能点进去看参数、以及描述 虽然QAbstractSocket 有这么多的函数,但是我们实际上使用的函数就那么几个,我们后面一一介绍,我们现在先来说说QT 客户端创建网络连接的流程:
- 1.我们需要
new 一个QTcpSocket 的对象,当然初始化只需要将当前的obj 传入即可,也就是this ,然后给这个对象的readyRead 创建一个凹槽做一些收到信号后的处理(比如将收到的数据显示在某个地方)。 - 2.通过
connectToHost 函数去连接服务器,函数中传入ip 和port (ip 要强转为QHostAddress 类) - 3.通过调用
waitForConnected 函数,来判断服务器是否连接超时,一般设置1000 ,表示的是1s 未连接就超时,通过官方的文档我们能知道如果返回的是true 表示的是建立了连接,否则表示建立失败或未建立连接 注意的是这里,只有使用waitForConnected() 后,QTcpSocket 才真正尝试连接服务器,并返回是否连接的结果。 - 4.当我们的客户端接收到
readyRead 的信号,我们就可以通过readAll() 函数读取服务器返回的信息,同样的我们也可以通过write() 函数向服务器发送信息 注意的是这里服务端读到的数据是一个QByteArray 类型的,我们写入的数据可以是Qstring 类型的,当然也可以是QByteArray
那么这就是客户端的通信流程了
1.4 QTcpServer
对于服务器端我们需要用到QTcpServer 类,同样在官网的文档我们能得到这个类的一些基本信息:
服务端的流程:
- 1.首先创建一个
QTcpServer 类,并初始化,然后给这个对象的 QTcpServer::newConnection() 建立一个凹槽,用于处理与客户端建立连接后要做的一些事情,例如继续为QTcpSocket::readyRead 创建一个凹槽进行数据读取操作、为QTcpSocket::disconnected 创建凹槽用于对服务端失联后的操作…… - 2.通过
listen(QHostAddress::Any,port) 函数监听所有的ip 请求 - 3.当有新的客户端连接服务器的时候,会自动触发
newConnection() 信号函数,然后我们可以通过通过QTcpSocket * nextPendingConnection() 成员函数来获取当前连接上的新的客户端类.然后再对QTcpSocket 来进行信号槽绑定(这里可以写一个客户端的池但是我这里为了方便就只写了一个客户端连接的情况) - 4.对于数据的读取的话,由于我们这里只写了一个客户端的情况,那么就可以直接给这个
QTcpSocket 对象绑定和客户端相同的事件就好
二、设计UI
我们直接使用QT Creator 自带的绘制工具,简单绘制一下就好,界面不重要,重要是控件的objectName 经量设置合理一点,下面是我的设置:
2.1 客户端UI
2.2 服务器端UI
三、核心代码
对于客户端来说:mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QTcpSocket>
#include <QLabel>
#include <QHostAddress>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
setWindowTitle(QString("客户端"));
ui->setupUi(this);
ui->port_2->setText("8899");
ui->ip->setText("127.0.0.1");
ui->disconnect->setDisabled(true);
m_tcp=new QTcpSocket(this);
connect(m_tcp,&QTcpSocket::readyRead,this,[=](){
QByteArray array=m_tcp->readAll();
ui->record->append("服务端说:"+array);
});
connect(m_tcp,&QTcpSocket::disconnected,this,[=](){
m_status->setPixmap(QPixmap(":/a/tmp/disconnect.png").scaled(20,20));
ui->record->append("断开链接服务器");
ui->connect->setDisabled(false);
ui->disconnect->setDisabled(true);
});
connect(m_tcp,&QTcpSocket::connected,this,[=](){
m_status->setPixmap(QPixmap(":/a/tmp/connect.png").scaled(20,20));
ui->record->append("已经链接成功服务器");
ui->connect->setDisabled(true);
ui->disconnect->setDisabled(false);
});
m_status =new QLabel;
ui->statusbar->addWidget(new QLabel("链接状态:"));
ui->statusbar->addWidget(m_status);
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::on_send_clicked()
{
QString string=ui->sendmsg->toPlainText();
m_tcp->write(string.toUtf8());
ui->record->append("客户端说:"+string);
ui->sendmsg->clear();
}
void MainWindow::on_connect_clicked()
{
QString ip=ui->ip->text();
unsigned short port=ui->port_2->text().toUShort();
qDebug("click_on_connect state = %d\n",m_tcp->state());
m_tcp->connectToHost(QHostAddress(ip),port);
if(m_tcp->waitForConnected(1000)) {
qDebug("connected !\n");
ui->record->clear();
}
else
qDebug("connect out time limit !\n");
}
void MainWindow::on_disconnect_clicked()
{
qDebug("loc1 state = %d\n",m_tcp->state());
m_tcp->disconnectFromHost();
qDebug("loc2 state = %d\n",m_tcp->state());
if(m_tcp->state() == QAbstractSocket::UnconnectedState
|| m_tcp->waitForDisconnected(1000))
qDebug("Disconnected!\n");
else
qDebug("Disconnect fail!\n");
m_tcp->close();
ui->connect->setDisabled(false);
ui->disconnect->setDisabled(false);
}
对于服务器来说:mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QTcpServer>
#include <QTcpSocket>
#include <QLabel>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
ui->port_2->setText("8899");
m_s=new QTcpServer(this);
m_tcp = new QTcpSocket;
connect(m_s,&QTcpServer::newConnection,this,[=](){
m_status->setPixmap(QPixmap(":/a/tmp/connect.png").scaled(20,20));
m_tcp=m_s->nextPendingConnection();
QString client_ip = m_tcp->peerAddress().toString().split("::ffff:")[1];
quint16 client_port = m_tcp->peerPort();
ui->record->append(tr("%1:%2 connected!\n").arg(client_ip).arg(client_port));
connect(m_tcp,&QTcpSocket::readyRead,this,[=](){
QByteArray array = m_tcp->readAll();
ui->record->append("客户端说:"+array);
});
connect(m_tcp,&QTcpSocket::disconnected,this,[=](){
QString client_ip = m_tcp->peerAddress().toString().split("::ffff:")[1];
quint16 client_port = m_tcp->peerPort();
ui->record->append(tr("%1:%2 Disconnected!\n").arg(client_ip).arg(client_port));
m_tcp->disconnectFromHost();
if(m_tcp->state() == QAbstractSocket::UnconnectedState
|| m_tcp->waitForDisconnected(1000))
qDebug("Disconnected!\n");
else
qDebug("Disconnect fail!\n");
m_status->setPixmap(QPixmap(":/a/tmp/disconnect.png").scaled(20,20));
});
});
m_status =new QLabel;
ui->statusbar->addWidget(new QLabel("链接状态:"));
ui->statusbar->addWidget(m_status);
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::on_setlisten_clicked()
{ setWindowTitle("服务器");
unsigned short port=ui->port_2->text().toShort();
m_s->listen(QHostAddress::Any,port);
ui->setlisten->setDisabled(true);
}
void MainWindow::on_send_clicked()
{
QString string=ui->sendmsg->toPlainText();
m_tcp->write(string.toUtf8());
ui->record->append("服务端说:"+string);
ui->sendmsg->clear();
}
四、效果图
|