第一种方式通过ioctl来处理(windows下没找到ioctl):
当select或者epoll检测到套接字有数据过来了,那么就用ioctl进行检测
ioctl(sockfd, FIONREAD, &nread);这个函数可以检测套接字里面有多少数据来了,这个数据了会存在nread中,或者说套接字都有一个接收buff(滑动窗口),他可以检测到这个buff中有多少数据量。
如何ioctl检测到nread=0的话其实对方是有信息传输过来的毕竟它也触发了select的,但是这个信息其实是对方已经关闭了,所以这时候你可以处理对方已经结束的情况。
如果ioctl获取到的数据量还没到达头的时候那么你需要继续接收信息。
如果ioctl获取的大小已经超过头了那么你应该判断头里面应该接收多少的数据量,一直到接受完为止。
模型:
if (FD_ISSET(sockfd, &rset))//表明有数据达到
{
ioctl(sockfd, FIONREAD, &nread);//获取数据量是多少存在nread中
if (nread == 0)//表明对端关闭
{
cout << "ioctl = 0" << endl;
exit(0);
}
if (nread >= sizeof(baseHandle) || recvHeadFlag)//当数据量大于头或者已经接收过头了才能进来
{
/*如果是第一次,则只接收一个头*/
if (!recvHeadFlag)//没接收过头说明第一次只recv一个头
{
if (!recv(sockfd, get_recv(), sizeof(baseHandle), 0))//get_recv表示自己创建一个buff存储接收的头信息这个buff自己创建大一点下面还要用到
{
perror("recv");
exit(0);
}
struct baseHandle *tmp = (struct baseHandle *)recvbuff;
totalLengthOfData = tmp->length;//得到后面需要接收多少数据
remainLengthtoRecv = tmp->length - sizeof(baseHandle);//剩下需要接收的数据,刚开始等于length
recvHeadFlag = true;//表示已经接受过头了
}
/*如果不是第一次,则接收完其他的数据*/
if (recvHeadFlag)
{
nread = recv(sockfd, recvbuff+16+lengthHasRecv, remainLengthtoRecv, 0);//因为头已经存储在buff中了,所以从buff偏移一个头的长度(16)开始接收,刚开始lengthHasRecv是等于0的
if (nread > 0)
{
remainLengthtoRecv -= nread;//下面就是不断的接收,直到remainLengthtoRecv=0为止所有的包信息都存在recvbuff中
lengthHasRecv += nread;
if (remainLengthtoRecv == 0)
{
recvHeadFlag = false;//一个包读完了
remainLengthtoRecv = totalLengthOfData;//需要接收的长度还原
lengthHasRecv = 0;
m_readList->packPush(recvbuff);//最后将包存储到链表中以供后面解析使用
}
}else
{
perror("recv");
exit(0);
}
}
}
}
第二种方式没有ioctl判断滑动窗口
maxLen = 10240;
if(!g_cache) {
g_cache = malloc(maxLen);
}
char* cache = g_cache;
memcpy(cache + pos, cmd, cmdLen);
pos += cmdLen;
上面是接收一个一个的数据包,不规律的
while (pos >= PDU_HEADER_SIZE) {//接收的数据量已经超过一个包头的长度
if(pos >= length) {//判断数据量有没有超过包头里面的数据的长度
//超过了,则开始处理一个包的数据
pos -= length;
memmove(cache,cache+length,maxLen-length);//处理完之后将缓冲区整体往前移动一个length的距离,可以开始处理下一个数据包了
} else {
break;//数据量没满一个头的长度就继续接收
}
}