C#

패킷을 모아서 전송하기

monstro 2025. 1. 27. 13:13
728x90
반응형

이번 포스트에서는 이전의 포스트에서 미리 예고하였던

패킷을 모아서 전송하는 방법으로 기존의 코드를 수정해보겠습니다.

 

1) Server

class Server
{
    static Listener serverListener = new Listener();
    // 새로운 채팅방을 위한 GameRoom 생성
    public static GameRoom Room = new GameRoom();

    static void Main(string[] args)
    {
        
		.
		.
		.

        while (true)
        {
            Room.Push(() => Room.Flush());
            Thread.Sleep(250);
        }
    }
}

 

Server의 메인 스레드에서 기존에 아무것도 하지 않는 방향 대신

위와 같이 0.25초를 기준으로 GameRoom의 Flush 함수를 호출하게끔 수정하였습니다.

 

2) GameRoom

class GameRoom : IJobQueue
{
    
	.
	.
	.
    
    List<ArraySegment<byte>> _pendingList = new List<ArraySegment<byte>>();

	.
	.
	.

    public void Flush()
    {
        foreach (ClientSession eachSession in _sessions)
            eachSession.Send(_pendingList);

        Console.WriteLine($"Flushed {_pendingList.Count} Items");
        _pendingList.Clear();
    }

    public void Broadcast(ClientSession session, string chat)
    {
        
		.
 		.
 		.

        // 매 프레임마다 foreach 문으로 순회하면서 ClientSession을 통해 메세지를 브로드캐스트하지 않고
        // _pendingList에 메세지를 저장함
        _pendingList.Add(segment);
    }

	.
	.
	.
    
}

 

GameRoom에서는 아주 중요한 역할을 맡게 됩니다.

패킷을 모아 보낸다는 주제에서 패킷을 모은다의 개념패킷을 보낸다의 개념

모두를 GameRoom에서 수행하게 됩니다.

 

새로 추가한 _pendingList패킷을 모아 보관하는 역할을 수행합니다.

 

이전에 사용하던 Broadcast의 경우,

기존의 foreach문을 사용하여 메세지를 일일이 브로드캐스트하는 방식 대신

_pendingList에 전송할 패킷을 저장하게 됩니다.

 

최종적으로 저장된 패킷들을 보내는 역할Flush 메서드가 수행합니다.

Flush 메서드에서는 우선 _pendingList를 순회하면서 저장된 패킷을 Send 합니다.

순회가 끝난 후, 보내진 전체 패킷의 개수를 출력하고

_pendingList를 깔끔하게 비워줍니다.

 

 3) Session

현재 Send에서는 패킷을 받는 버전만 존재하고, 리스트를 받는 버전이 존재하지 않았습니다.

따라서 Send를 오버로드하여 리스트를 받는 버전을 만들겠습니다.

 

그리고 Session에서 Recv하는 RecvBuffer의 양을 늘려주어

버퍼 사이즈에 따른 문제를 미리 해결하도록 하겠습니다.

 

그리고 Recv 하는 경우, 받은 패킷이 하나라도 존재하다면 

모아보내진 패킷의 개수를 출력하도록 하겠습니다.

public abstract class PacketSession : Session
{
    
	.
	.
	.
    
    public sealed override int OnRecv(ArraySegment<byte> buffer)
    {
        
		.
		.
		.
        
		int packetCount = 0;
        
		while (true)
		{
    		if (buffer.Count < HeaderSize)
        		break;

    		ushort dataSize = BitConverter.ToUInt16(buffer.Array, buffer.Offset);
    		if (buffer.Count < dataSize)
        		break;

    		OnRecvPacket(new ArraySegment<byte>(buffer.Array, buffer.Offset, dataSize));
    		packetCount++;

    		processLength += dataSize;
    		buffer = new ArraySegment<byte>(buffer.Array, buffer.Offset + dataSize, buffer.Count - dataSize);
		}

		if (packetCount > 1)
			Console.WriteLine($"모아보내진 패킷 : {packetCount}");

		.
		.
		.
        
    }

	.
	.
	.
    
}

public abstract class Session
{
	
	.
	.
	.

    RecvBuffer _recvBuffer = new RecvBuffer(65535);
	
	.
	.
	.

    public void Send(List<ArraySegment<byte>> sendBuffList)
    {
        if (sendBuffList.Count == 0)
            return;

        lock (_lock)
        {
            foreach(ArraySegment<byte> sendBuff in sendBuffList)
                _sendQueue.Enqueue(sendBuff);

            if (_pendingList.Count == 0)
                RegisterSend();
        }
    }

	.
	.
	.
    
}

 

4) SendBuffer

Recv 하는 용량을 늘려주었으므로, 동일하게 Send의 용량도 늘려주겠습니다.

// SendBuffer를 래핑하는 클래스
public class SendBufferHelper
{
	
	.
	.
	.
    
    public static int ChunkSize { get; set; } = 65535 * 100;

	.
	.
	.
    
}

 

최종 실행 결과는 다음과 같습니다.

728x90
반응형

'C#' 카테고리의 다른 글

유니티 연동 #1  (0) 2025.01.28
JobTimer  (0) 2025.01.27
JobQueue #2  (0) 2025.01.22
JobQueue #1  (0) 2025.01.22
Command 패턴  (0) 2025.01.22