溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

服務(wù)器編程心得(四)—— 如何將socket設(shè)置為非阻塞模式

發(fā)布時(shí)間:2020-07-08 19:15:54 來(lái)源:網(wǎng)絡(luò) 閱讀:958 作者:張小方32 欄目:建站服務(wù)器
  1. windows平臺(tái)上無(wú)論利用socket()函數(shù)還是WSASocket()函數(shù)創(chuàng)建的socket都是阻塞模式的:
    
    SOCKET WSAAPI socket(
    _In_ int af,
    _In_ int type,
    _In_ int protocol
    );

SOCKET WSASocket(
In int af,
In int type,
In int protocol,
In LPWSAPROTOCOL_INFO lpProtocolInfo,
In GROUP g,
In DWORD dwFlags
);


linux平臺(tái)上可以在利用socket()函數(shù)創(chuàng)建socket時(shí)指定創(chuàng)建的socket是異步的:

int socket(int domain, int type, int protocol);

在type的參數(shù)中設(shè)置SOCK_NONBLOCK標(biāo)志即可,例如:

int s = socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, IPPROTO_TCP);

2. 另外,windows和linux平臺(tái)上accept()函數(shù)返回的socekt也是阻塞的,linux另外提供了一個(gè)accept4()函數(shù),可以直接將返回的socket設(shè)置為非阻塞模式:

int accept(int sockfd, struct sockaddr addr, socklen_t addrlen);

int accept4(int sockfd, struct sockaddr addr, socklen_t addrlen, int flags);

只要將accept4()最后一個(gè)參數(shù)flags設(shè)置成SOCK_NONBLOCK即可。

3. 除了創(chuàng)建socket時(shí),將socket設(shè)置成非阻塞模式,還可以通過(guò)以下API函數(shù)來(lái)設(shè)置:

linux平臺(tái)上可以調(diào)用fcntl()或者ioctl()函數(shù),實(shí)例如下:

fcntl(sockfd, F_SETFL, fcntl(sockfd, F_GETFL, 0) | O_NONBLOCK);

ioctl(sockfd, FIONBIO, 1); //1:非阻塞 0:阻塞


參考:?http://blog.sina.com.cn/s/blog_9373fc760101i72a.html

但是網(wǎng)上也有文章說(shuō)(文章鏈接:http://blog.csdn.net/haoyu_linux/article/details/44306993),
linux下如果調(diào)用fcntl()設(shè)置socket為非阻塞模式,不僅要設(shè)置O_NONBLOCK模式,還需要在接收和發(fā)送數(shù)據(jù)時(shí),需要使用MSG_DONTWAIT標(biāo)志,即在recv,recvfrom和send,sendto數(shù)據(jù)時(shí),將flag設(shè)置為MSG_DONTWAIT。是否有要進(jìn)行這種雙重設(shè)定的必要,筆者覺(jué)得沒(méi)有這個(gè)必要。因?yàn)閘inux man手冊(cè)上recv()函數(shù)的說(shuō)明中關(guān)于MSG_DONTWAIT說(shuō)明如下:

Enables nonblocking operation; if the operation would block, the call fails with the error EAGAIN or EWOULDBLOCK (this can also be enabled using the O_NONBLOCK flag ?with?the F_SETFL fcntl(2)).

通過(guò)這段話我覺(jué)得要么通過(guò)設(shè)置recv()函數(shù)的flags標(biāo)識(shí)位為MSG_DONTWAIT,要么通過(guò)fcntl()函數(shù)設(shè)置O_NONBLOCK標(biāo)識(shí),而不是要同時(shí)設(shè)定。

windows上可調(diào)用ioctlsocket函數(shù):

int ioctlsocket(
In SOCKET s,
In long cmd,
Inout u_long *argp
);

將cmd參數(shù)設(shè)置為 FIONBIO,*argp=0即設(shè)置成阻塞模式,而*argp非0即可設(shè)置成非阻塞模式。但是windows平臺(tái)需要注意一個(gè)地方,如果你對(duì)一個(gè)socket調(diào)用了WSAAsyncSelect()或WSAEventSelect()函數(shù)后,你再調(diào)用ioctlsocket()函數(shù)將該socket設(shè)置為非阻塞模式,則會(huì)失敗,你必須先調(diào)用WSAAsyncSelect()通過(guò)設(shè)置lEvent參數(shù)為0或調(diào)用WSAEventSelect()通過(guò)設(shè)置lNetworkEvents參數(shù)為0來(lái)分別禁用WSAAsyncSelect()或WSAEventSelect()。再次調(diào)用ioctlsocket()將該socket設(shè)置成阻塞模式才會(huì)成功。因?yàn)檎{(diào)用WSAAsyncSelect()或WSAEventSelect()函數(shù)會(huì)自動(dòng)將socket設(shè)置成非阻塞模式。msdn上的原話是:

The WSAAsyncSelect and WSAEventSelect functions automatically set a socket to nonblocking mode. If WSAAsyncSelect or WSAEventSelect has been issued on a socket, then any attempt to use ioctlsocket to set the socket back to blocking mode will fail with WSAEINVAL.

To set the socket back to blocking mode, an application must first disable WSAAsyncSelect by calling WSAAsyncSelect with the lEvent parameter equal to zero, or disable WSAEventSelect by calling WSAEventSelect with the lNetworkEvents parameter equal to zero.

網(wǎng)址:https://msdn.microsoft.com/en-us/library/windows/desktop/ms738573(v=vs.85).aspx

4. 在看實(shí)際項(xiàng)目中以前一些前輩留下來(lái)的代碼中,通過(guò)在一個(gè)循環(huán)里面調(diào)用fcntl()或者ioctlsocket()函數(shù)來(lái)socket的非阻塞模式的,代碼如下:

for (;;)
{
#ifdef UNIX
on=1;
if (ioctlsocket(id, FIONBIO, (char *)&on) < 0)
#endif

#ifdef WIN32
unsigned long on_windows=1;
if (ioctlsocket(id, FIONBIO, &on_windows) < 0)
#endif

#ifdef VOS
int off=0;
if (ioctlsocket(id, FIONBIO, (char *)&off) <0)
#endif
{
if (GET_LAST_SOCK_ERROR() == EINTR)
continue;
RAISE_RUNTIME_ERROR("Can not set FIONBIO for socket");
closesocket(id);
return NULL;
}
break;
}


是否有必要這樣做,有待考證。
向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI