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 版权协议,转载请附上原文出处链接和本声明。