基于QT开发多人聊天项目3:登录功能的实现

  • Post author:
  • Post category:其他




1 本功能实现的效果

客户端通过IP和端口号与服务器建立连接;建立连接以后,客户端封装一个数据包“0x02 0x11 0x03”,发送给服务器;服务器收到这个数据包以后,先将这个数据包读出来“0x02 0x11 0x03”;读出来以后,进行解包。也就是:通过第二个字节,判断发来的数据包是什么类型的,根据类型,执行对应的操作。在这里,就是:当第二个字节是“0x11”的时候,就表示这是客户端发来的登录包;服务器需要回发给客户端服务器为他分配的唯一的ID号。客户端通过信号与槽机制,接受服务器发来的数据包。同样,也需要进行读数据包和解包,并进行相应的操作。在这里,就是显示服务器为他分配的唯一的ID号。

效果:

在这里插入图片描述



2 客户端的功能实现



关键函数1:一旦以服务器建立连接,就发送连接数据包;获取ID
void Widget::sendLogin()
{
    QByteArray buf;
    //组包
    buf.append(PACK_HEAD);
    buf.append(CMD_LOGIN);
    buf.append(PACK_REAR);
    cliSocket->write(buf);
}


关键函数2:解包函数(主要实现的功能是:剔除服务器发来的其他数据,只读取服务器发来的连接应答数据包,并调用解包函数)
void Widget::read_pack()
{
    int begin,end;
    //以追加的方式读,防止服务器一次只发半个包,后面的包覆盖之前的包
    recv_from_server.append(cliSocket->readAll());
    //调用索引函数,查找包头    默认从0开始找
    begin = recv_from_server.indexOf(PACK_HEAD);
    if(begin == -1){//表示结束的数据无效
        //将数据清空
        recv_from_server.clear();
    }else{  //找到包头,包头的位置就是 begin(也就是recv_from_server的下标)
        //调用索引函数,查找包尾    指定从begin开始找
        end = recv_from_server.indexOf(PACK_REAR,begin);
       // if(end == -1){//表示服务器发来的数据不完整,继续接收
        if(end > begin){    //表示读到了一个完整的数据包
           //解析数据包  mid是截取函数。表示传入一个数组指定的区间的数据
            analyze(recv_from_server.mid(begin,end-begin+1));
            //将这个数据包后面的数据保存起来
            recv_from_server = recv_from_server.mid(end+1);
        }
    }
}
//服务器发来的数据包 解析函数
void Widget::analyze(const QByteArray& buf)
{
    //表示接收到的是服务器发来的登录应答;我们需要读出他的ID
    if(buf.at(1)==CMD_LOGIN){
        //从第2 个字节开始截取
        userID = buf.mid(2,4);
        IDEdit->setText(userID);
    }
}



3 服务器端的功能实现

功能类似 2

/*解析包(数据),并组应答包   的函数
参数:文件描述符(用来作为ID,因为文件描述符唯一)输入的数据;输入数据的长度;应答包的数据;应答包数据的长度
放回值:应答包的数据长度*/
int  analyze_data(int fd,char *input_buf,size_t input_len,char *buf,size_t buf_len){
    int index=0;
    //解析登录数据包
    if(input_buf[1]==CMD_LOGIN){
        puts("client login success");
        //组应答包
        buf[index++]=PACK_HEAD;
        buf[index++]=CMD_LOGIN;
        //ID为4字节。为了方便,我们直接使用sptintf
        sprintf(buf+index,"%04d",fd);
        index += 4 ;
        buf[index++]=PACK_REAR;
    }
    return index;
}
/*定义一个读数据包的函数  0x02 xxx  0x03
参数:文件描述符  包的有效数据存放的地  数据长度
返回值:读到的字节数*/
ssize_t read_Packet(int fd,char *buf,size_t size){
    int ret,index=0;
    char c;
    //一个一个字节的读取
    do{    //通过do_while语句,找到包头
        ret =  read(fd,&c,1);
        if(ret<=0)  return ret;
    }while(c!=PACK_HEAD);  //如果是包头,则退出do_while;不是包头,继续do-while
    buf[index++]=c;
    
    for(;index<size;index++){    //通过do_while语句,找到包尾
        ret =  read(fd,&c,1);
        if(ret<=0)  return ret;  //0表示对端关闭;<0:表示读失败
        else{  //表示为在包头和包尾之间的内容
            buf[index] = c;
            if(c==PACK_REAR){
                index++;
                break;
            }
        }
    }     
    //返回读到了多少个字节
    return index;
}



版权声明:本文为cainiaofu原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。