简述
刚开始学习qt,在学习完网络编程后,觉得还是需要总结一下,毕竟总结才能更加的深刻。
本人第一次在csdn上写博客,想监督一下自己,让自己有一个学习的动力。好了不多说了,
目录
目录
一、网络体系结构模型
OSI模型 TCP/IP协议 模型
上图可以看到tcp/ip模型是将osi模型简化,将原来的七层简化成四层,最右边的图是对应四层经常用到的一些协议。
常用的网络协议有:
- TCP(Transport Control Protocol)传输控制协议
- IP(Internetworking Protocol)网间协议
- UDP(User Datagram Protocol)用户数据报协议
- ICMP(Internet Control Message Protocol)互联网控制信息协议
- SMTP(Simple Mail Transfer Protocol)简单邮件传输协议
- SNMP(Simple Network manage Protocol)简单网络管理协议
- HTTP(Hyper Text Transfer Protocol) 超文本传输协议
- FTP(File Transfer Protocol)文件传输协议
- ARP(Address Resolution Protocol)地址解析协议
这些看看就好,要用的时候查一下,下面主要讲一下tcp/ip协议
二、TCP/IP协议
1、TCP/IP介绍
- TCP/IP协议 q 传输控制/网际协议(Transfer Control Protocol/Internet Protocol) 又称作网络通讯协议
- Internet国际互联网络的基础,RFC793
- 一组协议,通常称它为TCP/IP协议族
- 四个层次:网络接口层、网际层、传输层、应用层
2、TCP/IP结构
3、TCP传输协议
是一种面向连接的传输层协议,它能提供高可靠性通信(即 数据无误、数据无丢失、数据无失序、数据无重复到达的 通信)
适用情况:
- 适合于对传输质量要求较高,以及传输大量数据的通信。
- 在需要可靠数据传输的场合,通常使用TCP协议
- MSN/QQ等即时通讯软件的用户登录账户管理相关的功能 通常采用TCP
三、socket套接字
1、Socket
- 一个编程接口
- 是一种特殊的文件描述符 (everything in Unix is a file) n
- 并不仅限于TCP/IP协议 n
- 面向连接 (Transmission Control Protocol - TCP/IP) n
- 无连接 (User Datagram Protocol -UDP 和 Inter-network Packet Exchange - IPX)
2、在linux中C语言的使用流程
在这里我就不细讲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模型的知识了这里小伙伴们可以去查一查,非阻塞和阻塞的使用体验完全不一样哦。
3、QT中的使用
在大致了解完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 <QWidget>
#include <QTcpServer>
#include <QTcpSocket>
#include <QFile>
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 <QDataStream>
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<QTcpSocket*>(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 <QWidget>
#include <QTcpSocket>
#include <QFile>
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 <QFileDialog>
#include <QFileInfo>
#include <QDataStream>
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<<filesize<<filename;
//设置进度条最大值
ui->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();//关闭文件
}
}