福建鞋子做淘宝图片网站宽带推广方案
九、网络
- 基础概念
1.1 TCP/UDP
TCP/UDP
UDP TCP 协议相同点:都存在于传输层,全双工通信
TCP:全双工通信、面向连接、可靠
TCP(即传输控制协议):是一种面向连接的传输层协议,它能提供高可靠性通信(即数据无误、数据无丢失、数据无失序、数据无重复到达的通信)。
高可靠原因:1.三次握手、四次挥手
2. 序列号和应答机制
3. 超时,错误重传机制
适用场景
适合于对传输质量要求较高的通信
在需要可靠数据传输的场合,通常使用TCP协议
MSN/QQ等即时通讯软件的用户登录账户管理相关的功能通常采用TCP协议
UDP:全双工通信、面向无连接、不可靠
UDP(User Datagram Protocol)用户数据报协议,是不可靠的无连接的协议。在数据发送前,因为不需要进行连接,所以可以进行高效率的数据传输。
适用场景
发送小尺寸数据(如对DNS服务器进行IP地址查询时)
适合于广播/组播式通信中。
MSN/QQ/Skype等即时通讯软件的点对点文本通讯以及音视频通讯通常采用UDP协议
1.2 IP地址与端口号
IP地址
1. IP地址是Internet中主机的标识,本质: 二进制
IP地址一般分割为 4个八位二进制的数(4字节-32位)
2. Internet中的主机要与别的机器通信必须具有一个IP地址
3. IP地址(长度)为32位(IPv4),128位(IPv6),目前ipv6还未普及,主要学习ipv4
4. 每个数据包都必须携带目的IP地址和源IP地址,路由器依靠此信息为数据包选择路由
5. 表示方法 : 点分十进制
点分十进制表示就是用4组从0~255的数字,来表示一个IP地址。
端口号Port
1. 为了区分一台主机接收到的数据包应该给哪个进程来进行处理,使用端口号来区别
(通过 IP地址 找到哪台主机 通过 port端口号 来找到哪台主机的哪个进程)
2. TCP端口号与UDP端口号独立 (UDP port为8888,TCP port也可为8888 )
3. 端口号一般由IANA (Internet Assigned Numbers Authority) 管理
4. 端口(sin_prot)用2个字节表示 2byte (IP地址占4个字节)
众所周知端口(被占用): 1~1023(1~255之间为众所周知端口,通常由UNIX系统占用)
比如文件传输端口 TFTP 端口号为 69
已登记端口:1024~49151 (----可用来建立与其它主机的会话----)
动态或私有端口:49152~65535 --固定某些服务使用--
自定义的端口号建议使用2000以上,且非豹子号(例如6666/8888等)。
仅以使用2000以上,非豹子号的端口号,本次授课采用8887
2、准备工作
与数据库编程一样,Qt的网络功能需要在.pro项目配置文件中增加network模块。
网络编程主要用到两个类:
- QTcpServer
表示一个基于Tcp的服务器,需要注意的是,此类直接继承了QObject类,不继承QIODevice类,因此不具备任何IO的能力。
- QTcpSocket
表示一个基于TCP的Socket连接,间接继承了QIODevice类,因此使用此类对象进行IO读写。
3、相关函数
// 构造函数,堆内存开辟
QTcpServer::QTcpServer(QObject * parent = 0)
// 开启监听服务,等待客户端发起连接
// 参数1:监听来源(那个网段的IP地址),默认值表示不加任何限制
// 参数2:服务器占用的端口号。默认值为0表示随机选取。8887
bool QTcpServer::listen(const QHostAddress & address = QHostAddress::Any, quint16 port = 0)
// 查看当前是否正在监听
bool QTcpServer::isListening() const
// 关闭监听服务
void QTcpServer::close()
#include "dialog.h"
#include "ui_dialog.h"Dialog::Dialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::Dialog)
{
ui->setupUi(this);
// 抢占前台
setWindowFlags(Qt::WindowStaysOnTopHint);
server = new QTcpServer(this); // 开启监听
bool result = server->listen(QHostAddress::Any,8887);
if(!result)
{
ui->textBrowser->append("监听失败");
return;
}
ui->textBrowser->append("监听开启成功,端口号为8887");
}Dialog::~Dialog()
{
// 关闭监听功能
if(server->isListening()) // 判断是否正在监听
{
server->close();
}
delete ui;
}
客户端
// 构造函数,堆区开辟
QTcpSocket::QTcpSocket(QObject * parent = 0)
// 连接到服务器
// 参数1:服务器的IP地址
// 参数2:服务器的端口号
// 参数3:打开模式。默认读写模式
void QAbstractSocket::connectToHost(const QString & hostName,
quint16 port,
OpenMode openMode = ReadWritel)[virtual]
#include "dialog.h"
#include "ui_dialog.h"Dialog::Dialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::Dialog)
{
ui->setupUi(this);
setWindowFlags(Qt::WindowStaysOnTopHint); connect(ui->pushButtonConn,SIGNAL(clicked()),
this,SLOT(btnConnClickedSlot()));
connect(ui->pushButtonSend,SIGNAL(clicked()),
this,SLOT(btnSendClickedSlot())); socket = new QTcpSocket(this);
}Dialog::~Dialog()
{
// 如果数据流处于打开状态
if(socket->isOpen())
{
// 如果打开则关闭
socket->close();
}
delete ui;
}void Dialog::btnConnClickedSlot()
{
// 获取用户输入的IP和端口
QString ip = ui->lineEditIp->text();
int port = ui->spinBox->value(); // 建立连接
socket->connectToHost(ip,port);
}void Dialog::btnSendClickedSlot()
{}
我们怎么直到,是否有客户端连接了那?
所有通知类,第一时间想到到的信号槽。
// 每当有新的连接可以用时,就会发出此信号
void QTcpServer::newConnection() [signal]
// 连接成功的信号
void QAbstractSocket::connected()[signal]
// 连接失败的信号
void QAbstractSocket::disconnected()[signal]
#include "dialog.h"
#include "ui_dialog.h"Dialog::Dialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::Dialog)
{
ui->setupUi(this);
setWindowFlags(Qt::WindowStaysOnTopHint); connect(ui->pushButtonConn,SIGNAL(clicked()),
this,SLOT(btnConnClickedSlot()));
connect(ui->pushButtonSend,SIGNAL(clicked()),
this,SLOT(btnSendClickedSlot())); socket = new QTcpSocket(this);
connect(socket,SIGNAL(connected()),
this,SLOT(connectedSlot()));
connect(socket,SIGNAL(disconnected()),
this,SLOT(disconnectedSlot()));
}Dialog::~Dialog()
{
// 如果数据流处于打开状态
if(socket->isOpen())
{
// 如果打开则关闭
socket->close();
}
delete ui;
}void Dialog::btnConnClickedSlot()
{
// 获取用户输入的IP和端口
QString ip = ui->lineEditIp->text();
int port = ui->spinBox->value(); // 建立连接
socket->connectToHost(ip,port);
}void Dialog::btnSendClickedSlot()
{}// 连接成功
void Dialog::connectedSlot()
{
// 屏蔽连接按钮
ui->pushButtonConn->setEnabled(false);
ui->pushButtonConn->setText("已连接"); // 释放发送按钮
ui->pushButtonSend->setEnabled(true);
}// 断开连接
void Dialog::disconnectedSlot()
{
// 释放连接按钮
ui->pushButtonConn->setEnabled(true);
ui->pushButtonConn->setText("连接"); // 屏蔽发送按钮
ui->pushButtonSend->setEnabled(false);
}
// 返回与客户端连接的QTcpSocket对象
QTcpSocket * QTcpServer::nextPendingConnection()[virtual]
// 获取对面(客户端)的IP地址
// 返回值为IP地址封装类
QHostAddress QAbstractSocket::peerAddress() const
// 返回对面的(客户端)端口号
quint16 QAbstractSocket::peerPort() const
// 转换为IP地址字符串,在计算机中会自动增加一个前缀
QString QHostAddress::toString() const
客户端.cpp
#include "dialog.h"
#include "ui_dialog.h"Dialog::Dialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::Dialog)
{
ui->setupUi(this);
setWindowFlags(Qt::WindowStaysOnTopHint); connect(ui->pushButtonConn,SIGNAL(clicked()),
this,SLOT(btnConnClickedSlot()));
connect(ui->pushButtonSend,SIGNAL(clicked()),
this,SLOT(btnSendClickedSlot())); socket = new QTcpSocket(this);
connect(socket,SIGNAL(connected()),
this,SLOT(connectedSlot()));
connect(socket,SIGNAL(disconnected()),
this,SLOT(disconnectedSlot()));
}Dialog::~Dialog()
{
// 如果数据流处于打开状态
if(socket->isOpen())
{
// 如果打开则关闭
socket->close();
}
delete ui;
}void Dialog::btnConnClickedSlot()
{
// 获取用户输入的IP和端口
QString ip = ui->lineEditIp->text();
int port = ui->spinBox->value(); // 建立连接
socket->connectToHost(ip,port);
}void Dialog::btnSendClickedSlot()
{}// 连接成功
void Dialog::connectedSlot()
{
// 屏蔽连接按钮
ui->pushButtonConn->setEnabled(false);
ui->pushButtonConn->setText("已连接"); // 释放发送按钮
ui->pushButtonSend->setEnabled(true);
}// 断开连接
void Dialog::disconnectedSlot()
{
// 释放连接按钮
ui->pushButtonConn->setEnabled(true);
ui->pushButtonConn->setText("连接"); // 屏蔽发送按钮
ui->pushButtonSend->setEnabled(false);
}
服务端.cpp
#include "dialog.h"
#include "ui_dialog.h"Dialog::Dialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::Dialog)
{
ui->setupUi(this);
// 抢占前台
setWindowFlags(Qt::WindowStaysOnTopHint);
server = new QTcpServer(this); // 开启监听
bool result = server->listen(QHostAddress::Any,8887);
if(!result)
{
ui->textBrowser->append("监听失败");
return;
}
ui->textBrowser->append("监听开启成功,端口号为8887");
connect(server,SIGNAL(newConnection()),this,
SLOT(newConnectionSlot()));
}Dialog::~Dialog()
{
// 关闭监听功能
if(server->isListening()) // 判断是否正在监听
{
server->close();
}
delete ui;
}void Dialog::newConnectionSlot()
{
// 如果不是第一次连接,就踢掉上一个人
if(socket != NULL)
{
// 踢掉上一个人
socket->close();
} // 保存当前连接对象
socket = server->nextPendingConnection(); // 获取对面的IP地址
QString ip = socket->peerAddress().toString(); // 获取对面的端口号
quint16 port = socket->peerPort(); ip.prepend("新连接来了!").append(":").append(QString::number(port)); ui->textBrowser->append(ip);
}
// 构造函数
// 参数事Qt的读写类,可以是QFile,也可以是QTcpSocket........
QTextStream::QTextStream(QIODevice * device)
// 输出字符串内容,支持链式调用
QTextStream & operator<<(const QString & string)
// 有数据可读时发射
void QIODevice::readyRead()[signal]
// 读取最大长度为maxlen个QChar的内容,返回值为读取的字符串
QString QTextStream::read(qint64 maxlen)
// 读取所有字符
QString QTextStream::readAll()
// 一次读取一行文本
// 参数为一行文本的最大字符数
QString QTextStream::readLine(qint64 maxlen = 0)
dialog.cpp 服务端
void Dialog::readyReadSlot()
{
// 创建文本对象
QTextStream input(socket);
// 读取内容
QString text = input.read(128);
ui->textBrowser->append(text);
}
dialog.cpp 客户端
// 发送信息
void Dialog::btnSendClickedSlot()
{
QString text = ui->lineEditSend->text();
if(text == "")
{
QMessageBox::warning(this,"提示","请输入发送的内容");
return;
} // 方法一:QTextStream 文本流
// QTextStream直接使用Unicode编码,适合Qt与Qt之间通信。
// 简化文本的读写操作
QTextStream output(socket);
// 发送数据
output << text;
}
dialog.cpp 服务端
void Dialog::readyReadSlot()
{
// // 创建文本对象
// QTextStream input(socket);
// // 读取内容
// QString text = input.read(128);
// ui->textBrowser->append(text); // 读取所有内容
QByteArray bufer = socket->readAll();
// QByteArray -> QString
QString text(bufer);
// 显示
ui->textBrowser->append(text);
}
dialog.cpp 客户端
void Dialog::btnSendClickedSlot()
{
QString text = ui->lineEditSend->text();
if(text == "")
{
QMessageBox::warning(this,"提示","请输入发送的内容");
return;
} // 方法一:QTextStream 文本流
// QTextStream直接使用Unicode编码,适合Qt与Qt之间通信。
// 简化文本的读写操作
// QTextStream output(socket);
// // 发送数据
// output << text; // 方法二、QByteArray
// 以字节为单位,可以与其他编程语言通信
// 可以操作非文本内容
// QString -> QByteArray
QByteArray buffer = text.toUtf8();
socket->write(buffer);
}