QTday6

day6

qt网络编程(udp数据报,tcp服务器收发,tcp登录账号界面)

重点,分辨协议,分包捡包,错误

1—–UDP简易服务器收发数据报

udpclient

增加network

如图.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);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值