简述
刚开始学习qt,在学习完网络编程后,觉得还是需要总结一下,毕竟总结才能更加的深刻。
本人第一次在csdn上写博客,想监督一下自己,让自己有一个学习的动力。好了不多说了,
目录
目录
简述
一、网络体系结构模型
二、TCP/IP协议
1、TCP/IP介绍
2、TCP/IP结构
3、TCP传输协议
三、socket套接字
1、Socket
2、在linux中C语言的使用流程
3、QT中的使用
OSI模型 TCP/IP协议 模型
上图可以看到tcp/ip模型是将osi模型简化,将原来的七层简化成四层,最右边的图是对应四层经常用到的一些协议。
常用的网络协议有:
这些看看就好,要用的时候查一下,下面主要讲一下tcp/ip协议
是一种面向连接的传输层协议,它能提供高可靠性通信(即 数据无误、数据无丢失、数据无失序、数据无重复到达的 通信)
适用情况:
在这里我就不细讲c/c++中的使用,因为我们有QT这个框架,QT中帮助我们封装了很多socket的库,使用起来很方便。但是我还是要讲一下,在c语言中linux环境下TCP通信息的流程,因为只有知道了底层调用的一些原理,用起框架来才更加的顺手。
常用接口函数:
socket() 创建套接字
bind() 绑定本机地址和端口
connect() 建立连接
listen() 设置监听端口
accept() 接受TCP连接
recv(), read(), recvfrom() 数据接收
send(), write(), sendto() 数据发送
close(), shutdown() 关闭套接字
在这里不讲这些函数的用法,可以自己查到例子,下面直接给出流程吧
从上图大家就可以很清楚的看到服务器和客户端的运行流程,对于客户端的bind哪一步文档上是说可以bind可以不bind,一般我们都不去将客户端bind
在这里我要说一下,对于系统编程中socket套接字都是默认阻塞的,因此accept这个函数会阻塞在哪里一直等到有客户端连接上来,还有读操作也是阻塞状态的。想要解除套接字阻塞,那就需要用到IO模型的知识了这里小伙伴们可以去查一查,非阻塞和阻塞的使用体验完全不一样哦。
在大致了解完socket的是怎么回事之后,我们就可以来学习QT中TCP通信的一些简单的应用了。
我们知道tcp通信的流程是
1)、服务器:申请套接字 -> 绑定套接字 -> 监听套接字 -> 接收连接 -> 开始发送数据接收数据
2)、客户端:申请套接字 -> 建立连接 -> 发送数据 和 接收数据
而在qt中将,这些操作都被封装成在一个模块中,所以我们就没有这么复杂的操作,大致流程如下
1)、服务器:
1、启动服务器,即监听
mserver.listen(QHostAddress::Any,8080);
2、连接newConnection这个信号判断是否有客户端连接进来
connect(&mserver, &QTcpServer::newConnection, this, &FileRecv::new_client);
3、创建套接字
QTcpSocket *msocket = mserver.nextPendingConnection();
2)、客户端
1、初始化一个套接字对象
2、连接connected这个信号判断是否连接成功
3、对套接字进行读写
下面我就一个传输文件的例子来讲解qt中怎么使用tcp通信。
首先我们给qt程序添加模块如下
QT += core gui network
在传输文件的时候我们要考虑到文件很大一次传过去数据容易丢失,因此我们分多次来传输。首先,我们封装一个文件数据头
文件名,文件大小。先将文件的头信息发送过去,在分多次发送文件的内锅过去。对于接收端来说,我们先接收头信息,在接收文件内容信息。
下面直接添加代码 server :
#ifndef FILERECV_H
#define FILERECV_H
#include
#include
#include
#include
namespace Ui {
class FileRecv;
}
class FileRecv : public QWidget
{
Q_OBJECT
public:
explicit FileRecv(QWidget *parent = nullptr);
~FileRecv();
protected slots:
void read_data();
void new_client();
private:
Ui::FileRecv *ui;
QTcpServer mserver;
QFile file;
QString filename;
quint64 filesize;
quint64 recvsize;
};
#endif // FILERECV_H
#include "filerecv.h"
#include "ui_filerecv.h"
#include
FileRecv::FileRecv(QWidget *parent) :
QWidget(parent),
ui(new Ui::FileRecv)
{
ui->setupUi(this);
//关联客户端连接信号
connect(&mserver, &QTcpServer::newConnection, this, &FileRecv::new_client);
//启动服务器
mserver.listen(QHostAddress::Any,8080);
}
FileRecv::~FileRecv()
{
delete ui;
}
void FileRecv::new_client()
{
//创建与客户端通信的套接字
QTcpSocket *msocket = mserver.nextPendingConnection();
//关联读数据信号readyRead
connect(msocket, &QTcpSocket::readyRead, this, &FileRecv::read_data);
filesize = 0;
recvsize = 0;
}
void FileRecv::read_data()
{
QTcpSocket *msocket = dynamic_cast(sender());
if(filesize == 0) //表达第一次读取数据--读文件信息
{
QByteArray array = msocket->readAll();
QDataStream stream(&array, QIODevice::ReadOnly);//把套接字与数据流绑定
stream>>filesize>>filename;//获取文件大小, 文件名
//设置进度条最大值
ui->progressBar->setMaximum(filesize);
//打开文件
file.setFileName(filename);
file.open(QIODevice::WriteOnly);
//显示进度条
this->show();
}
//读文件内容
if(recvsize < filesize)
{
//读取一段写一段
QByteArray array = msocket->readAll();
file.write(array);
recvsize += array.size();
//更新进度条
ui->progressBar->setValue(recvsize);
}
if(recvsize == filesize)
{
//读完,关闭文件
file.close();
//关闭套接字
msocket->disconnectFromHost();
//隐藏进度条
this->hide();
}
}
界面,添加一个进度条
客户端:
界面
//client.h
#ifndef FILESEND_H
#define FILESEND_H
#include
#include
#include
namespace Ui {
class FileSend;
}
class FileSend : public QWidget
{
Q_OBJECT
public:
explicit FileSend(QWidget *parent = nullptr);
~FileSend();
private slots:
void on_selectBt_clicked();
void on_sendBt_clicked();
void send_file_head();
void send_file_text();
private:
Ui::FileSend *ui;
QTcpSocket msocket;
QFile file;
QString filename;
quint64 filesize;
quint64 sendsize;
};
#endif // FILESEND_H
//client.cpp
#include "filesend.h"
#include "ui_filesend.h"
#include
#include
#include
FileSend::FileSend(QWidget *parent) :
QWidget(parent),
ui(new Ui::FileSend)
{
ui->setupUi(this);
//当客户端连接成功会发送connected信号, 当客户端掉线会发送disconnected信号
connect(&msocket, &QTcpSocket::connected, this, &FileSend::send_file_head);
//当套接字发送完毕会发送一个信号bytesWritten
connect(&msocket, &QTcpSocket::bytesWritten, this, &FileSend::send_file_text);
}
FileSend::~FileSend()
{
delete ui;
}
void FileSend::on_selectBt_clicked()
{
//通过文件对话框获取文件路径
QString filepath = QFileDialog::getOpenFileName(this);
ui->fileEdit->setText(filepath);
}
void FileSend::on_sendBt_clicked()
{
//连接服务器
msocket.connectToHost(ui->ipedit->text(), ui->portedit->text().toUShort());
//发送文件信息
qDebug()<<"connect";
//初始化
filesize = 0;
sendsize = 0;
}
//发送文件头信息
void FileSend::send_file_head()
{
//发送文件名,文件大小
QFileInfo info(ui->fileEdit->text());//文件信息对象
filename = info.fileName();
filesize = info.size();
QByteArray array; //空间
//把array与数据流绑定
QDataStream stream(&array, QIODevice::WriteOnly);
stream<progressBar->setMaximum(filesize);
//打开文件准备读取数据发送
file.setFileName(ui->fileEdit->text());
file.open(QIODevice::ReadOnly);
//发送
msocket.write(array);
}
void FileSend::send_file_text()
{
if(sendsize < filesize)
{
QByteArray array = file.read(1024*10);//读取一段内容
msocket.write(array);//发送一段内容
sendsize += array.size();
//设置进度条
ui->progressBar->setValue(sendsize);
}
if(sendsize == filesize)
{
file.close();//关闭文件
}
}