Microsoft MVP성태의 닷넷 이야기
글쓴 사람
정성태 (techsharer at outlook.com)
홈페이지
첨부 파일
 
(연관된 글이 1개 있습니다.)
(시리즈 글이 8개 있습니다.)
개발 환경 구성: 533. Wireshark + C#으로 확인하는 TCP 통신의 MSS(Maximum Segment Size) - 리눅스 환경
; https://www.sysnet.pe.kr/2/0/12527

개발 환경 구성: 534. Wireshark + C#으로 확인하는 TCP 통신의 MSS(Maximum Segment Size) - 윈도우 환경
; https://www.sysnet.pe.kr/2/0/12528

개발 환경 구성: 535. Wireshark + C#으로 확인하는 TCP 통신의 MIN RTO
; https://www.sysnet.pe.kr/2/0/12529

개발 환경 구성: 536. Wireshark + C#으로 확인하는 TCP 통신의 Receive Window
; https://www.sysnet.pe.kr/2/0/12530

개발 환경 구성: 538. Wireshark + C#으로 확인하는 ReceiveBufferSize(SO_RCVBUF), SendBufferSize(SO_SNDBUF)
; https://www.sysnet.pe.kr/2/0/12532

개발 환경 구성: 539. Wireshark + C/C++로 확인하는 TCP 연결에서의 shutdown 동작
; https://www.sysnet.pe.kr/2/0/12533

개발 환경 구성: 540. Wireshark + C/C++로 확인하는 TCP 연결에서의 closesocket 동작
; https://www.sysnet.pe.kr/2/0/12534

개발 환경 구성: 541.  Wireshark로 확인하는 LSO(Large Send Offload), RSC(Receive Segment Coalescing) 옵션
; https://www.sysnet.pe.kr/2/0/12535




Wireshark + C#으로 확인하는 TCP 통신의 Receive Window

검색해 보면, 이에 관해 자세한 설명이 나온 글을 쉽게 찾을 수 있습니다.

TCP series #4: TCP receive window and everything you need to know about it
; https://accedian.com/blog/tcp-receive-window-everything-need-know/

그래도, 이쪽 분야가 워낙 빠른지라 무턱대고 저 내용을 그대로 믿을 수는 없으니 정말 그런지 한 번 테스트를 해보겠습니다. ^^

이를 위해 지난 글의 예제 코드를 그대로 재사용해 서버는 Azure VM에 올려 두고, 클라이언트도 제가 가진 물리 서버의 VM에 올려 둔 후, 연결해 패킷 캡처를 해봤습니다.

2016 48.517893 ..client_ip... ..server_ip.. TCP	66 2865 → 15000 [SYN] Seq=0 Win=64240 Len=0 MSS=1460 WS=256 SACK_PERM=1
2018 48.522650 ..server_ip.. ..client_ip... TCP	66 15000 → 2865 [SYN, ACK] Seq=0 Ack=1 Win=8192 Len=0 MSS=1440 WS=256 SACK_PERM=1
2019 48.522716 ..client_ip... ..server_ip.. TCP	54 2865 → 15000 [ACK] Seq=1 Ack=1 Win=263424 Len=0

예전에는 Window 크기를 TCP Header에 2바이트로 보관해 총 65535 바이트까지 지정할 수 있었다고 합니다. 하지만 네트워크의 발전으로 64K는 너무 작아 TCP Header에 추가 Option 필드로 "Window scale" 값을 포함해 이를 곱하는 방식으로 보완했습니다. 현재는 Scale 값으로 1바이트를 점유하는데, 재미있는 것은 이 값이 2의 제곱을 나타내기 때문에 Wireshark에서 출력한 WS=256에 대해 실제 패킷에는 log2(256) = 8이 담겨 있습니다. 그러니까 이론 상 Win = 65535, WS=14(Scale 허용 최댓값 14: 2^14 = 16384)라면 최대 1,073,725,440(1GB)까지 Window Size를 지정할 수 있습니다.

