Coding Memo

[C#] SocketAsyncEventArgs.BufferList 참고사항 본문

Language/C#

[C#] SocketAsyncEventArgs.BufferList 참고사항

minttea25 2023. 7. 16. 22:43

[요약]

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[] 필드가 있는데 실제 전송하는 것은 이 배열 필드이기 떄문이다.

 

https://stackoverflow.com/questions/11820677/how-use-bufferlist-with-socketasynceventargs-and-not-get-socketerror-invalidargu

 

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가 들어가 있는 것을 확인 할 수 있다. 그리고 데이터를 받는 쪽에서도 아무 데이터를 받지 못한다.

 

무엇이 문제일까 공식 문서도 살펴보았다.

https://learn.microsoft.com/en-us/dotnet/api/system.net.sockets.socketasynceventargs.bufferlist?view=net-7.0 

 

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);
    }
}