【EPOLL 非阻塞SOCKET READ 函数写法】
使用EPOLL进行SOCKET通讯的代码,网上资料写的不明确,专门写了测试代码进行测试,对READ中注意事项进行了总结。
一、LT模式下的READ
READ()返回值:
大于0,正常接收的数据
等于0, SOCKET关闭
等于-1 ,ERRNO == EINTR 重新调用READ( 可以立即调用,也可以退出等下一个LT的触发);ERRNO == EAGAIN 稍后调用(退出等下一个LT的触发,立即调用有可能空转);ERRNO == EWOULDBLOCK(和EAGAIN多数系统是同一值,同EAGAIN处理)
由于非阻塞SOCKET LT模式,READ退出后,只要有数据在SOCKET BUFFER中,就一定会持续LT触发,所以READ退出的判断条件,要求不严格。
示例代码如下:
int Socket_Read(int socketfd, char *buf, int buf_len )
{
int ret;
int num = 0;
int savenum = 0;
if ( NULL == buf )
{
printf("\n +++ [KLC_Socket_Read] pointer is null +++\n");
return(0);
}
while(1)
{
if ( buf_len == num )
{
printf("\n +++ [KLC_Socket_Read] buffer full +++\n");
return(num);
}
ret = read(socketfd, buf + num, buf_len-num);
if (ret == 0)
{
/* exit, then close socket */
return(-1);
}
if (ret >0)
{
num += ret;
continue;
}
if ( ret < 0)
{
/* LT mode */
if ( errno == EINTR || errno == EWOULDBLOCK || errno == EAGAIN)
{
/* 也可以直接退出,不CONTINUE , 两者都可以*/
if ( num != savenum)
{
savenum = num;
continue;
}
break;
}
printf("\n +++ Socket_Read error: %s\n", strerror(errno));
break;
}
}
return(num);
}
二、ET模式下的READ
READ()返回值:
大于0,正常接收的数据,继续接收(READ BUFFER LEN和READ LEN值相等,退出READ, 新分配BUFFER后,在再次调用READ)
等于0, SOCKET关闭
等于-1 ,ERRNO == EINTR 退出READ ( 退出READ, 新分配BUFFER后,在再次调用READ );
ERRNO == EAGAIN 退出READ (退出READ, 新分配BUFFER后,在再次调用READ);
ERRNO == EWOULDBLOCK(和EAGAIN多数系统是同一值,同EAGAIN处理);
ERRNO == EBADF 退出READ, 马上调用CLOSE SOCKET( 在测试中发现有此种情况出现,几率不大)
由于非阻塞SOCKET ET模式,READ退出后,如果数据没有读完,此时不会ET触发,所以要继续读取,一直读到失败( 返回-1, num = 0),并且READ BUFFER 不等于 READ LEN.
示例代码如下:
int Socket_Read(int socketfd, char *buf, int buf_len)
{
int ret;
int num = 0;
if ( NULL == buf )
{
printf("\n +++ [Socket_Read] pointer is null +++\n");
return(0);
}
while(1)
{
if ( buf_len == num )
{
printf("\n +++ [Socket_Read] buffer full +++\n");
return(num);
}
ret = read(socketfd, buf + num, buf_len-num);
if (ret == 0)
{
return(-1);
}
if (ret >0)
{
num += ret;
continue;
}
if ( ret < 0)
{
/* ET mode */
if ( errno == EINTR )
{
break;
}
/* same errno for most version */
if ( errno == EWOULDBLOCK || errno == EAGAIN)
{
break;
}
if ( errno == EBADF)
{
/* close socket */
return(-1);
}
printf("\n +++ [Socket_Read] error: %s\n", strerror(errno);
break;
}
}
return(num);
}
实际代码,需要在Socket_Read(int socketfd, char *buf, int buf_len) 外在加一层WHILE循环,当调用Socket_Read(socketfd, buffer_address, MAX_BUFFERSIZE)后 NUM = 0 的情况下,退出WHILE循环。
static void net_read(int socketfd, int socketindex, NETTASK_S *ptaskinfo)
{
。。。
while(1)
{
bufferindex = Buffer_Alloc(bufferid, bufferlen);
Buffer_GetAddress(bufferid,bufferindex, &buffer_address );
len = Socket_Read(socketfd, buffer_address, MAX_BUFFERSIZE);
if ( len == 0)
{
Buffer_Free( bufferid, bufferindex);
break;
}
if ( len == -1)
{
Buffer_Free( bufferid, bufferindex);
net_close( socketfd, socketindex, ptaskinfo);
break;
}
。。。。。。
BUFFER数据接收处理
。。。。。。
Buffer_Free( bufferid, bufferindex);
continue;
}
return;
}