이를 감안해 위의 2016번과, 2018번 패킷에 있는 Window 크기는 다음과 같이 "정상적이지 않게" 계산할 수 있습니다.

[클라이언트 측의 Receive Window 크기]
Win=64240
WS=256, (Scale = 8, 2^8 == 256)
윈도우 크기 = 64240 * 256 = 16,445,440 (약 16MB)

[서버 측의 Receive Window 크기]
Win=8192
WS=256, (Scale = 8, 2^8 == 256)
윈도우 크기 = 8192 * 256 = 2,097,152 (2MB)

왜 저것이 "정상적이지 않은" 크기인지 다음의 문서에서 이를 설명합니다.

Windows scaling
; https://docs.microsoft.com/en-us/troubleshoot/windows-server/networking/description-tcp-features#windows-scaling

It's important to note that the window size used in the actual three-way handshake is NOT the window size that is scaled. This is per RFC 1323 section 2.2, "The Window field in a SYN (for example, a [SYN] or [SYN,ACK]) segment itself is never scaled."
This means that the first data packet sent after the three-way handshake is the actual window size. If there is a scaling factor, the initial window size of 65,535 bytes is always used. The window size is then multiplied by the scaling factor identified in the three-way handshake.


따라서 (3-way handshake의) SYN 패킷에 있는 Window Size는 (양측 모두 scale 값을 설정한 경우) 버리는 값이고, 오직 Scale 값만이 유효해 이후 통신에서 해당 Scale 값을 재사용한다는 점입니다. 예를 들어, 저렇게 연결을 맺은 통신에서 5,000 바이트를 클라이언트에서 서버로 보내면 이런 패킷이 나옵니다.

252 5.875126 ..client_ip... ..server_ip.. TCP 5054 1093 → 15000 [PSH, ACK] Seq=1 Ack=1 Win=263424 Len=5000
253 5.880317 ..server_ip.. ..client_ip... TCP 60  15000 → 1093 [ACK] Seq=1 Ack=4321 Win=262656 Len=0
254 5.880317 ..server_ip.. ..client_ip... TCP 60  15000 → 1093 [ACK] Seq=1 Ack=5001 Win=261888 Len=0

위에서 wireshark가 보여주는 Win 값은 편의상 계산된 값이며 실제로는 해당 패킷에 window size만 각각 다음과 같이 2바이트로 표현해 전송합니다.

252번 패킷 Window size value = 0x0405(1029)
253번 패킷 Window size value = 0x0402(1026)
254번 패킷 Window size value = 0x03ff(1023)

그리고, 각각 연결 시에 알렸던 scale 값을 곱하면 wireshark가 보여준 Win=... 값을 확인할 수 있습니다.

252번 패킷 1029 * 256 = 263424
253번 패킷 1026 * 256 = 262656
254번 패킷 1023 * 256 = 261888

이런 이유로 네트워크 성능 도구들은 처음 연결 단계의 패킷을 잡지 못한 경우 이후 TCP 패킷들의 Window 크기를 정상적으로 보여줄 수 없습니다. 일례로, wireshark도 TCP 연결이 맺어진 다음 패킷 모니터링을 시작하면 다음과 같이 Win 크기를 scale 계산이 안 된 값으로 보여줘 매우 작게 나옵니다.

1 0.000000 ..client_ip... ..server_ip... TCP 1054 34069 → 15000 [PSH, ACK] Seq=1 Ack=1 Win=1026 Len=1000
2 0.059503 ..server_ip... ..client_ip... TCP 60 15000 → 34069 [ACK] Seq=1 Ack=1001 Win=509 Len=0

그리고 이때의 TCP 상세 설명을 보면 "[Window size scaling factor: -1 (unknown)]"라고 출력합니다.

참고로, 어느 한쪽이라도 (3-way handshake 단계에서) scale을 사용한다고 명시하지 않으면 양 측 모두 이 기능을 사용하지 않습니다. (아마도 이를 대비해 Window Size에도 기본적으로는 값을 설정해서 보내는 듯합니다.)




