Coding Memo
[C#] SocketAsyncEventArgs.BufferList 참고사항 본문
[요약]
SocketAsyncEventArgs.BufferList을 통해 데이터를 전송 할 때, 반드시 새로운 리스트를 만들고 바이트 데이터를 그 리스트에 추가(Add)를 한 후에 그 리스트를 SocketAsyncEventArgs.BufferList에 직접 할당해야만 한다!!
(이 관련 내용은 공식적으로 문서화 되어있지 않다고 한다.)
1. 리스트 생성 (IList<ArraySegment<byte>>)
2. 1에서 생성한 리스트에 아이템 추가(Add)
3. SocketAsyncEventArgs.BufferList에 1에서 생성한 리스트 직접 할당
***SocketAsyncEventArgs.BufferList에 직접 아이템 Add 하지 말자.***
Why?
BufferList의 Setter 호출 시, 기존 리스트에 있는 버퍼만 사용하기 때문이다.
(즉, 이후 아이템을 추가해도 반영이 안된다는 것이다.)
C#의 System.Net.Sockets에 구현되어 있는 SocketAsyncEventArgs의 BufferList의 setter 코드를 보면 알 수 있는데, BufferList를 설정할 때 바이트 배열 참조를 복사하고 사용하는 WSABuffer[] 필드가 있는데 실제 전송하는 것은 이 배열 필드이기 떄문이다.
How use BufferList with SocketAsyncEventArgs and not get SocketError InvalidArgument?
I can use SetBuffer with SocketAsyncEventArgs just fine. If I try to use BufferList (after doing SetBuffer(null, 0, 0)) I always and immediately get SocketError InvalidArgument (10022) when I do
stackoverflow.com
문제를 발견한 것은 AsyncSocketEventArgs.BufferList에 버퍼 데이터를 넣고 클라이언트나 서버로 데이터를 전송하려고 할 때였다.
다음의 코드는 문제가 없어 보인다.
단순히 이벤트의 버퍼리스트에 데이터를 추가하고 그것을 Send하는 코드이다.
public class Session
{
Socket _socket;
readonly List<Memory<byte>> _sendPendingList = new();
...
readonly SocketAsyncEventArgs _sendEvent = new();
...
void Send()
{
...
_sendEvent.BufferList = new List<ArraySegement<byte>>();
...
foreach (Memory<byte> buff in _sendPendingList)
{
if (MemoryMarshal.TryGetARray<byte>(buff, out var segment))
{
_sendEvent.BufferList.Add(segment);
}
}
...
_socket.SendAsync(_sendEvent);
}
}
하지만, 실제 전송을 하면 전송(SendAsync) 후, SocketError 필드에 SocketError.InvalidArgument가 들어가 있는 것을 확인 할 수 있다. 그리고 데이터를 받는 쪽에서도 아무 데이터를 받지 못한다.
무엇이 문제일까 공식 문서도 살펴보았다.
SocketAsyncEventArgs.BufferList Property (System.Net.Sockets)
Gets or sets an array of data buffers to use with an asynchronous socket method.
learn.microsoft.com
특별히 확인할 내용은 없었다.
해결은 위에 링크를 걸어둔 stackoverflow에서 답을 찾을 수 있었다.
따라서 코드를 다음과 같이 고쳤다.
public class Session
{
Socket _socket;
readonly List<Memory<byte>> _sendPendingList = new();
...
readonly SocketAsyncEventArgs _sendEvent = new();
...
void Send()
{
...
List<ArraySegement<byte>> list = new();
...
foreach (Memory<byte> buff in _sendPendingList)
{
if (MemoryMarshal.TryGetARray<byte>(buff, out var segment))
{
list.Add(segment);
}
}
// 직접 리스트 할당 (set)
_sendEvent.BufferList = list;
...
_socket.SendAsync(_sendEvent);
}
}
'Language > C#' 카테고리의 다른 글
[C#] lock (Monitor) (0) | 2023.08.08 |
---|---|
[C#] TCP 흐름제어 확인해보기 (0) | 2023.07.17 |
ThreadPool (SetMinThreads, SetMaxThreads) 주의사항 (0) | 2023.05.02 |
메소드 인자 전달 방법 (Method Parameters) (0) | 2023.05.02 |
매개변수 한정자 (ref, in, out) (0) | 2023.05.02 |