일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | ||
6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | 30 |
- 집 제어
- 캐드 꿀팁
- C#
- mysql
- 날씨 프로그램
- string길이
- 날씨 및 온도 제어
- 파이썬
- 리눅스
- 설계
- todolist 응용어플리케이션
- PYTHON
- 지각하면 안됨
- 기상청 api
- 한글 표시 안됨
- todolist앱
- string 초기화
- toy_project
- 센서
- 근태 자동
- 할일이 많다
- string 한계
- 라즈베리파이
- tcp클라이언트
- EFECS
- 마리아 db
- maria db
- 스마트팜
- 토이 프로젝트
- 토이프로젝트
- Today
- Total
경험을 통해서
[C#] 클라이언트 재연결 및 수신 프로그램 만들기 본문
델파이의 소켓 컴포넌트를 경험하고 난 후 그래도 c#에서도 그런 컴포넌트를 찾기보다
한번 해당 코드를 실제 업무에서도 적용 시켜보고 싶어서 작성 해봤다
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Net;
using System.Net.Sockets;
using System.Threading;
namespace TCP_model
{
public partial class Form1 : Form
{
Socket fSocket;
Thread fRec_thread;
Thread fReconnection;
IPAddress fiA;
IPEndPoint fEP;
private bool fconnection;
private int fCon_Err = 0; //연결이 물리적으로 끊겼을 경우 Receive 함수의 반환 값이 0이 되는 것을 확인하여 2번이상 0이 반환되면 일방적으로 끊는다
private string fmsg="";
private int [] fCnt = new int[3];
Form2 fFormshow;
TCP_Class clTCP;
/// <summary>
/// g가 붙은 변수는 외부 전역 변수
/// f가 붙은 변수는 내부 전역 변수
/// s가 붙은 변수는 지역 변수
/// c는 조건 반복등에서만 쓰이는 변수 - foreach 문 안에 주로 쓰임
/// ps 반환 값이 없는 외부 함수
/// pf 반환 값이 있는 내부 함수
/// gs 반환 값이 없는 외부 함수
/// gf 반환 값이 있는 내부 함수
/// cl은 사용자 클래스 인스턴스
///
/// </summary>
public Form1()
{
InitializeComponent();
fFormshow = new Form2();
clTCP = new TCP_Class();
}
private void button1_Click(object sender, EventArgs e)
{
if (!fconnection)
{
fRec_thread = null;
fconnection = true;
fRec_thread = new Thread(new ThreadStart(psReceive));
fRec_thread.IsBackground = true;
fRec_thread.Start();
}
}
private void btn_close_Click(object sender, EventArgs e)
{
fSocket.Close();
fconnection = false;
}
private void Form1_Load(object sender, EventArgs e)
{
//===============기존의 TCP의 값들을 설정하여 둔다============//
fiA = IPAddress.Parse("127.0.0.1");
fEP = new IPEndPoint(fiA, 101);
//===============기존의 TCP의 값들을 설정하여 둔다============//
fReconnection = new Thread(new ThreadStart(psReconnection));
fReconnection.IsBackground = true;
fReconnection.Start();
}
private void psReconnection()
{
while (true)
{
if (!fconnection)
{
fCnt[1]++;
try
{
listBox1.Invoke(new MethodInvoker(delegate
{
listBox1.Items.Add(string.Format("{0} Connecting...", fCnt[1]));
}));
fSocket = null;
fSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
fSocket.Connect(fEP);
fconnection = true;
}
catch (Exception)
{
fSocket = null;
}
if (fconnection)
{
listBox1.Invoke(new MethodInvoker(delegate
{
listBox1.Items.Add(string.Format("Connected"));
}));
fCnt[1] = 0;
fRec_thread = null;
fRec_thread = new Thread(new ThreadStart(psReceive));
fRec_thread.IsBackground = true;
fRec_thread.Start();
}
}
Thread.Sleep(1000);
}
}
private void psReceive()
{
while (fconnection)
{
byte[] receiveBuffer = new byte[512];
int length = 0;
try
{
length = fSocket.Receive(receiveBuffer, receiveBuffer.Length, SocketFlags.None);
}
catch (Exception)
{
fconnection = false;
}
if (length != 0) // 반환 되는 값이 0일 경우는 경험 상 물리적인 이더넷 통신이 끊겼을 경우
{
fCon_Err = 0;
fmsg =fmsg + Encoding.UTF8.GetString(receiveBuffer, 0,
length);
char sSTX_ASCII = Convert.ToChar(0x02);
string[] sPacket = fmsg.Split(sSTX_ASCII);
fmsg = "";
foreach (string cPkt in sPacket)
{
switch (clTCP.gfConfirm_packet(cPkt, 0x03))
{
case 0:
//크로스 스레드 오류로 대리자 함수로 listbox와 richText에 각각 즉각 추가 함수//
listBox1.Invoke(new MethodInvoker(delegate
{
listBox1.Items.Add(string.Format("({0}) : Message : {1}", DateTime.Now.ToString(), cPkt));
}));
fmsg += cPkt;
break;
default:
richTextBox1.Invoke(new MethodInvoker(delegate
{
richTextBox1.AppendText(string.Format("({0}) : Message : {1}\n", DateTime.Now.ToString(), cPkt));
}));
break;
}
}
}
else
{
fCon_Err++;
fconnection = fCon_Err % 20 == 0 ? false : true;
}
Thread.Sleep(10);
}
fSocket.Close();
// 재연결된 값을 표현하기 위해 배열로 선언하여 연결 해제 시 카운트가 올라간다
fCnt[0]++;
fconnection = false;
}
private void btn_confirm_Click(object sender, EventArgs e)
{
if (fiA != null) pan_fIA.BackColor = Color.Green;
else pan_fIA.BackColor = Color.Red;
if (fEP != null) pan_fIE.BackColor = Color.Green;
else pan_fIE.BackColor = Color.Red;
}
private void timer1_Tick(object sender, EventArgs e)
{
label1.Text = fCnt[0].ToString();//psReceive thread
label2.Text = fCnt[1].ToString();//psReconnection thread
if (fmsg != "")
{
listBox1.Items.Add(string.Format("({0}) : Message : {1}", DateTime.Now.ToString(), fmsg));
fmsg = "";
}
}
private void pan_fIE_Paint(object sender, PaintEventArgs e)
{
}
private void pan_fIE_Click(object sender, EventArgs e)
{
if (!fFormshow.Visible)
{
fFormshow.Visible = true;
}
}
}
}
psReceive와 psReconnection의 함수를 자세히 보면 된다
-psReconnection
private void psReconnection()
{
while (true)
{
if (!fconnection)
{
fCnt[1]++;
try
{
listBox1.Invoke(new MethodInvoker(delegate
{
listBox1.Items.Add(string.Format("{0} Connecting...", fCnt[1]));
}));
fSocket = null;
fSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
fSocket.Connect(fEP);
fconnection = true;
}
catch (Exception)
{
fSocket = null;
}
if (fconnection)
{
listBox1.Invoke(new MethodInvoker(delegate
{
listBox1.Items.Add(string.Format("Connected"));
}));
fCnt[1] = 0;
fRec_thread = null;
fRec_thread = new Thread(new ThreadStart(psReceive));
fRec_thread.IsBackground = true;
fRec_thread.Start();
}
}
Thread.Sleep(1000);
}
}
2개의 thread를 컨트롤 하는 fconnection 불 값이 소켓 통신이 끊어질 경우 false 값으로 변경되면서 재 연결을 시도하도록 한다(여기서 reconnection 스레드는 프로그램이 살아 있는 한 멈추지 않는다.)
기존에 설정이 되어 있던 IP 주소와 포트의 번호는 초기화되지 않았기 때문에 다시 사용이 가능하다(fiA와 fEP)
try문에서 연결이 되지 않을 경우 fConnection의 값은 변하지 않고 except으로 넘어간다
-psReceive
private void psReceive()
{
while (fconnection)
{
byte[] receiveBuffer = new byte[512];
int length = 0;
try
{
length = fSocket.Receive(receiveBuffer, receiveBuffer.Length, SocketFlags.None);
}
catch (Exception)
{
fconnection = false;
}
if (length != 0) // 반환 되는 값이 0일 경우는 경험 상 물리적인 이더넷 통신이 끊겼을 경우
{
fCon_Err = 0;
fmsg =fmsg + Encoding.UTF8.GetString(receiveBuffer, 0,
length);
char sSTX_ASCII = Convert.ToChar(0x02);
string[] sPacket = fmsg.Split(sSTX_ASCII);
fmsg = "";
foreach (string cPkt in sPacket)
{
switch (clTCP.gfConfirm_packet(cPkt, 0x03))
{
case 0:
//크로스 스레드 오류로 대리자 함수로 listbox와 richText에 각각 즉각 추가 함수//
listBox1.Invoke(new MethodInvoker(delegate
{
listBox1.Items.Add(string.Format("({0}) : Message : {1}", DateTime.Now.ToString(), cPkt));
}));
fmsg += cPkt;
break;
default:
richTextBox1.Invoke(new MethodInvoker(delegate
{
richTextBox1.AppendText(string.Format("({0}) : Message : {1}\n", DateTime.Now.ToString(), cPkt));
}));
break;
}
}
}
else
{
fCon_Err++;
fconnection = fCon_Err % 20 == 0 ? false : true;
}
Thread.Sleep(10);
}
fSocket.Close();
// 재연결된 값을 표현하기 위해 배열로 선언하여 연결 해제 시 카운트가 올라간다
fCnt[0]++;
fconnection = false;
}
Thread는 while 문을 벗어나면 소멸된다.
*Receive 함수를 잠깐 살펴보자~
public int Receive(byte[] buffer, int offset, int size, SocketFlags socketFlags)
{
SocketError errorCode;
int result = Receive(buffer, offset, size, socketFlags, out errorCode);
if (errorCode != 0)
{
throw new SocketException(errorCode);
}
return result;
}
위의 Receive의 인자 값을 보면 out errorCode 값이 있다.
이 값으로 한쪽의 연결 끊어짐을 알 수 있을 거라 생각 했지만 한번 연결 된 후 서버 쪽에서 연결이 끊기면 에러코드 값이 반환이 되지 않아 다른 방법을 선택해야 한다.
그래서 택한 값은 패킷이 수신 될 경우 길이 값(length)이 절대로 0이 될 수 없기에
fconnection = fCon_Err % 20 == 0 ? false : true;
위의 연산 값에서 일정 횟수가 오버 되면 fconnection을 false로 변환하면서 Receive 스레드를 종료 시킨다
솔직히 잘 짠 코드도 아니고 그냥 해당 로직에 맞게 스레드를 실행하고 소멸 시키고 하는 거 밖에는 없다
지금은 많이 개선해서 오류가 없기는 하지만 역시 아직 많이 부족한 부분이 있다 ㅎㅎ
'Coding Diary > C,C++,C#' 카테고리의 다른 글
Thread 파라미터 전달하기(왜 이걸 이제 알았을까...) (0) | 2024.08.29 |
---|---|
스레드 간섭 간 생긴 일 1편 (1) | 2024.08.20 |
TCP 통신의 패킷 처리하기(클라이언트 편) (0) | 2023.05.20 |
Thread 작성 시 고려 사항 및 오류 사항 : 스레드가 실행 중이거나 종료되었습니다. 다시 시작할 수 없습니다. (0) | 2023.05.20 |
Smart Farm(스마트 팜)2 (0) | 2022.04.30 |