성태의 닷넷 이야기
홈 주인
모아 놓은 자료
프로그래밍
질문/답변
사용자 관리
사용자
메뉴
아티클
외부 아티클
유용한 코드
온라인 기능
MathJax 입력기
최근 덧글
[정성태] Roll A Lisp In C - Reading ; https...
[정성태] Java - How to use the Foreign Funct...
[정성태] 제가 큰 실수를 했군요. ^^; Delegate를 통한 Bein...
[정성태] Working with Rust Libraries from C#...
[정성태] Detecting blocking calls using asyn...
[정성태] 아쉽게도, 커뮤니티는 아니고 개인 블로그입니다. ^^
[정성태] 질문이 잘 이해가 안 됩니다. 우선, 해당 소스코드에서 ILis...
[양승조
] var대신 dinamic으로 선언해서 해결은 했습니다. 맞는 해...
[양승조
] 또 막혔습니다. ㅠㅠ var list = props[i].Ge...
[양승조
] 아. 감사합니다. 어제는 안됐던것 같은데....정신을 차려야겠네...
글쓰기
제목
이름
암호
전자우편
HTML
홈페이지
유형
제니퍼 .NET
닷넷
COM 개체 관련
스크립트
VC++
VS.NET IDE
Windows
Team Foundation Server
디버깅 기술
오류 유형
개발 환경 구성
웹
기타
Linux
Java
DDK
Math
Phone
Graphics
사물인터넷
부모글 보이기/감추기
내용
<div style='display: inline'> <h1 style='font-family: Malgun Gothic, Consolas; font-size: 20pt; color: #006699; text-align: center; font-weight: bold'>C# - TCP KeepAlive에 새로 추가된 Retry 옵션</h1> <p> 예전 글에서,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > TCP 소켓 연결의 해제를 알 수 있는 방법 ; <a target='tab' href='https://www.sysnet.pe.kr/2/0/1825'>https://www.sysnet.pe.kr/2/0/1825</a> </pre> <br /> Socket.IOControl 메서드를 이용해 KeepAlive를 제어하는 방법을 소개했습니다. 그때 소개한 제어 방법은,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > int size = sizeof(UInt32); byte[] inArray = new byte[size * 3]; // 12바이트 할당 // ...[생략]... _socket.IOControl(IOControlCode.KeepAliveValues, inArray, null); </pre> <br /> IOControlCode.KeepAliveValues 옵션과 함께 해당 설정을 담은 버퍼값을 이용하고 있습니다. 버퍼값의 구조는, Windows 운영체제에서 구현한 Socket의 의존성이 발생하는데요, Windows의 경우 구조체는 다음과 같습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > // SIO_KEEPALIVE_VALS Control Code // <a target='tab' href='https://learn.microsoft.com/en-us/windows/win32/winsock/sio-keepalive-vals'>https://learn.microsoft.com/en-us/windows/win32/winsock/sio-keepalive-vals</a> /* Argument structure for SIO_KEEPALIVE_VALS */ struct tcp_keepalive { <span style='color: blue; font-weight: bold'>u_long onoff; u_long keepalivetime; u_long keepaliveinterval;</span> }; </pre> <br /> 닷넷도 저 형식에 따라 값을 설정해야만 KeepAlive 설정이 동작하게 됩니다.<br /> <br /> <hr style='width: 50%' /><br /> <br /> 그런데, 최근에 아래의 소스 코드를 보면서,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > SuperSimpleTcp/src/SuperSimpleTcp/SimpleTcpClient.cs ; <a target='tab' href='https://github.com/jchristn/SuperSimpleTcp/blob/master/src/SuperSimpleTcp/SimpleTcpClient.cs#L1125'>https://github.com/jchristn/SuperSimpleTcp/blob/master/src/SuperSimpleTcp/SimpleTcpClient.cs#L1125</a> </pre> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > #if NETCOREAPP3_1_OR_GREATER || NET6_0_OR_GREATER // NETCOREAPP3_1_OR_GREATER catches .NET 5.0 _client.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive, true); _client.Client.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.TcpKeepAliveTime, _keepalive.TcpKeepAliveTime); _client.Client.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.TcpKeepAliveInterval, _keepalive.TcpKeepAliveInterval); // Windows 10 version 1703 or later if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows) && Environment.OSVersion.Version >= new Version(10, 0, 15063)) { _client.Client.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.TcpKeepAliveRetryCount, _keepalive.TcpKeepAliveRetryCount); } #elif NETFRAMEWORK byte[] keepAlive = new byte[12]; // Turn keepalive on Buffer.BlockCopy(BitConverter.GetBytes((uint)1), 0, keepAlive, 0, 4); // Set TCP keepalive time Buffer.BlockCopy(BitConverter.GetBytes((uint)_keepalive.TcpKeepAliveTimeMilliseconds), 0, keepAlive, 4, 4); // Set TCP keepalive interval Buffer.BlockCopy(BitConverter.GetBytes((uint)_keepalive.TcpKeepAliveIntervalMilliseconds), 0, keepAlive, 8, 4); // Set keepalive settings on the underlying Socket _client.Client.IOControl(IOControlCode.KeepAliveValues, keepAlive, null); #elif NETSTANDARD #endif </pre> <br /> SocketOptionName에 추가된 SocketOptionName.TcpKeepAliveTime, SocketOptionName.TcpKeepAliveInterval, SocketOptionName.TcpKeepAliveRetryCount 3가지 옵션을 알게 되었습니다.<br /> <br /> 아마도 닷넷에 이렇게 옵션이 있다는 것은 Windows SDK의 C/C++ Header 파일에도 추가되었음을 의미하는데요, 우선 이미 기존에도 있었던 SocketOptionLevel의 Socket/TCP에 해당하는 값들은 C/C++에서 이렇게 대응하고,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > [SocketOptionLevel.Socket] #define SOL_SOCKET 0xffff /* options for socket level */ [SocketOptionLevel.Tcp] #define IPPROTO_TCP 6 /* tcp */ </pre> <br /> 그다음 새롭게 추가된 SocketOptionName 3개의 옵션은 다음과 같이 매핑됩니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > [기존 SocketOptionLevel.Socket 레벨의 KeepAlive - winsock.h] #define SO_KEEPALIVE 0x0008 /* keep connections alive */ [신규 SocketOptionLevel.Tcp 레벨의 TcpKeepAliveTime] #define TCP_KEEPALIVE 3 #define TCP_KEEPIDLE TCP_KEEPALIVE [신규 SocketOptionLevel.Tcp 레벨의 TcpKeepAliveRetryCount] #define TCP_KEEPCNT 16 [신규 SocketOptionLevel.Tcp 레벨의 TcpKeepAliveInterval] #define TCP_KEEPINTVL 17 </pre> <br /> 정리하면, 기존에는 Socket Level에서만 SO_KEEPALIVE 옵션으로 KeepAlive 관련 설정을 할 수 있었지만, 이제는 새롭게 TCP Level에서 TCP_KEEPALIVE(TCP_KEEPIDLE), TCP_KEEPINTVL과 함께 완전히 새로운 기능인 TCP_KEEPCNT를 추가 설정할 수 있도록 확장한 것입니다.<br /> <br /> 이 중에서 TcpKeepAliveRetryCount(TCP_KEEPCNT)가 특히 더 흥미로운데요, 왜냐하면 과거에는 <a target='tab' href='https://support.microsoft.com/en-us/topic/how-to-modify-the-tcp-ip-maximum-retransmission-time-out-7ae0982a-4963-fa7e-ee79-ff6d0da73db8'>TcpMaxDataRetransmissions</a>라는 값으로 전역 레지스트리 설정(HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\Tcpip\Parameters)을 건드려야만 조정이 가능했기 때문입니다.<br /> <br /> <hr style='width: 50%' /><br /> <br /> 그렇다면 당연히 이 옵션들은 특정 운영체제부터 추가되었을 것이라고 짐작할 수 있습니다. 실제로 문서를 보면,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > IPPROTO_TCP socket options ; <a target='tab' href='https://learn.microsoft.com/en-us/windows/win32/winsock/ipproto-tcp-socket-options'>https://learn.microsoft.com/en-us/windows/win32/winsock/ipproto-tcp-socket-options</a> </pre> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > TCP_KEEPIDLE(TCP_KEEPALIVE) Gets or sets the number of seconds a TCP connection will remain idle before keepalive probes are sent to the remote. Note: <span style='color: blue; font-weight: bold'>This option is available starting with Windows 10, version 1709.</span> </pre> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > TCP_KEEPINTVL Gets or sets the number of seconds a TCP connection will wait for a keepalive response before sending another keepalive probe. Note: <span style='color: blue; font-weight: bold'>This option is available starting with Windows 10, version 1709.</span> </pre> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > TCP_KEEPCNT Gets or sets the number of TCP keep alive probes that will be sent before the connection is terminated. It is illegal to set TCP_KEEPCNT to a value greater than 255. </pre> <br /> 오히려 기존에 SO_KEEPALIVE로 설정 가능했던 TCP_KEEPALIVE, TCP_KEEPINTVL은 "Windows 10, version 1709"로 나중에 지원을 추가했고, 신규 기능인 TCP_KEEPCNT는 동일 문서의 별도로 정리돼 있는 표에,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > Windows support for IPPROTO_TCP options ; <a target='tab' href='https://learn.microsoft.com/en-us/windows/win32/winsock/ipproto-tcp-socket-options#windows-support-for-ipproto_tcp-options'>https://learn.microsoft.com/en-us/windows/win32/winsock/ipproto-tcp-socket-options#windows-support-for-ipproto_tcp-options</a> </pre> <br /> "Starting with Windows 10, version 1703"으로 나옵니다. 약간의 차이는 있지만, 간단하게 Windows 10/Windows Server 2019 이상부터 쓸 수 있는 기능이라고 보면 됩니다. <br /> <br /> 이에 대해 SuperSimpleTcp/src/SuperSimpleTcp/SimpleTcpClient.cs 코드에는 다음과 같이 버전 제약을 두고 있는데요,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > // Windows 10 version 1703 or later if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows) && Environment.OSVersion.Version >= <span style='color: blue; font-weight: bold'>new Version(10, 0, 15063)</span>) { _client.Client.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.TcpKeepAliveRetryCount, _keepalive.TcpKeepAliveRetryCount); } </pre> <br /> Windows Server 2016의 경우 10.0.14393 버전이기 때문에 new Version(10, 0, 15063)에서 걸러지게 됩니다. 만약 이 기능을 Windows Server 2016에서 사용한다면 C#의 경우 SocketException 예외가 발생합니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > // Windows Server 2016에서 아래의 코드를 실행하면, socket.GetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.TcpKeepAliveRetryCount); // 예외 발생: // System.Net.Sockets.SocketException: An unknown, invalid, or unsupported option or level was specified in a getsockopt or setsockopt call at System.Net.Sockets.Socket.GetSocketOption(SocketOptionLevel optionLevel, SocketOptionName optionName) </pre> <br /> 반면, Windows 10 또는 Windows Server 2019에서 실행해 보면 다음과 같은 기본값을 확인할 수 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); Console.WriteLine($"KeepAlive: {socket.GetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive)}"); Console.WriteLine($"TcpKeepAliveTime: {socket.GetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.TcpKeepAliveTime)}"); Console.WriteLine($"TcpKeepAliveInterval: {socket.GetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.TcpKeepAliveInterval)}"); Console.WriteLine($"TcpKeepAliveRetryCount: {socket.GetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.TcpKeepAliveRetryCount)}"); /* 출력 결과: KeepAlive: 0 TcpKeepAliveTime: 7200 TcpKeepAliveInterval: 1 TcpKeepAliveRetryCount: 10 */ </pre> <br /> 따라서 만약 단순히 SocketOptionName.KeepAlive만 활성화시켰다면, 2시간에 한 번씩 KeepAlive 확인을 하다가 그것에 실패하면 이후 1초마다 10번의 확인 신호를 전송하는 식으로 동작합니다. 운이 없다면 2시간 10초 만에 연결 끊김을 감지할 수도 있고, 운이 억수로 좋다면 연결이 끊긴 그 순간이 2시간의 타이밍에 맞춰 발생한다면 약 10초 만에 연결 끊김을 감지할 수도 있습니다.<br /> <br /> <hr style='width: 50%' /><br /> <br /> SuperSimpleTcp/src/SuperSimpleTcp/SimpleTcpClient.cs에서 약간 이해할 수 없는 점이 있다면,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > #if NETCOREAPP3_1_OR_GREATER || NET6_0_OR_GREATER // NETCOREAPP3_1_OR_GREATER catches .NET 5.0 _client.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive, true); // ...[생략]... #elif NETFRAMEWORK byte[] keepAlive = new byte[12]; // ...[생략]... _client.Client.IOControl(IOControlCode.KeepAliveValues, keepAlive, null); #elif NETSTANDARD #endif </pre> <br /> 닷넷 코어 3.1 이상인 경우에만 신규 SocketOptionName.KeepAlive, SocketOptionName.TcpKeepAliveInterval, SocketOptionName.TcpKeepAliveRetryCount를 사용하도록 빌드한다는 점입니다. 물론, 해당 enum 값들이 .NET Core 3.1 이상의 BCL에서 정의돼 그런 것일 수도 있지만, 위에서도 설명했듯이 그것들은 단순한 상수 이외의 의미가 없으므로 .NET Framework에서도 동일하게 사용할 수 있습니다. 따라서, 그냥 다음과 같이 합쳐도 무방합니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > // .NET Framework / .NET Core 공동으로 사용 가능 if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) // <a target='tab' href='https://www.sysnet.pe.kr/2/0/13226'>윈도우에서만 가능</a> { if (Environment.OSVersion.Version >= new Version(10, 0, 15063)) // TcpKeepAliveRetryCount까지 설정 가능 { socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive, true); socket.SetSocketOption(SocketOptionLevel.Tcp, (SocketOptionName)3, 1); socket.SetSocketOption(SocketOptionLevel.Tcp, (SocketOptionName)17, 3); socket.SetSocketOption(SocketOptionLevel.Tcp, (SocketOptionName)16, 10); } else // Windows 8 또는 Windows Server 2016 이하에서는 KeepAlive, TcpKeepAliveInterval만 설정 가능 { byte[] keepAlive = new byte[12]; // Turn keepalive on Buffer.BlockCopy(BitConverter.GetBytes((uint)1), 0, keepAlive, 0, 4); // Set TCP keepalive time Buffer.BlockCopy(BitConverter.GetBytes((uint)_keepalive.TcpKeepAliveTimeMilliseconds), 0, keepAlive, 4, 4); // Set TCP keepalive interval Buffer.BlockCopy(BitConverter.GetBytes((uint)_keepalive.TcpKeepAliveIntervalMilliseconds), 0, keepAlive, 8, 4); // Set keepalive settings on the underlying Socket _client.Client.IOControl(IOControlCode.KeepAliveValues, keepAlive, null); } } </pre> <br /> 그나저나, 실제로 retry 옵션이 잘 동작하는지 테스트를 해볼까요? ^^ 이를 위해 우선 (<a target='tab' href='https://www.sysnet.pe.kr/2/0/1334#vm_test'>이런 상황에서 테스트하기 쉬운</a>) VM에 실행해 둘 간단한 서버를 다음과 같이 만들어 두고,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > using System.Net.Sockets; using System.Net; namespace ConsoleApp1; internal class Program { static void Main(string[] args) { int port = 18500; Socket listenSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); IPEndPoint ep = new IPEndPoint(IPAddress.Any, port); listenSocket.Bind(ep); listenSocket.Listen(5); while (true) { Socket clientSocket = listenSocket.Accept(); Task.Run(() => { clientSocket.Send(new byte[4] { 1, 2, 3, 4 }); try { clientSocket.Receive(new byte[4]); } catch (Exception e) { } }); } } } </pre> <br /> 클라이언트는 서버에 연결한 다음, 서버 측의 VM을 Pause 시켰을 때 연결이 끊겼음을 감지하기 위한 Ping 시간을 함께 체크하도록 다음과 같이 만들 수 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > using System.Net; using System.Net.NetworkInformation; using System.Net.Sockets; using System.Text; namespace ConsoleApp2; internal class Program { static void Main(string[] args) { string host = "192.168.100.50"; // VM 서버 IP int port = 18500; Task.Run(() => { bool connected = true; <span style='color: blue; font-weight: bold'>while (true) {</span> <span style='color: blue; font-weight: bold'>Ping ping = new Ping();</span> PingOptions options = new PingOptions(); options.DontFragment = true; string data = "test"; byte[] buffer = ASCIIEncoding.ASCII.GetBytes(data); int timeout = 300; PingReply reply = <span style='color: blue; font-weight: bold'>ping.Send</span>(IPAddress.Parse(host), timeout, buffer, options); bool replied = reply.Status == IPStatus.Success; if (connected != replied) { connected = replied; Log($"Status changed to {reply.Status}"); <span style='color: blue; font-weight: bold'>// ping이 안 되기 시작한 시간을 남기고,</span> } Thread.Sleep(32); <span style='color: blue; font-weight: bold'>}</span> }); Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive, true); socket.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.TcpKeepAliveTime, 1); socket.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.TcpKeepAliveInterval, 3); socket.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.TcpKeepAliveRetryCount, 10); socket.Connect(host, port); Log("Connected"); int received = socket.Receive(new byte[4]); Log("Received"); try { <span style='color: blue; font-weight: bold'>socket.Receive(new byte[4]);</span> Log("Received"); } catch (Exception ex) { <span style='color: blue; font-weight: bold'>Log($"Exception thrown: {ex.Message}"); // KeepAlive로 인해 연결이 끊기는 시간을 확인</span> } socket.Close(); } private static void Log(string text) { Console.WriteLine($"[{DateTime.Now:mm ss fff}] {text}"); } } </pre> <br /> 자, 이제 서버와 클라이언트를 실행해 두고 VM을 "일시 중지"시키면 다음과 같은 결과를 얻을 수 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > C:\test> ConsoleApp2.exe [57 37 176] Connected [57 37 229] Received [58 <span style='color: blue; font-weight: bold'>27</span> 205] Status changed to TimedOut [58 <span style='color: blue; font-weight: bold'>57</span> 810] Exception thrown: A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond. </pre> <br /> 27초부터 Ping이 안 되고, 57에 KeepAlive로 인해 Receive에서 예외를 반환하고 있으니 대략 30초 만에 연결이 끊겼습니다. 코드에서 설정한 값으로 시간 계산해 본 것과,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > TcpKeepAliveTime == 1 TcpKeepAliveInterval == 3 TcpKeepAliveRetryCount == 10 1초마다 KeepAlive를 보내다가 ACK가 안 오면 이후 3초마다 10번에 걸쳐서 Retry 후 그래도 ACK가 없으면 연결을 끊음. 따라서, 1 + (3 * 10) = 31초 내에 연결이 끊김 (30 ~ 31) </pre> <br /> 크게 다르지 않은 차이입니다. (1초 정도는, KeepAliveTime의 주기와 위에서 제가 만든 Ping 호출의 주기를 감안하면 발생할 수 있는 차이입니다.) 혹시 모르니, 다른 값으로 한 번 더 테스트를 해볼까요? ^^<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > TcpKeepAliveTime == 20 TcpKeepAliveInterval == 50 TcpKeepAliveRetryCount == 1 20초마다 KeepAlive를 보내다가 ACK가 안 오면 이후 50초마다 1번에 걸쳐서 Retry 후 그래도 ACK가 없으면 연결을 끊음. 따라서, 20 + (50 * 1) = 70초 내에 연결이 끊김 (50 ~ 70) </pre> <br /> 이번엔 결과가 다음과 같이 나왔습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > C:\test> ConsoleApp2.exe [02 19 402] Connected [02 19 459] Received [<span style='color: blue; font-weight: bold'>02 29</span> 199] Status changed to TimedOut [<span style='color: blue; font-weight: bold'>03 29</span> 475] Exception thrown: A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond. </pre> <br /> 약 60초에 연결이 끊겼는데요, 역시나 첫 20초의 주기에 어떤 순간이 걸릴지 모르니 대략 비슷하다고 봐야 합니다. 이론상 위의 경우에는 20초의 주기로 인해 운에 따라 50초 ~ 70초 내에 연결이 끊긴다고 봐야 합니다.<br /> <br /> 참고로, 위의 결과는 .NET Core로 만들든, .NET Framework로 만들든 동일하게 발생합니다. 즉, 닷넷 버전이 중요한 것이 아니고, 해당 프로그램이 실행되는 운영체제의 버전이 중요합니다.<br /> <br /> 뭐, 대충 이 정도면 대략 감이 오시겠죠. ^^<br /> <br /> (<a target='tab' href='https://www.sysnet.pe.kr/bbs/DownloadAttachment.aspx?fid=2132&boardid=331301885'>첨부 파일은 이 글의 코드를 포함</a>합니다.)<br /> </p><br /> <br /> <br /><hr /><span style='color: Maroon'>[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]</span> </div>
첨부파일
스팸 방지용 인증 번호
9247
(왼쪽의 숫자를 입력해야 합니다.)