서버 코드
using System;
using System.Net.Sockets;
using System.Net;
using System.Threading;
using System.Threading.Tasks;
using System.Text;
namespace ServerCore
{
class Program
{
static Listener _listener = new Listener();
static void OnAcceptHandler(Socket clientSocket)
{
try
{
//클라이언트로부터 받아오는 처리
byte[] recvBuff = new byte[1024];
int recvBytpes = clientSocket.Receive(recvBuff);
string recvData = Encoding.UTF8.GetString(recvBuff, 0, recvBytpes);
Console.WriteLine($"[From Client] {recvData}");
//클라이언트로 보내는 처리
byte[] sendBuff = Encoding.UTF8.GetBytes("Welcome to server!");
clientSocket.Send(sendBuff);
clientSocket.Shutdown(SocketShutdown.Both); //연결된 소켓 끊기, 듣기와 말하기를 하지 않겠다는 것
clientSocket.Close(); //클라와 서버간의 연결 끊기
}
catch (System.Exception ex)
{
Console.WriteLine(ex);
}
}
static void Main(string[] args)
{
//DNS : Domain Name System
// 도메인을 하나 등록해서 해당하는 IP 를 찾아오면 관리가 쉬워짐
//www.google.com =>
string host = Dns.GetHostName();
//host = "google.com"; //ipHost.AddressList[0] == {172.217.161.238}
IPHostEntry ipHost = Dns.GetHostEntry(host);
//이렇게 GetHostEntry 로 주소를 얻어오는 건 DNS 서버를 통해서 얻어 올 수 있게 됨
// ipHost.addressList[0] = IPAddress.Parse(" 경우에 따라서 ip 주소는 여러개 일 수도 있다 부하 분산을 위해서 addressList
IPAddress ipAddr = ipHost.AddressList[0];
IPEndPoint endPoint = new IPEndPoint(ipAddr, 7777); //최종 주소
try
{
_listener.Init(endPoint, OnAcceptHandler);
while (true)
{
}
}
catch(Exception e)
{
Console.WriteLine(e);
}
}
}
}
리스터 코드 : 비동기 accept
using System;
using System.Collections.Generic;
using System.Text;
using System.Net.Sockets;
using System.Net;
namespace ServerCore
{
class Listener
{
Socket _listenSocket;
Action<Socket> _onAcceptHandler;
public void Init(IPEndPoint iPEndPoint, Action<Socket> onAcceptHandler)
{
//AddressFamily ip version 4,6 에 대한 것 , 위에서 자동으로 만들어줌,
//tcp 로 할 경우 stream, tcp 로 설정해준다
//리슨 하는 자체가 소켓을 하나 만들어야 한다
_listenSocket = new Socket(iPEndPoint.AddressFamily , SocketType.Stream, ProtocolType.Tcp);
_onAcceptHandler += onAcceptHandler;
_listenSocket.Bind(iPEndPoint); //소켓에 ip 와 포트 할당
//최대 동시 대기 수, 동시에 들어올대 10명까지만 처리 가능하고 그 위로는 실패가 된다
_listenSocket.Listen(10);
//이건 한번 사용하고 재사용이 가능하다
SocketAsyncEventArgs args = new SocketAsyncEventArgs();
args.Completed += new EventHandler<SocketAsyncEventArgs>(OnAcceptCompleted);
//최초 한번은 등록해준다
RegisterAccept(args);
}
void RegisterAccept(SocketAsyncEventArgs args)
{
//재사용이 됨으로 null 로 처리한다
args.AcceptSocket = null;
bool pending = _listenSocket.AcceptAsync(args); //비동기 임으로 예약만 하고 넘어간다, accdept 완료는 eventHandler 를 통해서 완료된다
if (pending == false) //false 면 pending 없이 바로 완료 됐다는 얘기임
OnAcceptCompleted(null, args);
}
void OnAcceptCompleted(object sender, SocketAsyncEventArgs args)
{
if(args.SocketError == SocketError.Success)
{
//accept 되어 새로 생성된 소켓을
_onAcceptHandler.Invoke(args.AcceptSocket); //넘겨준다
}
else
{
Console.WriteLine(args.SocketError.ToString());
}
//위에 까지 처리가 된것은 accept 가 완료 된것임으로 새로운 accept 를 위해서
//RegisterAccept 를 다시 호출하여 OnAcceptCompleted 이벤트를 받아 들을 수 있는 상태로 만든다
RegisterAccept(args);
}
public Socket Accept()
{
//return _listenSocket.Accept();
//클라의 접속이 있다면 받아오는 처리, 접속이 있을때까지 계속 대기, 즉 다음으로 넘어가지 않는다
//클라로부터 접속이 왔다면 accept 되어 클라와 별도 통실한 socket 이 생성되어 리턴된다
//return _listenSocket.Accept();
//async 는 비동기로 처리 된다
//return _listenSocket.AcceptAsync()
return null;
}
}
}
더미 클라이언트 코드
using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
namespace DummyClient
{
class Program
{
static void Main(string[] args)
{
string host = Dns.GetHostName();
IPHostEntry ipHost = Dns.GetHostEntry(host);
IPAddress ipAddr = ipHost.AddressList[0];
IPEndPoint endPoint = new IPEndPoint(ipAddr, 7777);
while (true)
{
try
{
Socket socket = new Socket(endPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
//소켓으로 서버에 연결한다 , 서버 입장에선 accept 가 된다
socket.Connect(endPoint);
Console.WriteLine($"connected to {socket.RemoteEndPoint.ToString()}");
//서버로 보낸다
byte[] sendBuff = Encoding.UTF8.GetBytes("Hello world!");
int bytesSent = socket.Send(sendBuff);
//서버에서 받는다
byte[] recvBuff = new byte[1024];
int recvBytes = socket.Receive(recvBuff);
string recvData = Encoding.UTF8.GetString(recvBuff, 0, recvBytes);
Console.WriteLine($"received : {recvData}");
//서버와 통신을 위해 생성했던 소켓 종료 처리
socket.Shutdown(SocketShutdown.Both);
socket.Close();
}
catch (System.Exception ex)
{
Console.WriteLine(ex);
}
Thread.Sleep(100);
}
}
}
}
AcceptAsync : 비동기 방식으로 클라의 접속을 말한다
AcceptAsync 로 처리하면 서버에서 클라의 접속을 받아 Accept() 함수 처럼 accept 하게 될때 해당 라인에서 무한 대기 하지 않고 클라와 통신하기 위한 전용 socket 을 만들어 리턴하고 서버에선 바로 다음 로직을 처리 할 수 있게 된다
bool pending = _listenSocket.AcceptAsync(args);
결과화면
반응형
'서버(Server) > Server' 카테고리의 다른 글
멀티스레드를 고려한 SendAsync 보내기 (0) | 2022.12.24 |
---|---|
_socket.ReceiveAsync 비동기 처리 (0) | 2022.12.23 |
기본적인 소켓 프로그래밍 (0) | 2022.12.23 |
소켓 통신 (0) | 2022.12.22 |
통신 모델 OSI 7 계층 (0) | 2022.12.22 |