이쯤에서 다시 3-way handshake 패킷을 볼까요?

2016 48.517893 ..client_ip... ..server_ip.. TCP 66 2865 → 15000 [SYN] Seq=0 Win=64240 Len=0 MSS=1460 WS=256 SACK_PERM=1
2018 48.522650 ..server_ip... .client_ip... TCP 66 15000 → 2865 [SYN, ACK] Seq=0 Ack=1 Win=8192 Len=0 MSS=1440 WS=256 SACK_PERM=1
2019 48.522716 ..client_ip... ..server_ip.. TCP 54 2865 → 15000 [ACK] Seq=1 Ack=1 Win=263424 Len=0

마지막에 (기준 scale 값을 제공하는) SYN 패킷이 아닌 ACK 하나가 클라이언트에서 서버로 전달되는 과정이 있는데요, 바로 저기에 포함된 263,424는 정확히 클라이언트의 TCP Receive Window 크기를 의미합니다. 이렇게 따지고 보면, TCP 통신에서 3-way handshake 단계 중 상대방의 TCP Receive Window 크기를 알 수 있는 것은 서버 측만 가능합니다. 달리 말하면, 클라이언트의 경우에는 직접 통신하면서 받게되는 이후의 패킷에서만 서버의 Receive Window 크기를 알 수 있다는 건데요, 실제로 위에서 5,000 바이트를 전송했을 때 서버로부터의 ACK 패킷을 보면,

252 5.875126 ..client_ip... ..server_ip..  TCP 5054    1093 → 15000 [PSH, ACK] Seq=1 Ack=1 Win=263424 Len=5000
253 5.880317 ..server_ip..  ..client_ip... TCP 60  15000 → 1093 [ACK] Seq=1 Ack=4321 Win=262656 Len=0
254 5.880317 ..server_ip..  ..client_ip... TCP 60  15000 → 1093 [ACK] Seq=1 Ack=5001 Win=261888 Len=0

그제서야 클라이언트는 서버의 Receive Window 크기를 인지하게 됩니다. 그렇다면, 클라이언트가 먼저 서버로 데이터를 보내는 경우 어떻게 서버의 TCP Receive Window 크기를 알고 흐름 제어를 할 수 있을까요? 이에 대한 답도 문서에 잘 보면 나옵니다. ^^

This means that the first data packet sent after the three-way handshake is the actual window size. If there is a scaling factor, the initial window size of 65,535 bytes is always used.


즉, TCP 협상에서 scale 사용을 지정했으면 기본 윈도우 크기를 65,535로 사용한다는 것입니다. 따라서 (서버로부터 scale을 사용하겠다고 했으므로) 클라이언트는 서버의 최초 TCP Receive Window의 크기를 65,535로 가정하고 흐름 제어를 하다가 서버로부터 수신한 패킷에 Window Size 필드가 있으면 그것에 scale 값을 곱해 새로운 윈도우 크기로 인식하게 되는 것입니다.




기왕 하는 김에, Receive Window 크기를 소진해 볼까요? 그런데 아쉽게도 아주 매끄러운 재현 방법이 없습니다.

위에서 설명했던 내용대로 서버의 Receive Window 크기는 이후에 (위에서는 262,656으로) 변경이 되겠지만 초기에는 클라이언트에서 65,535로 가정할 것입니다. 그리고 TCP Receive Window란, 상대의 ACK 없이 보낼 수 있는 (이상적인 상황에서의) 최대 데이터 크기이기 때문에 TCP 연결 후 서버 측의 VM을 paused 상태로 만들고 send를 하면 이론상 유사하게 재현이 될 걸로 보입니다. 즉, 클라이언트가 바로 데이터를 전송하는 경우 65,535 바이트까지는 전송이 되어야 하는 것입니다.

