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

namespace MYTCP
{

    //라이브러리 형태로 작성될 크래스.
    //TCP클라이언트로 접속할 수 있는 클래스를 작성.
    public class Client
    {
        //이벤트
        //연결이되면 연결을 알려주는 이벤트
        public delegate void ConnectedHandler(Socket s);
        public delegate void DisConnectedHandler(string disconnectMsg);
        public delegate void ReceivedHandler(byte[] bytes);
        public event ConnectedHandler OnConnected;
        public event DisConnectedHandler OnDisConnected;
        public event ReceivedHandler OnReceived;

        private Socket socket = null;
        private string ip = "";
        private int port = 43210;
        private Thread thread = null;
        private bool stop = false; //클라이언트가 연결이 종료됬는지.연결이 되있는 상태인지..

        public Client(string ipAddress,int port)
        {
            this.ip = ipAddress;
            this.port = port;
            //ip주소와,port를 할당.
            //127.120.120.255
            //데이터 중복없이 신뢰성있는 양방향 연결 기반의 바이트스트림
            socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
        }

        //클라이언트 연결을 시작
        public void Start()
        {
            try
            {
                //IPEndPoint클래스란? 끝점,즉 연결될 곳의 IP주소와 Port를 가지고있는 클래스
                //IP주소->어떠한 컴퓨터(서버)로 접속을 요청할 것인지..
                //PORT ->서버의 어떠한 프로그램에 접속을 요청할 것인지.,
                IPEndPoint remoteEp = new IPEndPoint(IPAddress.Parse(ip), port);
                socket.Connect(remoteEp);//서버에게 연결을 요청하게됨..->Connect 동기적으로 요청.->서버 한대만을,서버프로그램이라면 BeginConnect함수를 사용.
                                         //실패하면
                if (OnConnected != null)
                {
                    //연결이되면 이벤트를 통해서 사용하는 곳에 알려준다. 
                    OnConnected(socket);
                }

                //서버와 송수신 즉 데이터를 주고받아야됨.
                thread = new Thread(ReceiveMassage);
                thread.IsBackground = true;//프로그램이 종료되면
                thread.Start();
            }
            catch (Exception)
            {
                //서버와 연결실패시
                throw;
            }


        }

        //데이터수신 함수
        private void ReceiveMassage()
        {
            //클라이언트 연결이 끊긴게아니라면 계속해서 반복해서 수신하도록..
            while(!stop)
            {
                try
                {
                    //네트워크에서 바이트형태로 데이터를 주고
                    byte[] buffer = new byte[1024];//1024바이트

                    int bytesRead = socket.Receive(buffer,0,buffer.Length,SocketFlags.None);//데이터 수신.

                    if (bytesRead<=0)
                    {
                        throw new SocketException();
                    }

                    //buffer 실질적으로 데이터가 쓰인것을
                    Array.Resize(ref buffer, bytesRead);

                    if(OnReceived!=null)
                    {
                        OnReceived(buffer);
                    }

                }
                catch
                {
                    if(OnDisConnected!=null)
                    {
                        OnDisConnected("Disconnected");
                    }

                    //소켓연결 끊는작업
                    if (socket!=null)
                    {
                        socket.Close();
                    }

                    break;
                }
            }

            //클라이언트 연결이 끊겼거나 또는 예외가 발생했을 때.
            this.Stop();
        }

        public void SendMessage(string message)
        {
            int bytesSend = socket.Send(Encoding.UTF8.GetBytes(message));//UTF8->Bytes
        }

