기존의 Connect는 블로킹함수로 Connect요청부터 연결이 완료될 때까지 요청한 쓰레드는 다른 연산작업을 할 수 없다. 이를 위해 비동기 Connect를 위한 Connector Class를 만들 필요가 있다.
Connector Class의 구조는 Listener Class와 비슷하다.
서버에서 Connect를 한다는 것은 다른 호스트에 연결을 요청한다는 의미인데 '서버에서 연결을 요청할 일이 있을까?' 라고 생각할 수 있지만 다음과 같은 구조의 서버에서는 서버간의 Connect가 필요할 수 있다.
ConnectAsync를 사용한 비동기 Connect를 구현하는 것은 Session의 Send를 구현한 것과 매우 유사한데 Connect변수
, 인터페이스
, 비동기 함수 호출
, 콜백 함수
를 구현하면 된다.
Func<Session> _sessionFactory;
Connect Class의 변수로는 Session
을 생성하기 위한 팩토리 메서드가 필요하다. 실제 연결이 이루어 지고 어떻게 Session을 생성하는지는 나중에 설정하는 구조이다.
팩토리 메서드패턴이 생각나는 구조이다.
public void Connect(IPEndPoint endPoint, Func<Session> sessionFactory)
{
Socket socket = new Socket(endPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
_sessionFactory = sessionFactory;
SocketAsyncEventArgs args = new SocketAsyncEventArgs();
args.Completed += OnConnectCompleted;
args.RemoteEndPoint = endPoint;
args.UserToken = socket;
RegisterConnect(args);
}
외부에서 호출가능한 Connect 인터페이스로 Connect 요청을 위한 Socket
, sessionFactory 설정
, SocketAsyncEventArgs 설정
, ConnectAsync 호출
을 진행한다.
Session Class를 구현했을 때와 마찬가지로 Completed 이벤트의 콜백함수는 OnConnectCompleted
로 하였다.
void RegisterConnect(SocketAsyncEventArgs args)
{
Socket socket = args.UserToken as Socket;
if (socket == null)
return;
bool pending = socket.ConnectAsync(args);
if (pending == false)
OnConnectCompleted(null, args);
}
ConnectAsync를 실행하는 RegisterConnect함수로 SocketAsyncEventArgs에 포함된 Socket을 통해 비동기 Connect을 시도한다.
만약 시간내에 Connect가 완료된다면 콜백함수를 직접 실행한다.
void OnConnectCompleted(object sender, SocketAsyncEventArgs args)
{
if(args.SocketError == SocketError.Success)
{
Session session = _sessionFactory.Invoke();
session.Start(args.ConnectSocket);
session.OnConnected(args.RemoteEndPoint);
}
else
{
Console.WriteLine($"OnConnected Fail: {args.SocketError}");
}
}
Connnect가 완료되었을때 실행할 함수로 _sessionFactory
로 Session을 생성한 뒤 연결된 소켓으로 Session을 설정한다. 설정을 끝냈다면 Session.OnConnected로 Connect된 후의 Session 이벤트를 실행시킨다.