하지만, 테스트로 (연결 후 처음) 20,000 바이트를 보냈을 때 분할된 첫 번째 패킷에 대한 ACK 대기로 다음과 같이 Retransmission 절차로 빠집니다.

1 0.000000   ..client_ip... ..server_ip... TCP 66 33692 → 15000 [SYN] Seq=0 Win=64240 Len=0 MSS=1460 WS=256 SACK_PERM=1
2 0.005193   ..server_ip... ..client_ip... TCP 66 15000 → 33692 [SYN, ACK] Seq=0 Ack=1 Win=65535 Len=0 MSS=1460 WS=256 SACK_PERM=1
3 0.005281   ..client_ip... ..server_ip... TCP 54 33692 → 15000 [ACK] Seq=1 Ack=1 Win=262656 Len=0
4 15.104217  ..client_ip... ..server_ip... TCP 14654 33692 → 15000 [ACK] Seq=1 Ack=1 Win=262656 Len=14600
5 15.411778  ..client_ip... ..server_ip... TCP 1514 [TCP Retransmission] 33692 → 15000 [ACK] Seq=1 Ack=1 Win=262656 Len=1460
6 16.022453  ..client_ip... ..server_ip... TCP 1514 [TCP Retransmission] 33692 → 15000 [ACK] Seq=1 Ack=1 Win=262656 Len=1460
7 17.223054  ..client_ip... ..server_ip... TCP 590 [TCP Retransmission] 33692 → 15000 [ACK] Seq=1 Ack=1 Win=262656 Len=536
8 18.427186  ..client_ip... ..server_ip... TCP 590 [TCP Retransmission] 33692 → 15000 [ACK] Seq=1 Ack=1 Win=262656 Len=536
9 19.636720  ..client_ip... ..server_ip... TCP 1514 [TCP Retransmission] 33692 → 15000 [ACK] Seq=1 Ack=1 Win=262656 Len=1460
10 22.047373 ..client_ip... ..server_ip... TCP 1514 [TCP Retransmission] 33692 → 15000 [ACK] Seq=1 Ack=1 Win=262656 Len=1460
11 26.853856 ..client_ip... ..server_ip... TCP 1514 [TCP Retransmission] 33692 → 15000 [ACK] Seq=1 Ack=1 Win=262656 Len=1460
12 36.466235 ..client_ip... ..server_ip... TCP 54 33692 → 15000 [RST, ACK] Seq=1461 Ack=1 Win=0 Len=0

오호~~~ 당황스럽군요. ^^ 동일 환경의 연결 상태에서 20,000 바이트를 보내면 첫 번째 패킷 보낸 후 마지막 패킷의 ACK를 받는 시간까지 다 합쳐도 10ms가 안 걸렸습니다. 즉, 적어도 Receive Window라고 65,535 크기를 가정했다면 20,000 바이트 정도는 모두 보냈어야 하고 ACK를 받지 못한 300ms가 지난 시점부터 Retransmission 절차가 나와야 하는데 그렇지 않은 것입니다.

혹시, Send Window의 초기 크기가 14600 정도로 잡혀 있던 걸까요? ^^ 이를 확인하기 위해서는 InitialCongestionWindow의 값을 봐야 합니다.

C:\Windows\System32> netsh interface tcp show supplemental

The TCP global default template is internet

TCP Supplemental Parameters
----------------------------------------------
Minimum RTO (msec)                  : 300
Initial Congestion Window (MSS)     : 10
Congestion Control Provider         : cubic
Enable Congestion Window Restart    : disabled
Delayed ACK timeout (msec)          : 40
Delayed ACK frequency               : 2
Enable RACK                         : enabled
Enable Tail Loss Probe              : enabled

Please use the 'netsh int tcp show supplementalports' and
'netsh int tcp show supplementalsubnets' commands to view active filters.

그러고 보니 3-way handshake 단계에서 협의된 MSS 크기는 1460입니다. 거기다 InitialCongestionWindow의 값이 10이니까, 14600만큼 전송하고 ACK를 대기하게 된 것입니다. 정확히 설명이 되었군요. ^^

