Microsoft MVP성태의 닷넷 이야기
글쓴 사람
정성태 (techsharer at outlook.com)
홈페이지
첨부 파일
 
(연관된 글이 4개 있습니다.)

C# - IPv4, IPv6를 모두 지원하는 서버 소켓 생성 방법

코드는 다음과 같이 간단합니다.

using (Socket sock = new Socket(SocketType.Stream, ProtocolType.Tcp))
{
    sock.Bind(new IPEndPoint(IPAddress.IPv6Any, 9982));

    // ...[생략]...
}

보통 Socket 생성자에 AddressFamily를 함께 지정하는데요,

// IPv4
Socket sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

// IPv6
Socket sock = new Socket(AddressFamily.InterNetworkV6, SocketType.Stream, ProtocolType.Tcp);

(.NET 4.5부터 지원하는) 저걸 생략한 소켓 생성자의 경우,

public Socket(SocketType socketType, ProtocolType protocolType)
    : this(OSSupportsIPv6 ? AddressFamily.InterNetworkV6 : AddressFamily.InterNetwork, socketType, protocolType)
{
    if (OSSupportsIPv6)
    {
        DualMode = true;
    }
}

보는 바와 같이 운영체제가 IPv6를 지원하는 경우라면 기본적으로 InterNetworkV6로 초기화가 됩니다. 그러면서 동시에 (.NET 4.5부터 지원하는) DualMode 속성이 true로 바뀌는데요,

// NOTE: on *nix, the OS IP stack changes a dual-mode socket back to a
//       normal IPv6 socket once the socket is bound to an IPv6-specific
//       address. This can cause behavioral differences in code that checks
//       the value of DualMode (e.g. the checks in CanTryAddressFamily).
public bool DualMode
{
    get
    {
        if (AddressFamily != AddressFamily.InterNetworkV6)
        {
            return false;
        }
        return ((int)GetSocketOption(SocketOptionLevel.IPv6, SocketOptionName.IPv6Only)! == 0);
    }
    set
    {
        if (AddressFamily != AddressFamily.InterNetworkV6)
        {
            throw new NotSupportedException(SR.net_invalidversion);
        }
        SetSocketOption(SocketOptionLevel.IPv6, SocketOptionName.IPv6Only, value ? 0 : 1);
    }
}

즉, SocketOptionName.IPv6Only 옵션을 끄는 역할을 합니다. only 옵션을 껐다는 것은 IPv6/IPv4 듀얼 모드로 동작한다는 것을 의미합니다. 간단하죠? ^^ 따라서 위와 같이 소켓을 생성하는 것은 다음과 같이 바꿔서 표현할 수도 있습니다.

using (Socket sock = new Socket(AddressFamily.InterNetworkV6, SocketType.Stream, ProtocolType.Tcp))
{
    // Dual Mode Sockets – Never create an IPv4 Socket again
    // https://devblogs.microsoft.com/dotnet/dual-mode-sockets-never-create-an-ipv4-socket-again/
    sock.DualMode = true;
    sock.Bind(new IPEndPoint(IPAddress.IPv6Any, 9982));

    // ...[생략]...
}

기왕이면 코드가 단순한 것이 좋으니 첫 번째 유형으로 쓰고, 단지 그것이 어떤 의미인지만 이 글을 통해 알아두시면 되겠습니다. ^^

참고로, DualMode는 Vista 이후의 운영체제에서만 지원됩니다. 만약 Vista 미만의 운영체제를 지원해야 한다면 IPv4, IPv6 소켓을 각각 생성해 바인딩시켜야 합니다.




IPv6 소켓을 사용하면서 주소는 IPv4를 지정하면,

Socket sock = new Socket(AddressFamily.InterNetworkV6, SocketType.Stream, ProtocolType.Tcp);
sock.Bind(new IPEndPoint(IPAddress.Any, 18000));

이런 오류가 발생합니다.

