asp.net

[.net framework] Socket 통신(서버)

TTTGGG 2025. 2. 18. 13:34
728x90
반응형
SMALL

.net framework 4.7.2 WinForm

 

여러 클라이언트로부터 지속적으로 데이터를 받아올 수 있도록 구성

클라이언트 목록 관리

실시간 데이터 수신 및 UI 갱신

개별 클라이언트 연결 해제 및 전체 해제 기능

서버 시작 및 종료 기능

using System;
using System.Collections.Concurrent;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace SocketServer
{
    public partial class Form1 : Form
    {
        private TcpListener server;
        private ConcurrentDictionary<string, TcpClient> clients = new ConcurrentDictionary<string, TcpClient>();
        private ConcurrentDictionary<string, CancellationTokenSource> clientTokens = new ConcurrentDictionary<string, CancellationTokenSource>();
        private SynchronizationContext syncContext;

        public Form1()
        {
            InitializeComponent();
            syncContext = SynchronizationContext.Current;
        }

        // 서버 시작 버튼
        private async void btnStartServer_Click(object sender, EventArgs e)
        {
            int port = 5000;
            server = new TcpListener(IPAddress.Any, port);
            server.Start();
            AppendLog($"서버 시작: 포트 {port}");

            btnStartServer.Enabled = false;
            btnStopServer.Enabled = true;

            await AcceptClientsAsync();
        }

        // 클라이언트 연결 대기 (비동기)
        private async Task AcceptClientsAsync()
        {
            while (true)
            {
                try
                {
                    TcpClient client = await server.AcceptTcpClientAsync();
                    string clientKey = client.Client.RemoteEndPoint.ToString();

                    //if (clients.Count >= 130)
                    //{
                    //    AppendLog($"연결 거부: {clientKey} (최대 클라이언트 초과)");
                    //    client.Close();
                    //    continue;
                    //}

                    clients.TryAdd(clientKey, client);
                    var cts = new CancellationTokenSource();
                    clientTokens.TryAdd(clientKey, cts);

                    syncContext.Post(_ =>
                    {
                        lstClients.Items.Add(clientKey);
                        AppendLog($"클라이언트 연결: {clientKey}");
                    }, null);

                    _ = HandleClientAsync(client, clientKey, cts.Token);
                }
                catch (Exception ex)
                {
                    AppendLog($"[에러] 클라이언트 연결 실패: {ex.Message}");
                    break;
                }
            }
        }

        // 클라이언트 데이터 처리 (비동기)
        private async Task HandleClientAsync(TcpClient client, string clientKey, CancellationToken token)
        {
            try
            {
                using (NetworkStream stream = client.GetStream())
                {
                    byte[] buffer = new byte[1024];

                    while (!token.IsCancellationRequested)
                    {
                        int bytesRead = await stream.ReadAsync(buffer, 0, buffer.Length, token);
                        if (bytesRead == 0) break;

                        string receivedData = Encoding.UTF8.GetString(buffer, 0, bytesRead);
                        syncContext.Post(_ => AppendLog($"[{clientKey}] {receivedData}"), null);
                    }
                }
            }
            catch (OperationCanceledException)
            {
                AppendLog($"연결 종료: {clientKey} (사용자 요청)");
            }
            catch (Exception ex)
            {
                AppendLog($"[에러] {clientKey}: {ex.Message}");
            }
            finally
            {
                DisconnectClient(clientKey);
            }
        }

        // 개별 클라이언트 연결 해제
        private void DisconnectClient(string clientKey)
        {
            if (clients.TryRemove(clientKey, out TcpClient client))
            {
                client.Close();
            }

            if (clientTokens.TryRemove(clientKey, out CancellationTokenSource cts))
            {
                cts.Cancel();
                cts.Dispose();
            }

            syncContext.Post(_ =>
            {
                lstClients.Items.Remove(clientKey);
                AppendLog($"클라이언트 종료: {clientKey}");
            }, null);
        }

        // 선택한 클라이언트 해제 버튼
        private void btnDisconnectSelected_Click(object sender, EventArgs e)
        {
            if (lstClients.SelectedItem is string selectedKey)
            {
                DisconnectClient(selectedKey);
            }
        }

        // 모든 클라이언트 연결 해제
        private void btnDisconnectAll_Click(object sender, EventArgs e)
        {
            foreach (var key in clients.Keys)
            {
                DisconnectClient(key);
            }
            AppendLog("모든 클라이언트 연결 해제");
        }

        // 서버 정지 버튼
        private void btnStopServer_Click(object sender, EventArgs e)
        {
            foreach (var key in clients.Keys)
            {
                DisconnectClient(key);
            }

            server.Stop();
            btnStartServer.Enabled = true;
            btnStopServer.Enabled = false;
            AppendLog("서버 정지됨");
        }

        // 로그 출력
        private void AppendLog(string message)
        {
            if (txtLog.InvokeRequired)
            {
                txtLog.Invoke(new Action(() =>
                    txtLog.AppendText($"{DateTime.Now:HH:mm:ss} {message}{Environment.NewLine}")
                ));
            }
            else
            {
                txtLog.AppendText($"{DateTime.Now:HH:mm:ss} {message}{Environment.NewLine}");
            }
        }

    }
}

 

728x90
반응형
LIST