참고로 예전에는 InitialCongestionWindow 값이 보통 1이었다고 합니다. 그래서 간혹 블로그 글들을 보면 첫 번째 패킷은 무조건 송신 후 ACK를 기다린다는 식의 설명이 나오는데 바로 icwnd 값이 1로 설정된 당시의 환경에서 기인한 것입니다. 이후 2로 증가한 후 근래에는 10(RFC6928)을 기본으로 쓰는 운영체제들이 늘었다고.




다소 과격하지만 ^^ 넉넉잡아 500,000 바이트 정도 send 하고 수신 측은 recv만 호출하지 않는다면 Receive Window를 소진하는 것을 쉽게 재현할 수 있습니다.

// 연결 단계의 패킷 3개
153 2.888173 ..client_ip... ..server_ip... TCP 66 11337 → 15000 [SYN] Seq=0 Win=64240 Len=0 MSS=1460 WS=256 SACK_PERM=1
154 2.893354 ..server_ip... ..client_ip... TCP 66 15000 → 11337 [SYN, ACK] Seq=0 Ack=1 Win=65535 Len=0 MSS=1460 WS=256 SACK_PERM=1
155 2.893446 ..client_ip... ..server_ip... TCP 54 11337 → 15000 [ACK] Seq=1 Ack=1 Win=262656 Len=0

// 송신이 진행되면서 수신 측은 내부 버퍼인 SO_RCVBUF의 크기 정도는 처리되면서 Receive Window의 크기가 그다지 변화가 없다가,
290 7.682747 ..client_ip... ..server_ip... TCP 14654 11337 → 15000 [ACK] Seq=1 Ack=1 Win=262656 Len=14600
291 7.689219 ..server_ip... ..client_ip... TCP 60 15000 → 11337 [ACK] Seq=1 Ack=2921 Win=131328 Len=0
...[생략]...                                   
316 7.702878 ..server_ip... ..client_ip... TCP 60 15000 → 11337 [ACK] Seq=1 Ack=55481 Win=131328 Len=0
317 7.702897 ..client_ip... ..server_ip... TCP 11734 11337 → 15000 [ACK] Seq=113881 Ack=1 Win=262656 Len=11680
318 7.702932 ..server_ip... ..client_ip... TCP 60 15000 → 11337 [ACK] Seq=1 Ack=58401 Win=131328 Len=0
319 7.702932 ..server_ip... ..client_ip... TCP 60 15000 → 11337 [ACK] Seq=1 Ack=61321 Win=131328 Len=0
320 7.702932 ..server_ip... ..client_ip... TCP 60 15000 → 11337 [ACK] Seq=1 Ack=64241 Win=131328 Len=0

// SO_RCVBUF가 마침내 꽉 차게 되면, 이후 Receive Window가 점차로 줄어들기 시작
...[생략]...
362 7.714849 ..server_ip... ..client_ip... TCP 60 15000 → 11337 [ACK] Seq=1 Ack=175201 Win=21760 Len=0
363 7.714849 ..server_ip... ..client_ip... TCP 60 15000 → 11337 [ACK] Seq=1 Ack=178121 Win=18944 Len=0
364 7.714849 ..server_ip... ..client_ip... TCP 60 15000 → 11337 [ACK] Seq=1 Ack=181041 Win=15872 Len=0
365 7.714849 ..server_ip... ..client_ip... TCP 60 15000 → 11337 [ACK] Seq=1 Ack=183961 Win=13056 Len=0
366 7.714910 ..server_ip... ..client_ip... TCP 60 15000 → 11337 [ACK] Seq=1 Ack=186881 Win=9984 Len=0
367 7.714910 ..server_ip... ..client_ip... TCP 60 15000 → 11337 [ACK] Seq=1 Ack=189801 Win=7168 Len=0
368 7.716539 ..server_ip... ..client_ip... TCP 60 15000 → 11337 [ACK] Seq=1 Ack=192721 Win=4352 Len=0
369 7.716539 ..server_ip... ..client_ip... TCP 60 15000 → 11337 [ACK] Seq=1 Ack=195641 Win=1280 Len=0