Unhandled exception. System.Net.Sockets.SocketException (10014): The system detected an invalid pointer address in attempting to use a pointer argument in a call.
   at System.Net.Sockets.Socket.UpdateStatusAfterSocketErrorAndThrowException(SocketError error, String callerName)
   at System.Net.Sockets.Socket.DoBind(EndPoint endPointSnapshot, SocketAddress socketAddress)
   at System.Net.Sockets.Socket.Bind(EndPoint localEP)
   ...[생략]...




DualMode 속성을 지원하지 않는 .NET 4.5 미만에서는 이렇게!

Tired of creating sockets for each protocol, to make your application listen on all addresses for a given port?
; https://web.archive.org/web/20130909044601/http://blogs.msdn.com/b/malarch/archive/2005/11/18/494769.aspx

There is no more pain. Here is a cool new feature supported in Vista and Windows Server Longhorn. By just setting a socketoption, you will be able to use the same socket to accept connections from both ipv4 and ipv6 clients.

The following sample code shows how to configure the server socket.

static void Main(string[] args)
{
    Socket sock = new Socket(AddressFamily.InterNetworkV6, SocketType.Stream, ProtocolType.Tcp);
    sock.SetSocketOption(SocketOptionLevel.IPv6, (SocketOptionName)27, 0);
    sock.Bind(new IPEndPoint(IPAddress.IPv6Any, 8000));
    sock.Listen(4);
    Socket client = sock.Accept();
    Console.WriteLine("Client has connected successfully with the server");
}

Create an IPv6 socket. Set the socket option IPV6_V6Only to false (by default it is true), to enable the socket to accept connections from ipv4 addresses too. Bind and Listen as usual. Now both ipv4 and ipv6 clients can connect to it
More info can be found in http://www.ietf.org/rfc/rfc3493.txt.

This is because Vista has a completely redesigned TCP/IP stack.

Whereas Windows XP and Win2k3 have a dual stack architecture, i.e., the Tcp, Udp layer is separate for both ipv4 and ipv6 protocols, Vista supports the dual IP layer architecture. That is, Only a single Tcp, Udp layer is present for both the ipv4 and ipv6 protocols.




[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]

[연관 글]






[최초 등록일: ]
[최종 수정일: 11/16/2023]

Creative Commons License
이 저작물은 크리에이티브 커먼즈 코리아 저작자표시-비영리-변경금지 2.0 대한민국 라이센스에 따라 이용하실 수 있습니다.
by SeongTae Jeong, mailto:techsharer at outlook.com

비밀번호

댓글 작성자
 



2025-07-11 04시09분
eBPF Mystery: When is IPv4 not IPv4? When it's pretending to be IPv6!
; https://blog.gripdev.xyz/2025/05/06/ebpf-mystery-when-is-ipv4-not-ipv4-when-its-ipv6/

위의 글에서 .NET 5+의 IPv6/IPv4 Dual Mode에 대한 재미있는 시행 착오가 나옵니다.

즉, IPv6 지원을 끈 컴퓨터에서조차도 .NET 5+ 응용 프로그램의 Socket은 기본적으로 IPv6로 소켓을 다루고 있었고, 그것이 가능한 이유가 바로 Dual mode로 설정된 소켓이었기 때문이라고 합니다.

개인적으로는 Dual Mode라는 것이 내부적으로 IPv6 또는 IPv4 소켓으로 자동 생성되는 유형이라고 생각했는데, 그보다는 소켓 자원 자체는 IPv6로 고정인 상태에서 IPv4 통신을 지원하는 유형이었던 것입니다. (참고: RFC1933 - "https://datatracker.ietf.org/doc/html/rfc1933")
정성태

