day6
qt网络编程(udp数据报,tcp服务器收发,tcp登录账号界面)
重点,分辨协议,分包捡包,错误
1—–UDP简易服务器收发数据报
udpclient
如图.pro 文件,补充network
包含头文件
#include <QUdpSocket>
#include<QtWidgets>
声明套接字
private:
QUdpSocket udpsocket;
ui设计,槽函数实现
QByteArray buffer;//缓冲数组
QDataStream out(&buffer,QIODevice::ReadWrite);//打开数组并绑定到二进制流 out<<ui->lineEdit_3->text();//把数据写到流里
udpsocket.writeDatagram(buffer,
QHostAddress(ui->lineEdit_3->text()),
ui->lineEdit_2->text().toInt());
//udp根据QHostAddress和port提供的地址,发送buffer里的内容
udpserver
.pro += network
包含头文件
#include <QUdpSocket>
#include<QtWidgets>
声明槽函数和套接字
private slots:
void udpread();
private:
QUdpSocket udpsocket;
构造函数里初始化服务器
udpsocket.bind(7788);//设置端口号
connect(&udpsocket,
SIGNAL(readyRead()),//可读信号
this,
SLOT(udpread()));//接收数据的槽函数
槽函数接收信息并显示
QByteArray buffer;//缓冲数组
while(udpsocket.hasPendingDatagrams())//至少一个数据块可以被接收吗
{
buffer.resize(udpsocket.pendingDatagramSize());//设置数组大小=数据块大小
QHostAddress addr;//客户端地址
quint16 port;//端口号
udpsocket.readDatagram(buffer.data(),//数组数据
buffer.size(),//数组大小
&addr,//客户端地址
&port);//端口号
QDataStream in(&buffer,QIODevice::ReadOnly);//只读打开缓冲数组,并绑定到流
QString str;//定义字符串类对象,把流里的数据放到字符串中
in>>str;
ui->textBrowser->insertPlainText(addr.toString()+"\n");//将客户端地址转换成字符串,以富文本形式插入到文本浏览器中
ui->textBrowser->insertPlainText(str+"\n");//文本浏览器中插入富文本字符串
}
2—–TCP简易服务器收发数据
tcpclient
.pro += network
包含头文件,声明槽和套接字
#include <QTcpSocket>
#include <QtWidgets>
//
private slots:
void sendRequest();//向服务器发送连接请求
void readResponse();//接收服务器的响应
private:
QTcpSocket tcpSocket;
构造函数里先连接信号与槽
//当tcpSocket连接上服务器后,调用槽函数sendRequest
connect(&tcpSocket,
SIGNAL(connected()),
this,
SLOT(sendRequest()));
//当tcpSocket上有数据可读时,调用槽函数
connect(&tcpSocket,
SIGNAL(readyRead()),
this,
SLOT(readResponse()));
实现槽函数
void Dialog::on_pushButton_clicked()
{
//连接服务器
tcpSocket.connectToHost("127.0.0.1",7788);
}
void Dialog::sendRequest()
{
//tcpSocket绑定到文本流
QTextStream out(&tcpSocket);
out << ui->lineEdit->text();
}
void Dialog::readResponse()
{
//将文本流in和tcpSocket绑定起来
QTextStream in(&tcpSocket);
//显示接收到的所有信息
ui->textBrowser->insertPlainText(in.readAll()+"\n");
tcpSocket.close();
}
tcpserver
改.pro文件步骤相同,包含头文件
#include
#include
#include
声明槽函数和监听套接字、
private slots:
void newClient();//客户端开始发送连接请求时,创建连接套接字与客户端连接
void readClient();//监听套接字监听连接套接字,读取客户端信息
private:
QTcpServer tcpServer; //产生一个监听套接字
构造函数里,需要连接信号与槽
//端口号7788绑定服务器端任意网卡地址信息
tcpServer.listen(QHostAddress::Any,7788);
//当tcpServer上有新的连接时,调用槽函数
connect(&tcpServer,
SIGNAL(newConnection()),
this,
SLOT(newClient()));
实现槽函数
void Dialog::newClient()
{
//从tcpServer里面取出一个连接请求,建立一个新的连接套接字
QTcpSocket *tcpSocket=tcpServer.nextPendingConnection();
//连接读准备信号和槽函数,当连接套接字上有数据可读,调用readClient槽函数
connect(tcpSocket,SIGNAL(readyRead()),this,SLOT(readClient()));
//当连接套字断开连接时,销毁该套接字
connect(tcpSocket,SIGNAL(disconnected()),tcpSocket,SLOT(deleteLater()));
}
void Dialog::readClient()
{
//需要得到要读哪个套接字
//获取信号的发送者,并强制转换为QTcpSocket*
QTcpSocket *tcpSocket=qobject_cast<QTcpSocket*>(sender());
//将文本流in跟套接字绑定起来
QTextStream in(tcpSocket);
//用字符串对象str保存流in读取到的所有信息
QString str(in.readAll());
ui->textBrowser->insertPlainText(str+"\n");
//把数据发回给客户端
in << str;
}
3—–TCP本地服务器登录界面—–
loginclient
客户端需要实现登录和注册按钮,通过TCP协议发送数据到服务器,然后接收服务器数据。每一次发送的数据长度固定。
所以,头文件声明:
private slots:
void on_pushButton_clicked();
void send();
void read();
private:
QTcpSocket tcpSocket;
quint16 nextBlockSize;//数据包大小
实现“客户端通过TCP协议发送数据到服务器,客户端接收服务器数据”的功能,需要在构造函数里连接信号与槽
connect(&tcpSocket,SIGNAL(connected()),this,SLOT(send()));
connect(&tcpSocket,SIGNAL(readyRead()),this,SLOT(read()));
设置按钮1,客户端连接本地服务器(端口号7788)
void Dialog::on_pushButton_clicked()
{
tcpSocket.connectToHost("127.0.0.1",7788);
nextBlockSize=0;//清零
}
客户端发送连接请求到服务器,数据包格式为: 前两个字节表示数据包长度 L/R 用户名 密码。
void Dialog::send()
{
//定义缓冲数组
QByteArray buffer;
//数组绑定到流
QDataStream out(&buffer,QIODevice::WriteOnly);
//0标记开头写到流
out<<quint16(0);
//判断登录还是注册,决定写L/R到流
if(ui->radioButton->isChecked())
{
out<<quint8('L');
}else
{
out<<quint8('R');
}
//写入用户名 密码
out<<ui->lineEdit->text()<<ui->lineEdit_2->text();
//找到0的位置,即开头,用于计算数据包大小
out.device()->seek(0);
//发送数据包大小(数组总长度-元素单位长度)
out<<quint16(buffer.size()-sizeof(quint16));
//把缓冲数组写到套接字上,即发送数据包到服务器
tcpSocket.write(buffer);
}
接收服务器数据同样要先拆数据包。
void Dialog::read()
{
QDataStream in(&tcpSocket);
//第一次
if(nextBlockSize==0)
{
//套接字中有多少字节的数据可读
if(tcpSocket.bytesAvailable()<sizeof(quint16))
{
return;
}
in>>nextBlockSize;
}
if(tcpSocket.bytesAvailable()<nextBlockSize)
{
return;
}
quint8 type;//接收字符L/R
QString str,message;//str是接收到的用户名 密码
in>>type>>str;
if(type==quint8('L'))
{
message="Login";
}
else if(type==quint8('R'))
{
message="Register";
}
//弹出提示对话框
QMessageBox::information(this,message,str);
//关闭套接字
tcpSocket.close();
}
loginserver
服务器端有监听套接字,当有客户端发送连接请求时,服务器端生成一个连接套接字与之连接。
当有数据发送时,连接套接字接收数据,并由监听套接字监听。
private slots:
void newclient();//连接客户端
void readclient();//接收数据
private:
QTcpServer tcpServer;//监听套接字
quint16 nextBlockSize;//数据包大小
构造函数里,设置连接地址,连接信号与槽
tcpServer.listen(QHostAddress::Any,7788);
connect(&tcpServer,
SIGNAL(newconnection()),
this,
SLOT(newclient()));
创建连接套接字
void Dialog::newclient()
{
//创建一个监听套接字准备好的连接套接字
QTcpSocket *tcpSocket=tcpServer.nextPendingConnection();
//客户端发出可读请求时,服务器读接收客户端的信息
connect(tcpSocket,
SIGNAL(readyRead()),
this,
SLOT(readclient()));
//断开连接时,删除套接字
connect(tcpSocket,
SIGNAL(disconnected()),
tcpSocket,
SLOT(deleteLater()));
nextBlockSize=0;
}
接收客户端的信息,判断是否登录成功
void Dialog::readclient()
{
//根据发包者获得连接套接字信息
QTcpSocket *tcpSocket=qobject_cast<QTcpSocket*>(sender());
//连接套接字绑定流
QDataStream inout(tcpSocket);
//读客户端数据,拆包
if(nextBlockSize==0)
{
if(tcpSocket->bytesAvailable()<sizeof(quint16))
{
return ;
}
inout>>nextBlockSize;
}
if(tcpSocket->bytesAvailable()<nextBlockSize)
{
return;
}
quint8 type;
QString name,password,ans;
inout>>type>>name>>password;
nextBlockSize=0;
//判断接受的name和password与password.dat文件里是否一致
//登录
QFile file("F:/myqt/loginserver/password.dat");
if(!file.open(QIODevice::ReadOnly))
{
QMessageBox::warning(this,"error","open password err");
return ;
}
if(type=='L')
{
QDataStream in(&file);
QString username,keyword;
while(!in.atEnd())
{
in>>username>>keyword;
if(username==name&&password==keyword)
{
QMessageBox::information(this,"login","login success");
//
ans="login success";
//
}else{
QMessageBox::information(this,"login","login fail");
//
ans="login fail";
}
}
}
if(type=='R')
{
QDataStream reg(&file);
QString username,word;
while(!reg.atEnd())
{
reg>>username>>word;
if(username==name)
{
ans="register fail";
QMessageBox::warning(this,"regester","you had regestered");
out<<quint16(0)<<quint8(type)<<ans;
tcpSocket->write(buffer);
return;
}
}
ans="register success";
reg<<name<<password;
}
//发送应答数据包给客户端
out<<quint16(0)<<quint8(type)<<ans;
tcpSocket->write(buffer);
}