C++ socket非阻塞模式

C++ socket非阻塞模式,第1张

一、前言

初期学习socket的时候,为了方便理解,使用默认的阻塞模式比较多。而实际做项目时,我们必须考虑程序的并发性,非阻塞模式在其中担任着很重要的角色,是必会的点之一。本文不对阻塞IO和非阻塞IO的概念做说明,不了解的请自行了解。下文代码以linux平台为例。

二、设置非阻塞模式

设置非阻塞模式,通过fcntl方法设置,为了保存socket其他设置,一般选择先获取 status flags, 并在其基础上设置O_NONBLOCK属性, 代码如下:

fcntl失败返回值为-1, 同时errno会被设置成对应的错误码。(errno在此不做说明,不了解的自行了解。) 考虑失败的情况,个人注意到网上有些例子(包括ss-libev项目)在 F_GETFL 失败后,给了flags默认值,代码如下:

经过测试,默认情况下,flags得到的值为2,也就是O_RDWR 读写, 而 0 对应的相关宏为O_RDONLY只读,明显不合理。个人感觉,对于一个正常的socket来说,F_GETFL 出错的机会不大吧, 至少我是没遇到过。如果实在出错了,还是建议走错误流程而不是给个默认值。

三、 非阻塞server

server端通常在accept后,我们为客户端连接的fd设置为非阻塞。设置O_NONBLOCK后,recv和send发生了变化。默认阻塞模式下,recv在没有数据可以接收(对方未发数据,或者缓冲区的数据已读完对方没有继续发)情况下,recv会阻塞等待,直到下次有数据发送过来。而非阻塞模式下,recv在没有数据可以接收的时候, recv会直接返回-1, 同时errno会被设置为EAGAIN/EWOULDBLOCK 。同理,非阻塞send也会在对方缓冲区满的情况下直接返回-1并设置errno, 而不是阻塞等待。 非阻塞模式下server代码大致如下:

四、非阻塞client

client除了在send/recv, 还可以在connect前设置非阻塞模式,这样在connect时候可以直接返回。

client 非阻塞connect的时候,如果返回0表示连接成功,如果返回-1, 则需要判断errno 是否为EINPROGRESS,EINPROGRESS表示非阻塞连接不能立刻获取connect结果,后面可使用select/poll/epoll等对socket 可写性进行判断,如果socket已可写,使用 getsockopt(iSocket, SOL_SOCKET, SO_ERROR ,&err, &len)进行判断。。。好像挺麻烦是不是,但是我还是建议在大部分项目中connect前设置非阻塞(小工具之类的就无所谓了,项目中一定要保证效率)。如果使用阻塞模式,有可能的问题:

下面是个非阻塞connect的部分代码, 使用select, 至于poll/epoll请自行搜索代码,跟非阻塞逻辑无关:

void* CTCPClient::AUReceive(void *aInstance)

{

struct timeval tv_out

CTCPClient *pInstance = (CTCPClient *)aInstance

fd_set sockfd

pInstance->m_IsExit = false

char ReceiveDataInfo[1024]={0}

    char Temp[4] = {0}

   

while(pInstance->m_IsExit == false)

{

if(pInstance->m_socket == SOCKETERROR)

{

FD_ZERO(&sockfd)

}

else

{

FD_ZERO(&sockfd)

FD_SET(pInstance->m_socket,&sockfd)

}

fd_set mySet = sockfd

memset(ReceiveDataInfo,0,1024)

int Max_ID = pInstance->m_socket

int position=0

tv_out.tv_sec = 0

tv_out.tv_usec = 1000

if(select(Max_ID+1,&mySet,NULL,NULL,&tv_out)>0) //主要这一句

{

long  nBytesRead = 0

unsigned long nBytesToRecv = pInstance->mreceivebuflen -pInstance->hasrecvlen

pInstance->recvsignal.Wait()

if(pInstance->m_socket == SOCKETERROR)

{

FD_ZERO(&sockfd)

pInstance->recvsignal.Release()

continue

}

nBytesRead =  recv(pInstance->m_socket,(char *)pInstance->mreceivebuf + pInstance->hasrecvlen, nBytesToRecv, 0)

pInstance->recvsignal.Release()

if(nBytesRead == -1 || nBytesRead == 0)

{

pInstance->m_CSocket.SocketClose(pInstance->m_socket)

FD_ZERO(&sockfd)

pInstance->m_socket = SOCKETERROR

continue

}

pInstance->hasrecvlen += nBytesRead

pInstance->m_CLog->ddprintf("CTCPClient","AUReceive",1,"recv a package!")

pInstance->m_CLog->ddprintf("CTCPClient","AUReceive",1,"recvlen is %ld",nBytesRead)

if((pInstance->FindCompletePackage(pInstance->receive,nBytesRead,pInstance->mreceivebuf))==false)

{

continue

}

//printf("validlen is %d\n",pInstance->validlen)

pInstance->hasrecvlen =0

if(pInstance->validlen < 8)

{

pInstance->m_CLog->ddprintf("CTCPClient","AUReceive",1,"receive data is error(len is error)!")

continue

}

for(int i =0  i< pInstance->validlen i++)

{

sprintf(Temp,"%2x-", pInstance->receive[i])

strcat(ReceiveDataInfo, Temp)

}

pInstance->m_CLog->ddprintf("CTCPClient","AUReceive",1,"recv data is:%s",ReceiveDataInfo)

unsigned int recvSN = Char2Int(pInstance->receive)

position+=4

unsigned int recvCMD = Char2Short(pInstance->receive + position)

position+=2

unsigned long buflen = Char2Short(pInstance->receive + position)

position+=2

if(pInstance->validlen - position != buflen)

{

pInstance->m_CLog->ddprintf("CTCPClient","AUReceive",1,"receive data is error(len error)!")

continue

}

stCommand newReceiveCommand

newReceiveCommand.CmdSN = recvSN

//newReceiveCommand.DataBuffer = ""

bool rest = pInstance->FindSentCommand(newReceiveCommand)

if(rest == false)

{

pInstance->m_CLog->ddprintf("CTCPClient","AUReceive",1,"receive data is error(SN can not find)!")

continue

}

if(newReceiveCommand.CmdCode != recvCMD)

{

pInstance->m_CLog->ddprintf("CTCPClient","AUReceive",1,"receive data is error(CMD is error)!")

continue

}

char *RecvData = new char[buflen]

memcpy(RecvData,pInstance->receive+position,buflen)

pInstance->UpdateSentCommand(recvSN,buflen,RecvData)

newReceiveCommand.WaitEvent->Release()

pInstance->m_CLog->ddprintf("CTCPClient","AUReceive",1,"send a signal of recv!")

delete[] RecvData

}

else

Csleep(100)

}

//pInstance->m_ThdRecv.ThreadExit()

return NULL

}

这个是recv设置非阻塞的方式,accept也是差不多


欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/tougao/7775401.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2023-04-09
下一篇 2023-04-09

发表评论

登录后才能评论

评论列表(0条)

保存