... 61  62  63  64  65  66  67  68  69  70  71  72  [73]  74  75  ...
NoWriterDateCnt.TitleFile(s)
12201정성태3/18/202022406오류 유형: 611. git-credential-manager.exe: Using credentials for username "Personal Access Token". [1]
12200정성태3/18/202022537VS.NET IDE: 145. NuGet + Github 라이브러리 디버깅 관련 옵션 3가지 - "Enable Just My Code" / "Enable Source Link support" / "Suppress JIT optimization on module load (Managed only)"
12199정성태3/17/202020588오류 유형: 610. C# - CodeDomProvider 사용 시 Unhandled Exception: System.IO.DirectoryNotFoundException: Could not find a part of the path '...\f2_6uod0.tmp'.
12198정성태3/17/202024129오류 유형: 609. SQL 서버 접속 시 "Cannot open user default database. Login failed."
12197정성태3/17/202023447VS.NET IDE: 144. .NET Core 콘솔 응용 프로그램을 배포(publish) 시 docker image 자동 생성 - 두 번째 이야기 [1]
12196정성태3/17/202019847오류 유형: 608. The ServicedComponent being invoked is not correctly configured (Use regsvcs to re-register).
12195정성태3/16/202022401.NET Framework: 902. C# - 프로세스의 모든 핸들을 열람 - 세 번째 이야기
12194정성태3/16/202025618오류 유형: 607. PostgreSQL - Npgsql.NpgsqlException: sorry, too many clients already
12193정성태3/16/202022960개발 환경 구성: 485. docker - SAP Adaptive Server Enterprise 컨테이너 실행 [1]
12192정성태3/14/202023442개발 환경 구성: 484. docker - Sybase Anywhere 16 컨테이너 실행
12191정성태3/14/202025037개발 환경 구성: 483. docker - OracleXE 컨테이너 실행 [1]
12190정성태3/14/202019558오류 유형: 606. Docker Desktop 업그레이드 시 "The process cannot access the file 'C:\Program Files\Docker\Docker\resources\dockerd.exe' because it is being used by another process."
12189정성태3/13/202024875개발 환경 구성: 482. Facebook OAuth 처리 시 상태 정보 전달 방법과 "유효한 OAuth 리디렉션 URI" 설정 규칙
12188정성태3/13/202032953Windows: 169. 부팅 시점에 실행되는 chkdsk 결과를 확인하는 방법
12187정성태3/12/202020219오류 유형: 605. NtpClient was unable to set a manual peer to use as a time source because of duplicate error on '...'.
12186정성태3/12/202021191오류 유형: 604. The SysVol Permissions for one or more GPOs on this domain controller and not in sync with the permissions for the GPOs on the Baseline domain controller.
12185정성태3/11/202022328오류 유형: 603. The browser service was unable to retrieve a list of servers from the browser master...
12184정성태3/11/202024605오류 유형: 602. Automatic certificate enrollment for local system failed (0x800706ba) The RPC server is unavailable. [3]
12183정성태3/11/202020920오류 유형: 601. Warning: DsGetDcName returned information for \\[...], when we were trying to reach [...].
12182정성태3/11/202022837.NET Framework: 901. C# Windows Forms - Vista/7 이후의 Progress Bar 업데이트가 느린 문제파일 다운로드1
12181정성태3/11/202023271기타: 76. 재현 가능한 최소한의 예제 프로젝트란? - 두 번째 예제파일 다운로드1
12180정성태3/10/202020146오류 유형: 600. "Docker Desktop for Windows" - EXPOSE 포트가 LISTENING 되지 않는 문제
12179정성태3/10/202030717개발 환경 구성: 481. docker - PostgreSQL 컨테이너 실행
12178정성태3/10/202024522개발 환경 구성: 480. Linux 운영체제의 docker를 위한 tcp 바인딩 추가 [1]
12177정성태3/9/202023587개발 환경 구성: 479. docker - MySQL 컨테이너 실행
12176정성태3/9/202023078개발 환경 구성: 478. 파일의 (sha256 등의) 해시 값(checksum) 확인하는 방법
... 61  62  63  64  65  66  67  68  69  70  71  72  [73]  74  75  ...