초기 커밋.
This commit is contained in:
503
ControlServer/Form1.cs
Normal file
503
ControlServer/Form1.cs
Normal file
@@ -0,0 +1,503 @@
|
||||
using System.Drawing.Drawing2D;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using System.Text;
|
||||
|
||||
namespace ControlServer
|
||||
{
|
||||
public partial class Form1 : Form
|
||||
{
|
||||
private List<TerminalInfo> terminalList = new List<TerminalInfo>(); // 리스트 생성
|
||||
private Dictionary<string, TcpClient> _connectedAgents = new Dictionary<string, TcpClient>();
|
||||
private TcpListener _listener;
|
||||
private bool _isRunning = false;
|
||||
private string _connectedIP = "";
|
||||
private System.Windows.Forms.Timer _statusTimer;
|
||||
private System.Windows.Forms.Timer _dataSyncTimer;
|
||||
|
||||
public Form1()
|
||||
{
|
||||
InitializeComponent();
|
||||
this.Load += Form1_Load;
|
||||
pannGray.BackColor = Color.Transparent; // 배경색 초기화
|
||||
pannBlue.BackColor = Color.Transparent; // 배경색 초기화
|
||||
|
||||
grdTotList.RowHeadersVisible = false; // 그리드 자동으로 생성되는 첫 번째 열 제거
|
||||
grdTotList.AllowUserToAddRows = false; // 새 행 입력용 기본 로우 제거
|
||||
grdTotList.EnableHeadersVisualStyles = false; // 헤더 기본 테마 사용 안 함
|
||||
grdTotList.ColumnHeadersDefaultCellStyle.BackColor = Color.LightGray; // 헤더 배경색
|
||||
grdTotList.ColumnHeadersHeight = 40; // 헤더 높이
|
||||
grdTotList.AllowUserToResizeRows = false; // 행 높이 조절 금지
|
||||
grdTotList.AllowUserToResizeColumns = false; // 열 너비 조절 금지
|
||||
grdTotList.ReadOnly = true; // 읽기 전용
|
||||
grdTotList.ClearSelection();
|
||||
|
||||
txtSearch.PlaceholderText = "검색(단말기명, IP)";
|
||||
}
|
||||
|
||||
// 가짜 데이터 로드용 함수 (테스트용 예시이며 필요 시 구현체 연결)
|
||||
private void LoadSavedTerminals()
|
||||
{
|
||||
lblTotCnt.Text = grdTotList.RowCount.ToString() + "대";
|
||||
}
|
||||
|
||||
// 폼이 화면에 나타날 때 서버를 별도의 통로(Task)로 실행
|
||||
private void Form1_Load(object sender, EventArgs e)
|
||||
{
|
||||
LoadSavedTerminals(); // 기존에 저장된 단말기 데이터 리스트 로드
|
||||
InitStatusTimer(); // 에이전트pc 상태 여부 확인
|
||||
|
||||
if (grdTotList.Rows.Count > 0)
|
||||
{
|
||||
// 등록된 단말기가 있다면 상태 체크 스레드 및 서버 리스너 시작
|
||||
_ = Task.Run(() => StartServer());
|
||||
_ = Task.Run(() => CheckAllAlliveStatus()); // 상시 또는 최초 상태 체크
|
||||
}
|
||||
else
|
||||
{
|
||||
// 등록된 단말기가 하나도 없다면 리스너를 구동하지 않음
|
||||
MessageBox.Show("등록된 단말기가 없어 서버를 대기 상태로 유지합니다. 단말기를 등록해주세요.");
|
||||
}
|
||||
}
|
||||
private void InitStatusTimer()
|
||||
{
|
||||
_statusTimer = new System.Windows.Forms.Timer();
|
||||
_statusTimer.Interval = 5000; // 5초마다 체크
|
||||
_statusTimer.Tick += (s, e) => {
|
||||
CheckAllAlliveStatus();
|
||||
};
|
||||
_statusTimer.Start();
|
||||
}
|
||||
|
||||
// 기존 등록된 단말기들의 현재 On/Off 상태를 체크하는 함수
|
||||
private void CheckAllAlliveStatus()
|
||||
{
|
||||
foreach (DataGridViewRow row in grdTotList.Rows)
|
||||
{
|
||||
if (row.IsNewRow) continue;
|
||||
string ip = row.Cells[2].Value?.ToString();
|
||||
if (string.IsNullOrEmpty(ip)) continue;
|
||||
|
||||
// 현재 서버에 소켓이 활성화되어 연결되어 있는지 확인
|
||||
bool isAlive = false;
|
||||
lock (_connectedAgents)
|
||||
{
|
||||
if (_connectedAgents.ContainsKey(ip))
|
||||
{
|
||||
TcpClient client = _connectedAgents[ip];
|
||||
// 소켓이 연결되어 있고, 실제 연결 상태가 양호한지 체크
|
||||
if (client != null && client.Connected)
|
||||
{
|
||||
isAlive = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 만약 소켓이 연결 안 되어 있다면 보조 수단으로 원래의 PingTest를 수행해볼 수 있음
|
||||
if (!isAlive)
|
||||
{
|
||||
isAlive = PingTest(ip);
|
||||
}
|
||||
|
||||
this.Invoke(new Action(() => {
|
||||
if (isAlive)
|
||||
{
|
||||
row.Cells[4].Value = "Start";
|
||||
row.Cells[4].Style.BackColor = Color.Green;
|
||||
row.Cells[4].Style.ForeColor = Color.White;
|
||||
}
|
||||
else
|
||||
{
|
||||
row.Cells[4].Value = "Stop";
|
||||
row.Cells[4].Style.BackColor = Color.Red;
|
||||
row.Cells[4].Style.ForeColor = Color.White;
|
||||
}
|
||||
row.Cells[4].Style.Alignment = DataGridViewContentAlignment.MiddleCenter;
|
||||
}));
|
||||
}
|
||||
this.Invoke(new Action(() => UpdateRunningCount()));
|
||||
}
|
||||
|
||||
private bool PingTest(string ip)
|
||||
{
|
||||
if (string.IsNullOrEmpty(ip)) return false;
|
||||
try
|
||||
{
|
||||
using (System.Net.NetworkInformation.Ping ping = new System.Net.NetworkInformation.Ping())
|
||||
{
|
||||
var reply = ping.Send(ip, 1000); // 1초 타임아웃
|
||||
return reply.Status == System.Net.NetworkInformation.IPStatus.Success;
|
||||
}
|
||||
}
|
||||
catch { return false; }
|
||||
}
|
||||
|
||||
// 서버 시작 함수
|
||||
private async Task StartServer()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (_isRunning) return; // 이미 실행 중이면 중복 방지
|
||||
|
||||
_listener = new TcpListener(IPAddress.Any, 9999);
|
||||
_listener.Start();
|
||||
MessageBox.Show("서버 연결 중...");
|
||||
_isRunning = true;
|
||||
|
||||
while (_isRunning)
|
||||
{
|
||||
// 에이전트의 접속을 비동기로 대기
|
||||
TcpClient client = await _listener.AcceptTcpClientAsync();
|
||||
|
||||
// 에이전트 접속 처리 핸들러 실행
|
||||
_ = Task.Run(() => HandleAgent(client));
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// 리스너가 중단되었을 때의 예외 처리 제외 처리
|
||||
if (_isRunning)
|
||||
{
|
||||
MessageBox.Show("서버 구동 오류: " + ex.Message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 에이전트에서 데이터 받기
|
||||
private void HandleAgent(TcpClient client)
|
||||
{
|
||||
try
|
||||
{
|
||||
string clientIP = ((IPEndPoint)client.Client.RemoteEndPoint).Address.ToString();
|
||||
|
||||
// using을 쓰지 않고 직접 스트림과 리더를 생성(함수가 끝나도 닫히지 않게)
|
||||
NetworkStream stream = client.GetStream();
|
||||
System.IO.StreamReader reader = new System.IO.StreamReader(stream, Encoding.UTF8);
|
||||
|
||||
// 에이전트가 보낸 첫 줄(MAC 주소)
|
||||
string receivedMac = reader.ReadLine();
|
||||
|
||||
if (!string.IsNullOrEmpty(receivedMac))
|
||||
{
|
||||
this.Invoke(new Action(() => {
|
||||
// 기존 연결이 있다면 안전하게 닫고 교체
|
||||
if (_connectedAgents.ContainsKey(clientIP))
|
||||
{
|
||||
try { _connectedAgents[clientIP].Close(); } catch { }
|
||||
}
|
||||
|
||||
_connectedAgents[clientIP] = client; // 소켓 유지
|
||||
UpdateAgentStatusByMac(receivedMac, clientIP);
|
||||
}));
|
||||
|
||||
// 서버 소켓이 닫히거나 에이전트가 끊어질 때까지
|
||||
// 이 스레드가 종료되지 않고 백그라운드에서 계속 대기하도록 루프를 돌려줌
|
||||
// (에이전트로부터 추가적인 메시지를 받을 때도 여기서 처리 가능)
|
||||
while (client.Connected)
|
||||
{
|
||||
// 에이전트가 보낸 데이터가 없다면 여기서 대기
|
||||
// 에이전트가 연결을 끊으면 null이 들어와 루프를 빠져나감
|
||||
string agentMsg = reader.ReadLine();
|
||||
if (agentMsg == null) break;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine("서버 핸들러 오류: " + ex.Message);
|
||||
}
|
||||
finally
|
||||
{
|
||||
// 연결이 최종적으로 끊기면 딕셔너리에서 제거 및 리소스 정리
|
||||
try
|
||||
{
|
||||
string clientIP = ((IPEndPoint)client.Client.RemoteEndPoint).Address.ToString();
|
||||
lock (_connectedAgents)
|
||||
{
|
||||
if (_connectedAgents.ContainsKey(clientIP))
|
||||
{
|
||||
_connectedAgents.Remove(clientIP);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
client.Close();
|
||||
}
|
||||
}
|
||||
|
||||
// MAC 주소를 기반으로 에이전트 매칭 및 상태 업데이트
|
||||
private void UpdateAgentStatusByMac(string clientMac, string clientIP)
|
||||
{
|
||||
// 수신된 MAC에서 불필요한 문자 제거
|
||||
string cleanClientMac = clientMac.Replace("-", "").Replace(":", "").Replace(" ", "").ToUpper().Trim();
|
||||
|
||||
bool isMatched = false;
|
||||
|
||||
this.Invoke(new Action(() => {
|
||||
foreach (DataGridViewRow row in grdTotList.Rows)
|
||||
{
|
||||
string gridIp = row.Cells[2].Value.ToString();
|
||||
|
||||
if (gridIp == clientIP)
|
||||
{
|
||||
row.Cells[1].Value = cleanClientMac;
|
||||
row.Cells[4].Value = "Start";
|
||||
row.Cells[4].Style.BackColor = Color.Green;
|
||||
row.Cells[4].Style.ForeColor = Color.White;
|
||||
row.Cells[5].Value = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
|
||||
|
||||
isMatched = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!isMatched)
|
||||
{
|
||||
Console.WriteLine($"매칭 실패! 수신된 MAC: {cleanClientMac}");
|
||||
}
|
||||
|
||||
UpdateRunningCount();
|
||||
}));
|
||||
}
|
||||
|
||||
// 상단의 프로그램 실행 대수 갱신용 공통 메서드
|
||||
private void UpdateRunningCount()
|
||||
{
|
||||
int startCount = 0;
|
||||
foreach (DataGridViewRow row in grdTotList.Rows)
|
||||
{
|
||||
if (row.IsNewRow) continue;
|
||||
if (row.Cells[4].Value?.ToString() == "Start")
|
||||
{
|
||||
startCount++;
|
||||
}
|
||||
}
|
||||
lblRunningCnt.Text = startCount.ToString() + "대";
|
||||
}
|
||||
|
||||
private void btnRegister_MouseClick(object sender, MouseEventArgs e)
|
||||
{
|
||||
// 단말기 등록 팝업 호출
|
||||
Form2 form = new Form2();
|
||||
|
||||
if (form.ShowDialog() == DialogResult.OK)
|
||||
{
|
||||
TerminalInfo data = form.TerminalData;
|
||||
|
||||
grdTotList.Rows.Add(
|
||||
data.TerminalName, // 단말기명
|
||||
data.Mac, // MAC 주소
|
||||
data.IP, // IP
|
||||
"설정", // 통신설정 버튼
|
||||
"Stop", // 기본 상태
|
||||
DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"),
|
||||
data.Port // 포트번호
|
||||
);
|
||||
|
||||
int rowIndex = grdTotList.Rows.Count - 1;
|
||||
|
||||
// 프로그램 열 스타일 지정
|
||||
DataGridViewCell cell4 = grdTotList.Rows[rowIndex].Cells[4];
|
||||
cell4.Style.BackColor = Color.Red;
|
||||
cell4.Style.ForeColor = Color.White;
|
||||
cell4.Style.Alignment = DataGridViewContentAlignment.MiddleCenter;
|
||||
|
||||
// 통신설정 열 스타일 지정
|
||||
DataGridViewCell cell3 = grdTotList.Rows[rowIndex].Cells[3];
|
||||
cell3.Style.BackColor = Color.LightGray;
|
||||
cell3.Style.ForeColor = Color.Black;
|
||||
cell3.Style.Alignment = DataGridViewContentAlignment.MiddleCenter;
|
||||
|
||||
// 현재 단말기 개수 업데이트
|
||||
lblTotCnt.Text = grdTotList.RowCount.ToString() + "대";
|
||||
|
||||
// 단말 등록 완료 후 리스너가 구동 중이 아니었다면 리스너 최초 구동 개시
|
||||
if (!_isRunning)
|
||||
{
|
||||
_ = Task.Run(() => StartServer());
|
||||
}
|
||||
|
||||
// 기접속된 IP가 있는 장치라면 바로 연동 매칭
|
||||
if (!string.IsNullOrEmpty(_connectedIP) && _connectedIP == data.IP)
|
||||
{
|
||||
UpdateAgentStatusByMac(data.Mac, data.IP);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void btnDataUpdate_MouseClick(object sender, MouseEventArgs e)
|
||||
{
|
||||
// 1. 메시지 전송
|
||||
SendDataToAllAgents("데이터 전송 테스트(수동)");
|
||||
|
||||
// 2. 기존 로직
|
||||
Popup popup = new Popup("전체 단말기에 데이터 전송을 시도했습니다.");
|
||||
popup.ShowDialog();
|
||||
CheckAllAlliveStatus();
|
||||
}
|
||||
|
||||
private void btnLog_MouseClick(object sender, MouseEventArgs e)
|
||||
{
|
||||
Form4 form = new Form4();
|
||||
form.Show();
|
||||
}
|
||||
|
||||
private void grdTotList_CellMouseClick(object sender, DataGridViewCellMouseEventArgs e)
|
||||
{
|
||||
grdTotList.ClearSelection();
|
||||
|
||||
// 헤더 클릭 방지
|
||||
if (e.RowIndex < 0) return;
|
||||
|
||||
// 통신설정 버튼 열 클릭 시
|
||||
if (e.ColumnIndex == 3)
|
||||
{
|
||||
var row = grdTotList.Rows[e.RowIndex];
|
||||
string TerminalName = row.Cells[0].Value?.ToString() ?? "";
|
||||
string TerminalIp = row.Cells[2].Value?.ToString() ?? "";
|
||||
|
||||
Form3 form = new Form3(TerminalName, TerminalIp);
|
||||
form.ShowDialog();
|
||||
}
|
||||
// 프로그램 작동 제어 제어 열 클릭 시
|
||||
else if (e.ColumnIndex == 4)
|
||||
{
|
||||
DataGridViewRow row = grdTotList.Rows[e.RowIndex];
|
||||
DataGridViewCell cell4 = row.Cells[4];
|
||||
|
||||
string status = cell4.Value?.ToString() ?? "";
|
||||
string targetIP = row.Cells[2].Value?.ToString() ?? "";
|
||||
string targetMac = row.Cells[1].Value?.ToString() ?? "";
|
||||
|
||||
if (status == "Start")
|
||||
{
|
||||
// 에이전트 PC로 원격 강제 종료 패킷 전달
|
||||
SendStopCommandToAgent(targetIP);
|
||||
|
||||
cell4.Value = "Stop";
|
||||
cell4.Style.BackColor = Color.Red;
|
||||
cell4.Style.ForeColor = Color.White;
|
||||
}
|
||||
else
|
||||
{
|
||||
// WOL 매직 패킷 송신
|
||||
if (!string.IsNullOrEmpty(targetMac))
|
||||
{
|
||||
SendWakeOnLan(targetMac);
|
||||
|
||||
cell4.Value = "Start";
|
||||
cell4.Style.BackColor = Color.Green;
|
||||
cell4.Style.ForeColor = Color.White;
|
||||
}
|
||||
else
|
||||
{
|
||||
MessageBox.Show("등록된 MAC 주소가 없습니다.");
|
||||
}
|
||||
}
|
||||
|
||||
cell4.Style.Alignment = DataGridViewContentAlignment.MiddleCenter;
|
||||
UpdateRunningCount();
|
||||
}
|
||||
}
|
||||
|
||||
// [원격 끄기 구현] 소켓 스트림 연동을 위한 에이전트 대상 종료 전송 함수
|
||||
private void SendStopCommandToAgent(string ip)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (_connectedAgents.ContainsKey(ip))
|
||||
{
|
||||
TcpClient client = _connectedAgents[ip];
|
||||
if (client.Connected)
|
||||
{
|
||||
// 중요: 뒤에 "\n"을 추가하여 에이전트의 ReadLine()이 끝을 인식하게 합니다.
|
||||
byte[] msg = Encoding.UTF8.GetBytes("STOP\n");
|
||||
client.GetStream().Write(msg, 0, msg.Length);
|
||||
client.GetStream().Flush();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
MessageBox.Show("에이전트가 서버에 연결되어 있지 않습니다.");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
MessageBox.Show("종료 명령 전송 실패: " + ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
// WOL 매직 패킷 전송 함수
|
||||
private void SendWakeOnLan(string macAddress)
|
||||
{
|
||||
try
|
||||
{
|
||||
string cleanMac = macAddress.Replace("-", "").Replace(":", "").Replace(" ", "").Trim();
|
||||
|
||||
if (cleanMac.Length != 12)
|
||||
{
|
||||
MessageBox.Show("올바른 MAC 주소 형식이 아닙니다.");
|
||||
return;
|
||||
}
|
||||
|
||||
byte[] bytes = new byte[102];
|
||||
|
||||
for (int i = 0; i < 6; i++)
|
||||
{
|
||||
bytes[i] = 0xFF;
|
||||
}
|
||||
|
||||
byte[] macBytes = new byte[6];
|
||||
for (int i = 0; i < 6; i++)
|
||||
{
|
||||
macBytes[i] = Convert.ToByte(cleanMac.Substring(i * 2, 2), 16);
|
||||
}
|
||||
|
||||
for (int i = 1; i <= 16; i++)
|
||||
{
|
||||
Array.Copy(macBytes, 0, bytes, i * 6, 6);
|
||||
}
|
||||
|
||||
using (UdpClient client = new UdpClient())
|
||||
{
|
||||
client.Connect(IPAddress.Parse("255.255.255.255"), 9);
|
||||
client.Send(bytes, bytes.Length);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
MessageBox.Show("WOL 전송 실패: " + ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
private void SendDataToAllAgents(string message)
|
||||
{
|
||||
// 메시지 끝에 \n을 붙여서 에이전트가 ReadLine()으로 잘 읽게함
|
||||
byte[] data = Encoding.UTF8.GetBytes(message + "\n");
|
||||
|
||||
lock (_connectedAgents) // 여러 스레드에서 접근할 수 있으므로 lock 사용
|
||||
{
|
||||
foreach (var kvp in _connectedAgents)
|
||||
{
|
||||
try
|
||||
{
|
||||
TcpClient client = kvp.Value;
|
||||
if (client != null && client.Connected)
|
||||
{
|
||||
NetworkStream stream = client.GetStream();
|
||||
stream.Write(data, 0, data.Length);
|
||||
stream.Flush();
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"전송 실패 ({kvp.Key}): {ex.Message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void pannGray_Paint(object sender, PaintEventArgs e) { }
|
||||
private void pannBlue_Paint(object sender, PaintEventArgs e) { }
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user