        //직접적으로 로그아웃또는 
        public void Stop()
        {
            stop = true;
            if(socket!=null)
            {
                socket.Close();
                socket.Dispose();
            }
            socket = null;

            //수신하는 쓰레드도 정지
            if(thread!=null)
            {
                thread.Abort();
            }
            thread = null;
        }
    }
    
}
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace MYTCP
{
    public partial class Form1 : Form
    {
        //클라이언트가 주기적으로 메시지를 보내기위해서
        //Timer
        //소스코드는 앞서저기가 
        bool timerStop = false;
        System.Timers.Timer timer = null;
        public delegate void AsncMethodCaller(Client cnt);
        //
        DateTime receivedSuccessTime = DateTime.Now.AddMilliseconds(10 * 1000);//현재시간+10초


        public Form1()
        {
            InitializeComponent();
        }

        Client client = null;

        private void button1_Click(object sender, EventArgs e)
        {
            Start();
        }

        private void Start()
        {
            if(client == null)
            {
                receivedSuccessTime = DateTime.Now.AddMilliseconds(10 * 1000);
                client = new Client(textBox1.Text, Int32.Parse(textBox2.Text));
                client.OnConnected += TcpConneted;
                client.OnDisConnected += TcpDisConnected;
                client.OnReceived += TcpReceived;
                client.Start();//서버와의 연결이 이뤄지게된다.

                //클라이언트가 주기적으로 메시지를 보내기위해서
                timerStop = false;
                timer = new System.Timers.Timer();
                timer.Interval = 10 * 1000;//테스트
                timer.Elapsed += new System.Timers.ElapsedEventHandler(timer_Elasped);
                timer.AutoReset = true;
                timer.Start();
            }
        }

        
        //주기적으로 요청
        private void timer_Elasped(object sender,System.Timers.ElapsedEventArgs e)
        {
            if(!timerStop)
            {
                //시간비교차를 이용해서 연결이 송수신을 통해서 정상적인..
                bool validConnection = ValidConnection(receivedSuccessTime, 15);
                if (validConnection&& client!=null)
                {
                    //주기적으로 메시지를 보내고
                    //비동기적으로
                    AsncMethodCaller asncMethodCaller = new AsncMethodCaller(DoPolling);
                    asncMethodCaller.BeginInvoke(client, null, null);
                    //보낸게 일정시간내에 도착을 하면... 연결이 정상이라고 판단을 할수있다.
                }
                else
                {
                    //연결이 비정상적이면
                    Stop();

                    //재연결을 하겠다?
                    Start();
                }

            }
        }

        //메시지를 실질적으로 보내게될 함수,
        private void DoPolling(Client cnt)
        {
            if(client!=null)
            {
                client.SendMessage("polling123");
            }
        }

        //15초내로 
        private bool ValidConnection(DateTime dt,int limitTime)
        {
            //마지막으로 수신한 시간과 현재시간차를 계산.
            int diffSeconds = Convert.ToInt32(DateTime.Now.Subtract(dt).TotalSeconds);//총시간중에 초단위로 시간차를
            if(diffSeconds> limitTime)
            {
                return false;
            }
            else
            {
                //폴링이라던지 기타데이터를 수신하게되면
                return true;
            }
        }

        private void TcpConneted(Socket s)
        {
            this.BeginInvoke(new Action(() =>
            {
                richTextBox1.Text += "[Local]서버접속 성공.\r\n";
            }));
        }

        private void TcpDisConnected(string disconnectMsg)
        {
            this.BeginInvoke(new Action(() =>
            {
                richTextBox1.Text += "[Local]서버접속 해제됨.\r\n";
            }));

            this.TimerStop();
            client = null;
        }

        private void TcpReceived(byte[] bytes)
        {
            receivedSuccessTime = DateTime.Now;//데이터를 수신한 시각
            this.BeginInvoke(new Action(() =>
            {
                richTextBox1.Text += "[Local]수신메시지-" + Encoding.UTF8.GetString(bytes) + ".\r\n";
            }));
        }

        private void button2_Click(object sender, EventArgs e)
        {
            Stop();
        }

        private void Form1_FormClosing(object sender, FormClosingEventArgs e)
        {
            Stop();
        }

        private void Stop()
        {
            this.TimerStop();
            if (client != null)
            {
                client.Stop();
            }
            client = null;
        }

        private void button3_Click(object sender, EventArgs e)
        {
            if(client!=null)
            {
                client.SendMessage(textBox3.Text);//클라이언트에서 서버로 메시지전송.
            }
        }

        //대문자소문자 섞어서 쓰는데.. 통일
        private void TimerStop()
        {
            timerStop = true;
            if(timer!=null)
            {
                timer.Elapsed-= new System.Timers.ElapsedEventHandler(timer_Elasped);
                timer.Stop();
                timer.Dispose();
                timer = null;
            }
        }
    }
}

 

 

Source : C#.Net 0.5년차~3년차(파트2)

https://www.inflearn.com/course/lecture?courseSlug=%EB%8B%B7%EB%84%B7-%EC%9C%88%ED%8F%BC-2&unitId=77904&tab=curriculum 

 

학습 페이지

 

www.inflearn.com

 

'C#' 카테고리의 다른 글

데이터 바인딩  (0) 2023.06.28
dictionary 설명과 dictionary을 활용한 2문제  (0) 2023.06.21
전기제어 PLC(Programmable Logic Controller)  (0) 2023.06.19
C# 파일과 디렉터리  (0) 2023.06.19
dynamic  (0) 2023.06.18

+ Recent posts