๋ฐฐ๊ฒฝ
์์ฒ๊ฐ์ ์์ผ์ ์ด๊ณ Accept๋ฅผ Multiplexing ์ผ๋ก ์ฒ๋ฆฌํด์ผ ํฉ๋๋ค.
select๋ฅผ ์ฌ์ฉํ์ฌ ๋ฒ์ฉ์ ์ธ ์ฝ๋๋ฅผ ์์ฑํ ์ ์์ง๋ง
์ด๋ฒคํธ๋ฅผ ์ฒ๋ฆฌํ๊ธฐ ์ํด ์์ผ์ ๊ฐ์๋งํผ ์ ํ์ผ๋ก ์ฐพ๊ณ ๋งค ํธ์ถ๋ง๋ค
์์ผ ๋ฆฌ์คํธ๋ฅผ ๋ณต์ฌํด์ผํ๋ ๋ฌธ์ ๊ฐ ์์ต๋๋ค.
์ด๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด IO Completion Port ๋ฅผ ์ฌ์ฉํ ์ ์์ง๋ง
IOCP๋ Windows ์ ๊ธฐ์ ์ด๊ธฐ ๋๋ฌธ์ Linux์์ ๋์์ ์ฐพ์์ผํฉ๋๋ค.
epoll?
epoll์ ํ์ฅ ๊ฐ๋ฅํ I/O ์ด๋ฒคํธ ์๋ฆผ ๋ฉ์ปค๋์ฆ์์ํ Linux ์ปค๋ ์์คํ ํธ์ถ์ ๋๋ค.
ํ๊ฒฝ
- .NET 6, C# 10
- Ubuntu 20.04
๊ตฌํ
epoll_create
epoll_ctl
epoll_wait
์ ์ธ๊ฐ ํจ์๋ฅผ ์ฌ์ฉํ๊ธฐ ์ํด P/Invoke ์ฝ๋๋ฅผ ์์ฑํฉ๋๋ค.
(Tdms.Linux ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฌ์ฉํด๋ ๋ฉ๋๋ค.)
Libc.cs
public static class Libc
{
public const int EPOLL_CTL_ADD = 0x1;
public const int EPOLL_CTL_DEL = 0x2;
public const int EPOLL_CTL_MOD = 0x3;
[StructLayout(LayoutKind.Explicit)]
public struct epoll_data
{
[FieldOffset(0)]
public IntPtr ptr;
[FieldOffset(0)]
public int fd;
[FieldOffset(0)]
public uint u32;
[FieldOffset(0)]
public ulong u64;
};
[StructLayout(LayoutKind.Explicit)]
public struct epoll_event
{
[FieldOffset(0)]
public EPOLL_EVENTS events;
[FieldOffset(4)]
public epoll_data data;
};
[Flags]
public enum EPOLL_EVENTS : uint
{
EPOLLIN = 0x001,
EPOLLPRI = 0x002,
EPOLLOUT = 0x004,
EPOLLRDNORM = 0x040,
EPOLLRDBAND = 0x080,
EPOLLWRNORM = 0x100,
EPOLLWRBAND = 0x200,
EPOLLMSG = 0x400,
EPOLLERR = 0x008,
EPOLLHUP = 0x010,
EPOLLRDHUP = 0x2000,
EPOLLEXCLUSIVE = 1u << 28,
EPOLLWAKEUP = 1u << 29,
EPOLLONESHOT = 1u << 30,
EPOLLET = 1u << 31
}
[DllImport("libc.so.6")]
public static extern int epoll_create(int size);
[DllImport("libc.so.6")]
public static extern unsafe int epoll_ctl(int epfd, int op, int fd, epoll_data* epevent);
[DllImport("libc.so.6")]
public static extern unsafe int epoll_wait(int epfd, epoll_data* events, int maxevents, int timeout);
}
Program.cs
const int portStart = 10000;
const int portEnd = 12000;
// epoll ์์ฑ
var epollDescriptor = Libc.epoll_create(1);
if (epollDescriptor < 0)
throw new IOException($"Call to epoll_create API failed({epollDescriptor})");
var sockets = new Dictionary<int, Socket>();
for (int i = portStart; i <= portEnd; i++)
{
var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
socket.Bind(new IPEndPoint(IPAddress.Any, i));
socket.Listen();
sockets[socket.Handle.ToInt32()] = socket;
// epoll ์ด๋ฒคํธ ๋ฑ๋ก
unsafe
{
var epEvent = new Libc.epoll_event
{
events = Libc.EPOLL_EVENTS.EPOLLIN,
data = new Libc.epoll_data
{
fd = socket.Handle.ToInt32()
}
};
var result = Libc.epoll_ctl(epollDescriptor, Libc.EPOLL_CTL_ADD, epEvent.data.fd, &epEvent);
if (result != 0)
throw new IOException($"Call to epoll_ctl(EPOLL_CTL_ADD) API failed({result})");
}
}
// epoll ์ด๋ฒคํธ ์์ ํ Socket.Accept
unsafe
{
var epEvent = new Libc.epoll_event();
while (true)
{
var result = Libc.epoll_wait(epollDescriptor, &epEvent, 1, -1);
if (result > 0)
{
var socket = sockets[epEvent.data.fd];
var client = socket.Accept();
Console.WriteLine($"[{socket.LocalEndPoint}] Accept from {client.RemoteEndPoint}");
}
}
}