// 송신 측이 수신자의 Receive Window의 크기를 마지막으로 1280 바이트를 채워 보내는 것으로,
500 12.718148 ..client_ip... ..server_ip... TCP 1334 [TCP Window Full] 11337 → 15000 [ACK] Seq=195641 Ack=1 Win=262656 Len=1280

// Receive Window 소진 
501 12.778449 ..server_ip... ..client_ip... TCP 60 [TCP ZeroWindow] 15000 → 11337 [ACK] Seq=1 Ack=196921 Win=0 Len=0

// 이후, 수신 측의 Receive Window 크기 조회를 일정 시간 동안 반복
507 13.079354 ..client_ip... ..server_ip... TCP 55 [TCP ZeroWindowProbe] 11337 → 15000 [ACK] Seq=196921 Ack=1 Win=262656 Len=1
510 13.137834 ..server_ip... ..client_ip... TCP 60 [TCP ZeroWindow] [TCP ACKed unseen segment] 15000 → 11337 [ACK] Seq=1 Ack=196922 Win=0 Len=0
522 13.749622 ..client_ip... ..server_ip... TCP 55 [TCP Previous segment not captured] 11337 → 15000 [ACK] Seq=196922 Ack=1 Win=262656 Len=1
526 13.809922 ..server_ip... ..client_ip... TCP 60 [TCP ZeroWindow] [TCP ACKed unseen segment] 15000 → 11337 [ACK] Seq=1 Ack=196923 Win=0 Len=0
553 15.015558 ..client_ip... ..server_ip... TCP 55 [TCP ZeroWindowProbe] 11337 → 15000 [ACK] Seq=196923 Ack=1 Win=262656 Len=1
554 15.076075 ..server_ip... ..client_ip... TCP 60 [TCP ZeroWindow] [TCP ACKed unseen segment] 15000 → 11337 [ACK] Seq=1 Ack=196924 Win=0 Len=0
606 17.481915 ..client_ip... ..server_ip... TCP 55 [TCP Previous segment not captured] 11337 → 15000 [ACK] Seq=196924 Ack=1 Win=262656 Len=1
609 17.529619 ..server_ip... ..client_ip... TCP 60 [TCP ZeroWindow] [TCP ACKed unseen segment] 15000 → 11337 [ACK] Seq=1 Ack=196925 Win=0 Len=0

보다시피 수신 측은 "Ack=196921 Win=0"을 마지막 ACK로 응답하면서 Receive Window가 모두 소진되었습니다. 이후, 송신 측은 주기적으로 (wireshark에서 "TCP ZeroWindowProbe"라고 표시된) ACK 패킷을 수신 측으로 날리며 receive window의 변화가 있는지 묻습니다. 그리고 이에 대해 수신 측은 (wireshark에서 "ZeroWindow"라고 표시된) ACK 패킷으로 여전히 공간이 없음을 확인해 줍니다. ("ZeroWindow", "ZeroWindowProbe"라는 것은 wirehshark가 서비스로 붙여주는 문구이고 실제 이런 이름의 필드는 없습니다.)

물론, 저 상태에서 수신 측에 recv 호출을 해주면 다시 Receive Window 공간이 생겼음을 송신 측으로 보내고 이후 데이터 전송이 재개됩니다.




참고로, Vista/Windows Server 2008부터는 자동으로 Receive Window에 대한 크기를 조절한다고 합니다.

The Cable Guy TCP Receive Window Auto-Tuning
; https://docs.microsoft.com/en-us/previous-versions/technet-magazine/cc162519(v=msdn.10)

암튼... 너무나 복잡해진 TCP 스택입니다. ^^;




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

[연관 글]






[최초 등록일: ]
[최종 수정일: 2/5/2021]

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

비밀번호

댓글 작성자
 




