C# TCP/IP 를 이용한 Client 클래스

Software/C#|2023. 2. 13. 10:53
반응형

지난 글에서 멀티 Thread 를 이용한 서버를 구현했고 이번에는 Client 모듈을 클래스로 구현해 보았습니다. 

서버와의 연결 및 해제에 대한 이벤트와 데이타 수신에 대한 이벤트를 발생하도록 구현되어 있습니다. Client 객체를 생성한 후에 데이타 수신 이벤트를 구현하여 수신된 데이타를 원하는 목적으로 사용할 수 있습니다.

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

namespace Client
{
    public class ServerEventArgs : EventArgs
    {
        public string text { get; }    // 파라메터로 넘겨 줄 데이타
        public ServerEventArgs(string text)   // 생성자에서 변경된 Text 정보를 넘겨받는다.
        {
            this.text = text;
        }
    }
    internal class Client
    {
        public delegate void ClientEventHandler(object sender, ServerEventArgs e);
        public delegate void ServerDisConnectHandler(object sender, EventArgs e);

        private NetworkStream? stream = null ;
        private StreamReader? reader = null;
        private StreamWriter? writer = null;         

        private readonly TcpClient? client = null;
        public string ServerIP { set; get; } = "127.0.0.1";
        public int ServerPort { set; get; }

        private bool _connected = false;
        public event ClientEventHandler? OnDataReceived = null;
        public event ClientEventHandler? OnServerConnected = null;
        public event ServerDisConnectHandler? OnServerDisconnected = null;
        //--------------------------------------------------------------------------------------------------------------
        public bool Connected 
        {
            get { return _connected; } 
        }
        //--------------------------------------------------------------------------------------------------------------
        public Client()
        {
            var clientIP = GetLocalIP();
            var port = 10001;

            if (clientIP != null)
            {
                IPEndPoint clientAddress = new IPEndPoint(IPAddress.Parse(clientIP.ToString()), port);
                client = new TcpClient(clientAddress);
            }
        }
        //--------------------------------------------------------------------------------------------------------------
        public Client(string serverIP, int serverPort)
        {
            ServerIP = serverIP;
            ServerPort = serverPort;

            var clientIP = GetLocalIP();
            var port = 10001;

            if (clientIP != null)
            {
                IPEndPoint clientAddress = new IPEndPoint(IPAddress.Parse(clientIP.ToString()), port);
                client = new TcpClient(clientAddress);
            }
        }
        //--------------------------------------------------------------------------------------------------------------
        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 void Connect()
        {
            IPEndPoint serverAddress = new IPEndPoint(IPAddress.Parse(ServerIP), ServerPort);

            client?.Connect(serverAddress);
            _connected = (bool)client?.Connected!;

            if(_connected) 
            { 
                stream = client.GetStream();
                reader = new StreamReader(stream, Encoding.GetEncoding("utf-8"));
                writer = new StreamWriter(stream, Encoding.GetEncoding("utf-8")) { AutoFlush = true };

                if(OnServerConnected is not null)
                {
                    OnServerConnected(this, new ServerEventArgs(serverAddress.ToString()));     // 서버 연결에 대한 이벤트 함수 실행 
                }

                Thread receive_thread = new Thread(ReceiveMessage);
                receive_thread.Start();
            }
        }
        //--------------------------------------------------------------------------------------------------------------
        public void Disconnect()
        {
            if(_connected)
            {
                _connected = false;

                reader?.Close();
                writer?.Close();
                stream?.Close();

                client?.Close();
            }
        }
        //--------------------------------------------------------------------------------------------------------------
        public void SendMessage(string message)
        {
            writer?.WriteLine(message);
        }
        //--------------------------------------------------------------------------------------------------------------
        private void ReceiveMessage()
        {
            string? message;

            while(_connected)
            {
                try
                {
                    message = reader?.ReadLine();

                    if (message == null) break;

                    if (OnDataReceived is not null)
                    {
                        OnDataReceived(this, new ServerEventArgs(message));     // 데이타 수신에 대한 이벤트 함수 실행 
                    }
                }
                catch (Exception ex)
                {
                    if (OnServerDisconnected is not null)
                    {
                        OnServerDisconnected(this, new EventArgs());     
                    }

                    break; 
                }    
            }
        }
        //--------------------------------------------------------------------------------------------------------------
    }
}

사용 방법은 아래와 같습니다. 아래 코드에서 서버와의 연결 / 해제에 대한 이벤트는 없지만 필요하면 client 클래스 코드를 보시고 추가 하시면 됩니다. 

public partial class Form1 : Form
{
    Client client = new Client("192.168.0.12", 5555);
    public Form1()
    {
        InitializeComponent();

        client.OnDataReceived += DataReceived;
    }

    private void button1_Click(object sender, EventArgs e)
    {
        client.Connect();
    }

    private void button2_Click(object sender, EventArgs e)
    {
        client.SendMessage(textBox1.Text);
    }

    private void button3_Click(object sender, EventArgs e)
    {
        client.Disconnect();
    }

    private void DataReceived(object sender, ServerEventArgs e)
    {
        // client 클래스에서 데이타 수신부가 thread로 구현되어 있기 때문에 
           메인 화면의 TextBox 에 수신된 데이타를 쓸경우 아래와 같이 Invoke를 
           사용했습니다. 
        textBox2.Invoke(() => { textBox2.Text = e.text; });
    }
}
반응형

댓글()