Microsoft MVP성태의 닷넷 이야기
TCP 구현시 Socket 에서 Send 함수의 부분 전송 처리에 관한 질문 [링크 복사], [링크+제목 복사],
조회: 7606
글쓴 사람
정해성 (jghaesg at gmail.com)
홈페이지
첨부 파일
[WpfApp2.zip]    

TCP 구현시 Socket 에서 Send 함수 사용시에 부분 전송 처리를 해야한다는 글을 보았습니다.

1. 먼저 winsock에서 Send함수의 반환 값에 설명을 보면 "오류가 발생하지 않으면 send는 전송 된 총 바이트 수를 반환하며, 이는 len 매개 변수에서 전송하도록 요청된 수보다 작을 수 있습니다." 되어 있습니다.
--(https://learn.microsoft.com/ko-kr/windows/win32/api/winsock2/nf-winsock2-send)

2. 다음은 닷넷Framework 4.8.1에서 Socket에 대한 예제가 아래와 같이 작성이 되어있습니다
// Send the request.
// For the tiny amount of data in this example, the first call to Send() will likely deliver the buffer completely,
// however this is not guaranteed to happen for larger real-life buffers.
// The best practice is to iterate until all the data is sent.
int bytesSent = 0;
while (bytesSent < requestBytes.Length)
{
    bytesSent += socket.Send(requestBytes, bytesSent, requestBytes.Length - bytesSent, SocketFlags.None);
}
--(https://learn.microsoft.com/ko-kr/dotnet/api/system.net.sockets.socket?view=netframework-4.8.1)

3. 마지막으로 닷넷Framework 4.8.1에서 Socket의 Send 함수의 설명에는 아래와 같이 작성이 되어있습니다.
연결 지향 프로토콜 Send 을 사용하는 경우 를 사용하여 Socket.SendTimeout시간 초과를 설정하지 않는 한 버퍼의 모든 바이트가 전송될 때까지 가 차단됩니다. 제한 시간 값을 초과하면 호출에서 Send 을 throw합니다 SocketException. 비 차단 모드 Send 에서 는 버퍼의 바이트 수보다 적게 보내는 경우에도 성공적으로 완료될 수 있습니다.
--(https://learn.microsoft.com/ko-kr/dotnet/api/system.net.sockets.socket.send?view=netframework-4.8.1)

각각의 글을 정리하면
1. winsock에서 Send함수는 반환 값이 입력 버퍼의 길이 보다 작은 값을 리턴 할 수 있다.
2. 닷넷Framework Socket Send함수는 반환 값이 입력 버퍼의 길이 보다 작은 값을 리턴 할 수 있으니 부분 전송 처리를 해라는 주석
3. 닷넷Framework Socket Send함수는 blocking모드에서는 입력 버퍼를 모두 전송될 때까지 block되며, nonblocking모드에서는 작은 값을 리턴 할 수 있다.


#####
저는 닷넷Framework 4.8.1, C#으로 TCP통신을 하면서 단 한번도 일부분만 전송되는 현상을 본 적이 없습니다.
부분 전송이 일어나지 않은 원인이 3번에서 말하는 것처럼 blocking모드여서 그런것이라 생각되어
아래와 같이 간단한 예제를 작성했는데도 재현에는 실패했습니다.
송신측은 nonblocking모드, SendBuffersize=8192, 10mb 데이터를 while문으로 딜레이 없이 전송, 수신측은 Thread.Sleep(1000)지연
--(예제도 올려놨습니다)

일단은 제 질문은 이렇습니다.
1. blocking모드에서 Send 함수의 반환 값이 입력 버퍼 길이보다 작은 값을 리턴 할 수 있는지
2. nonblocking모드에서 Send 함수의 반환 값이 입력 버퍼 길이보다 작은 값을 리턴 할 수 있는지
3. 비동기 Send 함수 즉 BenginSend, SendAsync 함수에서도 부분전송이 일어날 수 있는지
4. 닷넷, 닷넷Framework, winsock, 리눅스 Socket에서 차이가 있는지
   => 이부분은 최소한 닷넷, 닷넷Framework, winsock은 같아야 된다고 생각하는데 다르다면 이유도 알 수 있을까요?
5. 만약 부분전송이 일어난다면 어떻게 하면 재현할 수 있는지

참고로 저는 blocking모드에서 Send 함수, 비동기 Send 함수(BenginSend, SendAsync), 의 경우 부분전송이 일어나지 않을거라고 생각하고 있습니다.
nonblocking모드에서 Send 함수만 부분전송이 일어날수 있다고 생각하고 있습니다. 단지 재현이 잘 안될뿐
이렇게 생각한 이유는 C#에서 제공해주는 TcpClient 때문인데 TcpClient에서 전송의 경우 NetworkStream의 Write함수와 WriteAsync함수를 사용하는데
해당 함수를 디컴파일해서 확인한 결과 Socket Send와 BenginSend를 사용하고 있고, 부분전송을 대비한 코드가 전혀 작성되어있지 않았기 때문입니다.
또한 Write함수와 WriteAsync함수는 반환 값이 없는데, 만약 부분전송이 일어난다면 유저 입장에서 부분전송을 대비한 코드를 작성할수 없게 되기 때문입니다.
NetworkStream은 Position과 Length또한 NotSupportedException 으로 지원되지도 않고요
























[최초 등록일: ]
[최종 수정일: 8/25/2025]


비밀번호

댓글 작성자
 



2025-08-26 01시25분
1. 문서에도 나오지만 blocking 모드에서는 그런 경우가 없습니다. 예외/오류가 없으면 언제나 같은 값입니다. 관련해서 저도 예전에 쓴 글의 예제 코드에서,

System.Net.Sockets.Socket이 Thread-safe할까?
; https://www.sysnet.pe.kr/2/0/1469#client

"MustSendBuffer"라는 메서드 내부에 "blocking call인 경우 sentLength == mustSend이지만!"라는 주석을 달았군요. ^^

2. 역시 문서상으로는 분명히 작은 값을 리턴할 수 있다고 하니, 그에 따르는 것이 좋겠습니다.

3. (BeginSend는 오래된 인터페이스라 생략하겠습니다.) SendAsync의 경우 Win32 API로는 Overlapped 형식의 WSASend 함수를 이용해 구현합니다. Send 함수와는 달리 문서엔 반환값을 고려한 재전송에 대한 언급이 없는데요, 일단 이론상 따진다면 이것은 부분 전송이 발생할 수 없습니다.

왜냐하면, Overlapped I/O의 경우 Send가 완료되었을 때의 completed event가 꼭 호출 순서대로 발생한다는 보장이 없습니다. 즉 아래와 같이 호출했다면,

WSASend(..overlapped-send-buffer, "HELLO");
WSASend(..overlapped-send-buffer, "WORLD");

보낸 데이터를 알 수 있는 완료 이벤트의 순서는 "WORLD"를 보낸 이벤트가 먼저 발생할 수 있습니다. 따라서 만약 그 시점에 sentBytes가 적게 보내졌다고 해서 다시 WSASend를 호출하게 되면 TCP packet의 순서가 달라지므로 stream 전송 방식에 위반한 동작이 됩니다.


(2025-09-16 업데이트: 비동기라고 해서 다중으로 전송하는 것이 안전해야 한다는 가정은 잘못된 것 같습니다. Microsoft의 문서에도 비동기 예제가 sentLength를 체크하는 걸로 나오니, 그에 따르는 것이 좋겠습니다.)

참고로, 완료에 대한 알림을 받는 순서가 달라질 수 있다는 것이지, 전송 자체는 반드시 호출 순서로 됩니다.

4. 닷넷 프레임워크와 winsock은 같을 것입니다. 단지 리눅스는 제가 장담할 수가 없습니다. 아직 그 부분으로는 제가 한참 지식이 부족합니다. 단지 제 경험으로는, 네트워크 관련해서는 API 수준에서 세세한 표준이 있지는 않았다는 점입니다.

5. 정리해 보면, 결국 non-blocking 모드에서의 부분 전송 가능성이 문제가 되는 것인데요, 이 부분은 정해성 님이 마침 흥미를 가지고 계시니 이리저리 테스트를 해보시면 더 빨리 답을 얻지 않을까 싶습니다. ^^ 개인적으로 non-blocking 소켓을 잘 쓰고 있지도 않고, 설령 쓰더라도 용기가 없어 문서에서 나온 대로 어차피 코딩을 할 것이므로... 현재는 모르겠고 나중에 심심해지면 그때나 돼야 한 번 해보게 될 것 같습니다.

그나저나, 쉽게 문제가 재현이 되면 좋겠지만 재현이 안 된다고 해도 100% 부분 전송이 발생하지 않는다고 장담할 수 없다는 것 또한 문제입니다. ^^
정성태
2025-08-26 08시20분
답변 감사합니다. 며칠 동안 검색도 해보고 GPT 에게도 물어봤는데 만족스럽지 못했는데 속이 뻥 뚫리네요. GPT의 경우 워낙 오락가락하게 대답을 하고, 제가 2번에 올린 MS의 예제에서도 조금 내려가면 비동기 함수로도 똑같이 예제를 작성했더라구요. 부분전송해야한다는 주석과 함께.... 이건 예제가 잘못되지 않았나 생각됩니다. 아무튼 non-blocking 모드는 저도 사용할 마음은 없지만 직접 눈으로 보지 않으면, 답답할 거 같아서 그 부분은 이리저리 테스트를 더 해봐야 될 것 같습니다. 덕분에 좀 더 확신을 가지고 테스트 할 수 있을 거 같네요. 다시 한번 감사드립니다.
정해성
2025-08-26 10시41분
저도 소켓은 꽤나 테스트를 하면서 진행을 하곤 합니다. 경험으로 보면, 이게 OS가 업데이트되면 네트워크 스택의 처리도 달라지는 듯해서 문서에 나온 내용이 과거 OS를 기준으로 적용된 것이 아닐까... 하는 짐작만 하게 됩니다.

예를 들어 기억에 남는 부분이 Nagle 테스트였는데요,

Wireshark + C#으로 확인하는 PSH flag와 Nagle 알고리듬
; https://www.sysnet.pe.kr/2/0/12531

기존에 알려져 있던 상식과는 달리 제가 테스트했던 2021년 당시의 환경에서는 Socket.NoDelay 옵션에 상관없이 버퍼링 과정을 거치지 않고 즉시 서버로 전송하는 결과를 얻었습니다. 어쩌면 제 컴퓨터의 NIC 카드에 대한 Device Driver가 그렇게 하는 것일 수도 있고, 아니면 운영체제의 네트워크 커널 단에서 그럴 수도 있는데... 암튼 결과상으로 보면 Nagle 설정 유무가 아무런 의미가 없었습니다.

-------------------

그렇긴 한데, 만약 Nagle 설정을 꺼야 한다면 여전히 저는 NoDelay 옵션을 조정할 것 같습니다. ^^; 어쨌든 코드는 보수적으로 대응하는 것이 좋을 듯하고 그 약간의 코드가 딱히 전체적인 성능에 영향을 미치는 것은 아니므로... 뭐랄까... 안정성을 높인다는 좋은 구실도 되니까요. ^^;
정성태

... 16  17  18  19  [20]  21  22  23  24  25  26  27  28  29  30  ...
NoWriterDateCnt.TitleFile(s)
5467hero...2/4/202115928실행 환경에 따른 Thread.Sleep 딜레이 차이 질문 [8]
5466pr1/29/202116094c# winform load시 작업표시줄에 뜨지 않는 현상을 겪으신적이 있으신가요? [4]파일 다운로드1
5465영귤1/28/202115967두 번째 await 부터는 스레드 개수만 늘어나는 것이 아닌가요 [1]
5464민우1/26/202117467C# 빌드시 코드 치환되는걸 확인하는 방법 문의 [2]
5463한예지 donator1/24/202118514Parameters.AddWithValue 와 Parameters.Add 의 차이점이 궁금합니다. [2]
5462C#초보1/22/202118753사용자 정의 메시지 전달이 가능한가요? [2]
5461한예지 donator1/22/202116578AsEnumerable() 메서드 질문 있습니다! [2]
5459한예지 donator1/21/202116297typeof와 GetType의 차이점 질문있습니다. [2]
5458진우1/20/202117752C# DataTable 에 SQL 쿼리문을 실행하는 방법 문의 (LINQ 사용하지 않고) [6]
5456성민1/17/2021194259.0 출간 계획이 있으신가요? [2]
5455한예지 donator1/16/202116412교재 194페이지 콜백메서드 질문 있습니다! [5]
5454한예지 donator1/15/202115137교재 208쪽 질문....있습니다... [3]
5453안녕하세요1/15/202117367C# dll 파일을 C++에서 사용 시 memory leak 문제 [2]파일 다운로드1
5452예지1/15/202117060var를 사용할 수 없는 이유가 궁금합니다! [3]
5451예지1/14/202114767for문의 초기식에 대해 질문드립니다. [3]
5450예지1/13/202115112Action 델리게이트 사용법 질문있습니다! [2]
5449김성민1/13/202116182Winform UserControl 상속 vs 감싸기? [2]
5448서형주1/13/202115762안녕하세요~~ DataGridView에 데이터를 표시하는 동작방법이 궁금합니다. [2]
5447종범1/11/202116729[WPF/OpenCV] 이미지->영상 저장에 대해서 질문 드립니다!! [5]파일 다운로드1
5446민우1/11/202115384닷넷 런타임을 dll 파일로 포함시킬수 있나요? [2]
5445정도현1/8/202113893directShow RenderFile 관련 재질문드립니다 [5]파일 다운로드1
5444정도현1/8/202114356directShow RenderFile 관련 질문드립니다 [3]
5443윤영호1/7/202115133xml 파일에서 데이터를 가지고 와서 list에 넣는 것을 질문드리고 싶습니다. [1]파일 다운로드1
5442진우1/4/202114304DB연결 객체나 파일 등은 GC 에서 관리해주지 않는 이유가 궁금합니다. [2]
5441한예지 donator1/4/202115894DB 연결 방법 질문 있습니다. [1]
5440한예지 donator1/1/202115755추상클래스로와 new [4]
... 16  17  18  19  [20]  21  22  23  24  25  26  27  28  29  30  ...