C# Multi Thread를 이용한 다중 Client 접속 Server
Software/C#2023. 2. 11. 12:44
반응형
C#에서 TCP/IP 를 이용한 서버 동작을 위한 클래스를 구현했습니다. 이 서버 클래스는 다중 클라이언트 접속을 허용하도록 제작하였고 클라이언트가 접속을 하면 Thread를 생성하여 개별 클라이언트와 통신하도록 구현되어 있습니다.
이번 구현은 클라이언트와 ehco 기능으로 구현되어 있어서 클라이언트에서 서버에 접속 후 메시지를 보내면 클라이언트로 보낸 메시지가 반송되도록 코딩되어 있습니다.
클라이언트 메시지에 대한 동작 구현은 아래 코드에서 ehco 기능의 함수 부분을 수정하시면 됩니다.
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
namespace Server
{
public class ServerEventArgs : EventArgs
{
public string text { get; } // 파라메터로 넘겨 줄 데이타
public ServerEventArgs(string text) // 생성자에서 변경된 Text 정보를 넘겨받는다.
{
this.text = text;
}
}
//------------------------------------------------------------------------------------------------------------
public delegate void ServerEventHandler(object sender, ServerEventArgs e);
//------------------------------------------------------------------------------------------------------------
public class ClientHandle
{
private Socket socket;
private NetworkStream? networkstream = null;
private StreamReader? streamReader = null;
private StreamWriter? streamWriter = null;
//--------------------------------------------------------------------------------------------------------
public ClientHandle(Socket client)
{
this.socket = client;
networkstream = new NetworkStream(client);
streamReader = new StreamReader(networkstream!, Encoding.GetEncoding("utf-8"));
streamWriter = new StreamWriter(networkstream!, Encoding.GetEncoding("utf-8")) { AutoFlush = true };
}
//--------------------------------------------------------------------------------------------------------
public void Run()
{
string? receiveData; // 데이타 수신이 null 이 될수 있으므로 null 참조 가능으로 선언한다.
while (true)
{
receiveData = streamReader!.ReadLine(); // NetworkStream 으로 들어오는 데이타의 끝은 CR, LF 가 필요하다.
// CR LF 가 없으면 버퍼가 overflow 될때 까지 함수 내부에서 빠져 나오지 못한다.
if (receiveData is null) break; // Client 연결이 끊어지면 null 이 리턴된다.
WriteClientData(receiveData); // Echo 기능 입니다. 다은 특별한 동작이 필요하면 이코드를 삭제하고 구현하시면 됩니다.
}
DisConnect(); // Client 연결과 관련된 부분을 모두 닫아 버린다.
}
//--------------------------------------------------------------------------------------------------------
public void DisConnect()
{
streamReader?.Close();
streamWriter?.Close();
networkstream?.Close();
socket.Close();
}
//--------------------------------------------------------------------------------------------------------
public void WriteClientData(string Message)
{
streamWriter?.WriteLine(Message);
}
//--------------------------------------------------------------------------------------------------------
}
//------------------------------------------------------------------------------------------------------------
public class MultiClientServer
{
private TcpListener? server = null;
public event ServerEventHandler? OnClientConnected = null;
public event ServerEventHandler? OnServerClosed = null;
public string? IpAddress { set; get; } = null;
public int Port { set; get; } = 5000;
//--------------------------------------------------------------------------------------------------------
public MultiClientServer()
{
this.IpAddress = GetLocalIP();
Enable();
}
//--------------------------------------------------------------------------------------------------------
public MultiClientServer(int port)
{
this.IpAddress = GetLocalIP();
this.Port = port;
Enable();
}
//--------------------------------------------------------------------------------------------------------
private static string GetLocalIP()
{
IPHostEntry host = Dns.GetHostEntry(Dns.GetHostName());
string ip = string.Empty;
foreach (var address in host.AddressList)
{
if (address.AddressFamily == AddressFamily.InterNetwork)
{
ip = address.ToString();
break;
}
}
return ip;
}
//--------------------------------------------------------------------------------------------------------
public bool Enable()
{
if (this.IpAddress == null) { return false; }
else
{
IPEndPoint local = new IPEndPoint(IPAddress.Parse(IpAddress), Port);
server = new TcpListener(local);
return true;
}
}
//--------------------------------------------------------------------------------------------------------
public void Stop()
{
server?.Stop(); // server 동작을 종료한다.
}
//--------------------------------------------------------------------------------------------------------
public void Start()
{
if (server is not null)
{
server.Start(); // Server 동작
Thread connect = new Thread(WaitClientConnect); // Client 접속 대기, Data 수신
connect.Start();
}
}
//--------------------------------------------------------------------------------------------------------
private void WaitClientConnect()
{
while (true)
{
try
{
Socket? client = server?.AcceptSocket(); // Client 접속을 기다린다.
if (client is not null)
{
var ipClient = ((IPEndPoint)client.RemoteEndPoint!).ToString(); // 접속된 Client의 IP 확인
if (OnClientConnected is not null) OnClientConnected(this, new ServerEventArgs(ipClient)); // Client 연결에 대한 이벤트 함수 실행
Thread connectedClient = new(new ThreadStart(new ClientHandle(client).Run)); // 접속된 Client 의 객체를 생성 Thread 를 사용하여 동작 수행
connectedClient.Start();
}
}
catch(Exception ex) // server 기능을 stop 하게되면 예외가 발생한다.
{
if(OnServerClosed is not null)
{
OnServerClosed(this, new ServerEventArgs(ex.ToString())); // server 기능 멈충에 대한 이벤트 발생
}
break;
}
}
}
//--------------------------------------------------------------------------------------------------------
}
}
사용방법
public partial class Form1 : Form
{
private MultiClientServer server = new MultiClientServer(5000) ; //로컬 IP는 자동으로 찾고 Port 5000 으로 서버 생성
public Form1()
{
InitializeComponent();
server.OnClientConnected += Server_OnClientConnected; // 클라이언트가 접속되었을 경우 발생하는 이벤트
}
private void Server_OnClientConnected(object sender, ServerEventArgs e)
{
// 클라이언트가 연결되면 클라이언트 IP 를 화면에 보여 줍니다.
textClientIP.Invoke(() => { textClientIP.Text = e.text; });
}
private void button1_Click(object sender, EventArgs e)
{
server.Start(); // 서버 동작 시작
}
private void button3_Click(object sender, EventArgs e)
{
server.Stop(); // 서버 동작 종료
}
}
반응형
'Software > C#' 카테고리의 다른 글
ScottPlot (Growing Data - #1) (0) | 2023.02.16 |
---|---|
C# TCP/IP 를 이용한 Client 클래스 (0) | 2023.02.13 |
C# 델리게이트를 이용한 이벤트 생성 (Part-3) (0) | 2023.02.06 |
C# 델리게이트를 이용한 이벤트 생성 (Part-2) (0) | 2023.02.05 |
C# 델리게이트를 이용한 이벤트 생성 (Part-1) (0) | 2023.02.04 |
댓글()