1  2  [3]  4  5  6  7  8  9  10  11  12  13  14  15  ...
NoWriterDateCnt.TitleFile(s)
13549정성태2/3/20242473개발 환경 구성: 706. C# - 컨테이너에서 실행하기 위한 (소켓) 콘솔 프로젝트 구성
13548정성태2/1/20242306개발 환경 구성: 705. "Docker Desktop for Windows" - ASP.NET Core 응용 프로그램의 소켓 주소 바인딩(IPv4/IPv6 loopback, Any)
13547정성태1/31/20242054개발 환경 구성: 704. Visual Studio - .NET 8 프로젝트부터 dockerfile에 추가된 "USER app" 설정
13546정성태1/30/20241894Windows: 255. (디버거의 영향 등으로) 대상 프로세스가 멈추면 Socket KeepAlive로 연결이 끊길까요?
13545정성태1/30/20241825닷넷: 2212. ASP.NET Core - 우선순위에 따른 HTTP/HTTPS 호스트:포트 바인딩 방법
13544정성태1/30/20241846오류 유형: 894. Microsoft.Data.SqlClient - Could not load file or assembly 'System.Security.Permissions, ...'
13543정성태1/30/20241822Windows: 254. Windows - 기본 사용 중인 5357 포트 비활성화는 방법
13542정성태1/30/20241875오류 유형: 893. Visual Studio - Web Application을 실행하지 못하는 IISExpress - 두 번째 이야기
13541정성태1/29/20241919VS.NET IDE: 188. launchSettings.json의 useSSL 옵션
13540정성태1/29/20242049Linux: 69. 리눅스 - "Docker Desktop for Windows" Container 환경에서 IPv6 Loopback Address 바인딩 오류
13539정성태1/26/20242143개발 환경 구성: 703. Visual Studio - launchSettings.json을 이용한 HTTP/HTTPS 포트 바인딩
13538정성태1/25/20242212닷넷: 2211. C# - NonGC(FOH) 영역에 .NET 개체를 생성파일 다운로드1
13537정성태1/24/20242258닷넷: 2210. C# - Native 메모리에 .NET 개체를 생성파일 다운로드1
13536정성태1/23/20242370닷넷: 2209. .NET 8 - NonGC Heap / FOH (Frozen Object Heap) [1]
13535정성태1/22/20242200닷넷: 2208. C# - GCHandle 구조체의 메모리 분석
13534정성태1/21/20242030닷넷: 2207. C# - SQL Server DB를 bacpac으로 Export/Import파일 다운로드1
13533정성태1/18/20242223닷넷: 2206. C# - TCP KeepAlive의 서버 측 구현파일 다운로드1
13532정성태1/17/20242134닷넷: 2205. C# - SuperSimpleTcp 사용 시 주의할 점파일 다운로드1
13531정성태1/16/20242019닷넷: 2204. C# - TCP KeepAlive에 새로 추가된 Retry 옵션파일 다운로드1
13530정성태1/15/20242009닷넷: 2203. C# - Python과의 AES 암호화 연동파일 다운로드1
13529정성태1/15/20241893닷넷: 2202. C# - PublishAot의 glibc에 대한 정적 링킹하는 방법
13528정성태1/14/20242030Linux: 68. busybox 컨테이너에서 실행 가능한 C++, Go 프로그램 빌드
13527정성태1/14/20241957오류 유형: 892. Visual Studio - Failed to launch debug adapter. Additional information may be available in the output window.
13526정성태1/14/20242045닷넷: 2201. C# - Facebook 연동 / 사용자 탈퇴 처리 방법
13525정성태1/13/20242011오류 유형: 891. Visual Studio - Web Application을 실행하지 못하는 IISExpress
13524정성태1/12/20242063오류 유형: 890. 한국투자증권 KIS Developers OpenAPI - GW라우팅 중 오류가 발생했습니다.
1  2  [3]  4  5  6  7  8  9  10  11  12  13  14  15  ...