目录
网络编程
Socket
qt下的socket
qt的TCP连接过程
TCP服务端给客户端传文件举例:
qt的UDP
qt的UDP单播举例:
UDP的广播与组播(多播)
网络编程
三个要素:IP地址、端口号和通信协议
IP地址:网络中的计算机使用IP地址来进行唯一标识,IP地址有IPv4和IPv6两种类型。IPv4采用十进制或二进制表示形式,十进制是一种比较常用的表示形式,如192.158.147.3。IPv6采用十六进制表示形式
端口号:计算机中的应用程序的一个整数数字标号,用来区分不同的应用程序
0 ~ 1024 为被系统使用或保留的端口号,0 ~ 65535为有效的端口号,也就是说我们要对一些程序定义端口号的时候,要选择1024 ~ 65535范围内的整数数字。
MySQL的端口号是3306,SQLServer的端口号是1433,Oracle的端口号是1521。
通信协议就是网络通信中的规则,分为TCP协议(面向连接的)和UDP协议(面向传输数据)两种
Socket
又叫“套接字”,它是计算机之间进行通信的一种约定或一种方式
通过 socket 这种约定,计算机可以接受其他计算机的数据,或者向其他计算机发送数据,里面有很多特殊的函数,是对其他计算机文件的读写操作
“一切皆文件”,都可以用“打开open –> 读写write/read –> 关闭close”模式来操作。 即socket是一种特殊的文件,一些socket函数就是对其进行的操作(读/写IO、打开、关闭)。 Socket()函数返回一个整型的Socket描述符,随后的连接建立、数据传输等操作都是通过该Socket实现的。
qt下的socket
TCP一般发文件,音频等,UDP一般发文字
QT? ? ? ?+= network
LINUX下的TCP:
在LINUX下进行网络编程,我们可以使用LINUX提供的统一的套接字接口。但是这种方法牵涉到太多的结构体,比如IP地址,端口转换等。QT中提供的SOCKET完全使用了类的封装机制,使用户不需要接触底层的各种结构体操作。而且它采用QT本身的信号和槽机制,使编写的程序更容易理解。
qt的TCP连接过程
不管谁发送信息,成功后,对方的通信套接字触发readyRead()
TCP服务端给客户端传文件举例:
传文件流程图
?
?
server? ui 如下:
public:
void sendData();//发数据
private:
QTcpServer *tcpServer;//监听套接字
QTcpSocket *tcpSocket;//通信套接字
QFile file;//文件对象
QString filename;
qint64 fileSize;
qint64 sendSize;//已经发送文件的大小
QTimer timer;//定时器
构造函数中
tcpServer =new QTcpServer(this);
tcpServer->listen(QHostAddress::Any,8888);//监听所有网址,设置server的端口8888
//连接成功自动触发newConnection
connect(tcpServer,&QTcpServer::newConnection,
[=]()
{
tcpSocket =tcpServer->nextPendingConnection();//获取连接进来的socket
QString ip=tcpSocket->peerAddress().toString();
qint16 port =tcpSocket->peerPort();
QString temp =QString("[%1:%2]:zhaoyi成功连接").arg(ip).arg(port);
ui->textEditread->setText(temp); //显示对方的ip和端口
}
);
//关闭定时器,发数据
connect(&timer,&QTimer::timeout,
[=]()
{
timer.stop();
sendData();
}
);
}
//选文件
void server::on_pushButtonxuanwj_clicked()
{
QString filepath=QFileDialog::getOpenFileName(this,"open","../");
if(filepath.isEmpty()==false)//如果选择路径有效
{
//初始化
filename.clear();
fileSize=0;
//获取文件信息
QFileInfo info(filepath);
filename=info.fileName();
fileSize=info.size();
sendSize=0;//发送文件的大小
file.setFileName(filepath);//指定文件名
bool isok=file.open(QIODevice::ReadOnly);//只读打开
if(isok==false)
{
qDebug()<<"只读失败";
}
ui->textEdit->append(filepath);//提示文件路径
}
else
{
qDebug()<<"读文件失败";
}
}
//发文件
void server::on_pushButtonxuan_clicked()
{
//先发文件头信息 文件名##文件大小
QString head=QString("%1##%2").arg(filename).arg(fileSize);
qint64 len=tcpSocket->write(head.toUtf8());
if(len>0)//如果头文件信息发送成功
{
timer.start(100);//间隔一定时间,发真正的文件,防止TCP黏包
}
else
{
qDebug()<<"head发送失败";
file.close();
}
}
void server::sendData()
{
qint64 len=0;
do
{
char buf[4*1024]={0};//发的大小
len=0;
//读多少,发多少
len=file.read(buf,sizeof(buf));
len=tcpSocket->write(buf,len);
sendSize+=len;//累计
}while(len>0);//判断读完
if(sendSize == fileSize)
{
ui->textEdit->append("文件发送完毕");
file.close();
tcpSocket->disconnectFromHost();
tcpSocket->close();
}
}
client?ui 如下:
private:
QTcpSocket *tcpSocket;
QFile file;//文件对象
QString filename;
qint64 fileSize;
qint64 recvSize;//已经接受文件的大小
bool isStart;
?
tcpSocket=new QTcpSocket(this);
isStart=true;
recvSize=0;
connect(tcpSocket,&QTcpSocket::readyRead,
[=]()
{
QByteArray buf=tcpSocket->readAll();//取出所有内容
if(isStart==true)
{
isStart=false;
//解析头文件信息
/*section的功能就是从一段字符串中拿出某一段连续的部分
从位置标识开始从左到右:0,1,2,3,4,5... */
filename=QString(buf).section("##",0,0);
fileSize=QString(buf).section("##",1,1).toInt();//QString->int
//打开文件
file.setFileName(filename);
bool isok=file.open(QIODevice::WriteOnly);
if(isok==false)
{
qDebug()<<"写入错误";
}
}
else//发真正的文件数据
{
qint64 len=file.write(buf);//写入数据
recvSize+=len;
if(recvSize==fileSize)
{
file.close();
QMessageBox::information(this,"ok","写入ok");
tcpSocket->disconnectFromHost();
tcpSocket->close();
}
}
}
);
//连接
void client::on_pushButtonlianjie_clicked()
{
QString ip=ui->lineEditIP->text();
qint16 port=ui->lineEditport->text().toInt();
tcpSocket->connectToHost(QHostAddress(ip),port);
}
LINUX下的UDP
qt的UDP
? ??
qt的UDP单播举例:
只发送到另一个指定地址和端口的UDP客户端,是一对一的数据传输
.cpp
udpSocket =new QUdpSocket(this);
udpSocket->bind(8888);
setWindowTitle("8888");//标题
connect(udpSocket,&QUdpSocket::readyRead,this,&Widget::dealMsg);//自定义槽函数
//接受对方的发来数据
void Widget::dealMsg()
{
char buf[1024]={0};
QHostAddress cliAddr;
quint16 port;
//对方连接“我”,“我”显示对方的ip,端口号和发来的数据
qint64 len= udpSocket->readDatagram(buf,sizeof(buf),&cliAddr,&port);
if(len>0)
{
QString str =QString("[%1:%2] %3").arg(cliAddr.toString()).arg(port).arg(buf);
ui->textEdit->setText(str);
}
}
//send控件
void Widget::on_Buttonsend_clicked()
{
//使用UDP协议进行信息的传输之前不需要建立连接,只需要给出对方的ip地址和端口号
QString ip =ui->lineEditip->text();
qint16 port =ui->lineEditport->text().toInt();
QString str =ui->textEdit->toPlainText();
udpSocket->writeDatagram(str.toUtf8(),QHostAddress(ip),port);
}
UDP的广播与组播(多播)
TCP只有点对点,UDP才有点对点、组播、广播的概念
使用UDP协议进行信息的传输之前不需要建立连接。换句话说就是客户端向服务器发送信息,客户端只需要给出服务器的ip地址和端口号,然后将信息封装到一个待发送的报文中并且发送出去。至于服务器端是否存在,或者能否收到该报文,客户端根本不用管。?
对于广播,同一网络范围内其他所有的UDP客户端都可以收到。单播和广播的实现方式基本相同,只是数据报的目标IP地址设置不同,在广播消息时,只需要将目标地址更换为一个特殊地址,即广播地址QHostAddress::Broadcast,一般为255.255.255.255
对于组播,组内成员(组播IP地址指定的多播组)都可以接收到。使用组播地址,其他的端口绑定、数据报收发功能与单播UDP完全相同。
组播(一对一组):
当多个客户端加入由一个组播地址定义的多播组之后,客户端向组播地址和端口发送数据报,组内成员都可以接收到
224.0.0.0~224.0.0.255为预留的组播地址(永久组地址),地址224.0.0.0保留不做分配,其他地址供路由协议使用 224.0.1.0~224.0.1.255是公用组播地址,可以用于internet 224.0.2.0~238.255.255.255为用户可用的组播地址(临时组地址),全网范围内有效 239.0.0.0~239.255.255.255为本地管理组播地址,仅在特定的本地范围内有效
?
|