서버 프로그래밍 소감 (4) - socket을 비차단 모드로 설정하는 방법

3960 단어
1. 윈도우즈 플랫폼에서 socket() 함수든 WSASocket() 함수를 이용하여 만든 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 플랫폼에서 socket () 함수를 이용하여 socket을 만들 때 지정한 socket은 비동기적입니다.
int socket(int domain, int type, int protocol);

type 매개변수에서 SOCK_ 설정NONBLOCK 로고는 다음과 같습니다.
int s = socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, IPPROTO_TCP);

2. 또한 윈도우즈와 linux 플랫폼에서 accept () 함수가 되돌아오는 socekt도 막힌다. linux는 되돌아오는 socket을 비막힘 모드로 설정할 수 있는 accept4 () 함수를 제공한다.
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
 
int accept4(int sockfd, struct sockaddr *addr, socklen_t *addrlen, int flags);

accept4 () 마지막 매개변수 플래그를 SOCK_로 설정하면NONBLOCK이면 됩니다.
3. socket을 만들 때 socket을 비차단 모드로 설정하는 것 외에 다음 API 함수를 통해 설정할 수 있습니다.
linux 플랫폼에서 fcntl () 또는 ioctl () 함수를 호출할 수 있습니다. 예는 다음과 같습니다.
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
하지만 인터넷에도 (링크:http://blog.csdn.net/haoyu_linux/article/details/44306993), linux에서 fcntl () 를 호출하여 socket을 비차단 모드로 설정하면 O_ 뿐만 아니라NONBLOCK 모드, 데이터 수신 및 전송 시 MSG_ 필요DONTWAIT 로고, 즉 recv,recvfrom,send,sendto 데이터에서 flag를 MSG_로 설정합니다DONTWAIT.이런 이중 설정을 할 필요가 있는지 필자는 그럴 필요가 없다고 생각한다.linux man 매뉴얼의 recv () 함수에 대한 설명에서 MSG_DONTWAIT 지침은 다음과 같습니다.
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)).
이 단락을 통해recv() 함수를 설정한flags를 통해 MSG_DONTWAIT, 또는 fcntl() 함수를 통해 O_ 설정동시에 설정하지 않고 NONBLOCK ID입니다.
윈도우즈에서 ioctlsocket 함수를 호출할 수 있습니다.
int ioctlsocket(
  _In_    SOCKET s,
  _In_    long   cmd,
  _Inout_ u_long *argp
);

cmd 매개 변수를 (으)로 설정
FIONBIO, *argp=0은 차단 모드로 설정하고, *argp는 0이 아니면 비차단 모드로 설정할 수 있습니다.그러나 윈도우즈 플랫폼은 한 socket에 대해 WSAAsyncSelect () 또는 WSAEventSelect () 함수를 호출한 후에 ioctlsocket () 함수를 비차단 모드로 설정하면 실패합니다. 먼저 WSAAsyncSelect () 를 호출해야 합니다. lEvent 매개 변수를 0으로 설정하거나 WSAEventSelect () 를 0으로 설정하여 각각 WSAAsyncSelect () 나 WSAEventSelect () 를 비활성화해야 합니다.이 소켓을 차단 모드로 설정하려면 ioctlsocket () 을 다시 호출해야 합니다.WSAAsyncSelect () 또는 WSAEventSelect () 함수를 호출하면 socket이 자동으로 비차단 모드로 설정되기 때문입니다.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.
사이트 주소:https://msdn.microsoft.com/en-us/library/windows/desktop/ms738573(v=vs.85).aspx
4. 실제 프로젝트에서 이전 선배들이 남긴 코드를 보면 한 순환에서 fcntl() 또는 ioctlsocket() 함수를 호출하여 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;
}

이렇게 할 필요가 있는지 없는지는 고증할 필요가 있다.

좋은 웹페이지 즐겨찾기