Files
Groupware/SubProject/ChatServer/TcpChatServer.cs
2025-11-12 13:57:13 +09:00

422 lines
14 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Threading;
namespace NoticeServer
{
/// <summary>
/// TCP 채팅 서버
/// </summary>
public class TcpChatServer
{
private TcpListener tcpListener;
private Thread acceptThread;
private bool isRunning;
private int tcpPort;
private int maxClients;
private readonly object clientsLock = new object();
private List<ChatClient> clients = new List<ChatClient>();
public TcpChatServer(int tcpPort, int maxClients = 100)
{
this.tcpPort = tcpPort;
this.maxClients = maxClients;
}
/// <summary>
/// TCP 채팅 서버 시작
/// </summary>
public void Start()
{
if (isRunning)
return;
try
{
tcpListener = new TcpListener(IPAddress.Any, tcpPort);
tcpListener.Start();
isRunning = true;
acceptThread = new Thread(AcceptLoop)
{
IsBackground = true,
Name = "TcpAcceptThread"
};
acceptThread.Start();
Console.WriteLine($"[TCP] Chat server started (Port: {tcpPort})");
Console.WriteLine($"[TCP] Max clients: {maxClients}");
}
catch (Exception ex)
{
Console.WriteLine($"[ERROR] Failed to start TCP server: {ex.Message}");
isRunning = false;
}
}
/// <summary>
/// 클라이언트 연결 수락 루프
/// </summary>
private void AcceptLoop()
{
while (isRunning)
{
try
{
TcpClient tcpClient = tcpListener.AcceptTcpClient();
lock (clientsLock)
{
if (clients.Count >= maxClients)
{
Console.WriteLine("[REJECTED] Maximum clients exceeded");
tcpClient.Close();
continue;
}
}
var client = new ChatClient(tcpClient);
client.MessageReceived += OnClientMessageReceived;
client.Disconnected += OnClientDisconnected;
lock (clientsLock)
{
clients.Add(client);
}
Console.WriteLine($"[CONNECTED] New client: {client.IpAddress} (ID: {client.ClientId})");
}
catch (SocketException)
{
// Server shutdown
break;
}
catch (Exception ex)
{
Console.WriteLine($"[ERROR] Client accept error: {ex.Message}");
}
}
}
/// <summary>
/// 클라이언트 메시지 수신 이벤트 핸들러
/// </summary>
private void OnClientMessageReceived(object sender, ChatMessage message)
{
var client = sender as ChatClient;
if (client == null)
return;
Console.WriteLine($"[RECEIVED] {client.NickName ?? client.IpAddress}: {message.Type} - {message.Content}");
switch (message.Type)
{
case MessageType.Connect:
HandleConnect(client, message);
break;
case MessageType.Chat:
HandleChat(client, message);
break;
case MessageType.Whisper:
HandleWhisper(client, message);
break;
case MessageType.UserListRequest:
HandleUserListRequest(client);
break;
case MessageType.Ping:
HandlePing(client);
break;
case MessageType.Disconnect:
client.Disconnect();
break;
}
}
/// <summary>
/// 연결 처리
/// </summary>
private void HandleConnect(ChatClient client, ChatMessage message)
{
// Check employee ID duplication
lock (clientsLock)
{
if (!string.IsNullOrEmpty(message.EmployeeId) &&
clients.Any(c => c.ClientId != client.ClientId && c.EmployeeId == message.EmployeeId))
{
var errorMsg = new ChatMessage
{
Type = MessageType.Notice,
Content = "Employee ID already connected.",
Timestamp = DateTime.Now
};
client.SendMessage(errorMsg);
client.Disconnect();
return;
}
}
Console.WriteLine($"[LOGIN] {message.NickName} ({message.EmployeeId}, {message.UserGroup}) from {client.IpAddress}, {message.HostName}");
// Welcome message
var welcomeMsg = new ChatMessage
{
Type = MessageType.Notice,
Content = $"Welcome to the chat server, {message.NickName}!",
Timestamp = DateTime.Now
};
client.SendMessage(welcomeMsg);
// Notify other users
var noticeMsg = new ChatMessage
{
Type = MessageType.Notice,
Content = $"{message.NickName} ({message.EmployeeId}) has joined the chat.",
Timestamp = DateTime.Now
};
BroadcastMessage(noticeMsg, client.ClientId);
}
/// <summary>
/// 채팅 메시지 처리 (1:1 messaging)
/// </summary>
private void HandleChat(ChatClient client, ChatMessage message)
{
// Set sender information
message.NickName = client.NickName;
message.EmployeeId = client.EmployeeId;
message.UserGroup = client.UserGroup;
message.IpAddress = client.IpAddress;
message.HostName = client.HostName;
message.Timestamp = DateTime.Now;
// Send to target recipient (1:1 messaging)
if (!string.IsNullOrEmpty(message.TargetEmployeeId))
{
lock (clientsLock)
{
// Find target by employee ID
var targetClient = clients.FirstOrDefault(c => c.EmployeeId == message.TargetEmployeeId);
if (targetClient != null)
{
// Send to target
targetClient.SendMessage(message);
// Echo back to sender
client.SendMessage(message);
Console.WriteLine($"[CHAT] {client.EmployeeId} -> {message.TargetEmployeeId}: {message.Content}");
}
else
{
var errorMsg = new ChatMessage
{
Type = MessageType.Notice,
Content = $"User '{message.TargetEmployeeId}' not found or offline.",
Timestamp = DateTime.Now
};
client.SendMessage(errorMsg);
}
}
}
else
{
// No target specified, send error
var errorMsg = new ChatMessage
{
Type = MessageType.Notice,
Content = "Please select a recipient.",
Timestamp = DateTime.Now
};
client.SendMessage(errorMsg);
}
}
/// <summary>
/// Whisper message handler
/// </summary>
private void HandleWhisper(ChatClient client, ChatMessage message)
{
message.NickName = client.NickName;
message.IpAddress = client.IpAddress;
message.Timestamp = DateTime.Now;
lock (clientsLock)
{
var targetClient = clients.FirstOrDefault(c => c.NickName == message.TargetNickName);
if (targetClient != null)
{
targetClient.SendMessage(message);
Console.WriteLine($"[WHISPER] {client.NickName} -> {message.TargetNickName}: {message.Content}");
}
else
{
var errorMsg = new ChatMessage
{
Type = MessageType.Notice,
Content = $"User '{message.TargetNickName}' not found.",
Timestamp = DateTime.Now
};
client.SendMessage(errorMsg);
}
}
}
/// <summary>
/// 사용자 목록 요청 처리
/// </summary>
private void HandleUserListRequest(ChatClient client)
{
lock (clientsLock)
{
// Format: "employeeId1:nickName1:userGroup1,employeeId2:nickName2:userGroup2,..."
// Filter: dev can see all, others can only see same group
var filteredClients = clients
.Where(c => !string.IsNullOrEmpty(c.NickName) && !string.IsNullOrEmpty(c.EmployeeId))
.Where(c => client.UserGroup == "dev" || c.UserGroup == client.UserGroup);
var userListStr = string.Join(",", filteredClients
.Select(c => $"{c.EmployeeId}:{c.NickName}:{c.UserGroup ?? "default"}"));
var response = new ChatMessage
{
Type = MessageType.UserListResponse,
Content = userListStr,
Timestamp = DateTime.Now
};
client.SendMessage(response);
Console.WriteLine($"[USER_LIST] Sent to {client.EmployeeId} ({client.UserGroup}): {userListStr}");
}
}
/// <summary>
/// Ping 처리
/// </summary>
private void HandlePing(ChatClient client)
{
var pongMsg = new ChatMessage
{
Type = MessageType.Pong,
Timestamp = DateTime.Now
};
client.SendMessage(pongMsg);
}
/// <summary>
/// 클라이언트 연결 해제 이벤트 핸들러
/// </summary>
private void OnClientDisconnected(object sender, string clientId)
{
ChatClient disconnectedClient = null;
lock (clientsLock)
{
disconnectedClient = clients.FirstOrDefault(c => c.ClientId == clientId);
if (disconnectedClient != null)
{
clients.Remove(disconnectedClient);
}
}
if (disconnectedClient != null)
{
Console.WriteLine($"[DISCONNECTED] {disconnectedClient.NickName ?? disconnectedClient.IpAddress} (ID: {clientId})");
if (!string.IsNullOrEmpty(disconnectedClient.NickName))
{
var noticeMsg = new ChatMessage
{
Type = MessageType.Notice,
Content = $"{disconnectedClient.NickName} has left the chat.",
Timestamp = DateTime.Now
};
BroadcastMessage(noticeMsg);
}
}
}
/// <summary>
/// 모든 클라이언트에게 메시지 전송
/// </summary>
private void BroadcastMessage(ChatMessage message, string excludeClientId = null)
{
lock (clientsLock)
{
foreach (var client in clients.ToList())
{
if (excludeClientId != null && client.ClientId == excludeClientId)
continue;
if (!string.IsNullOrEmpty(client.NickName))
{
client.SendMessage(message);
}
}
}
}
/// <summary>
/// 서버 상태 출력
/// </summary>
public void PrintStatus()
{
Console.WriteLine("\n========================================");
Console.WriteLine($"Current Clients: {clients.Count}/{maxClients}");
Console.WriteLine("========================================");
lock (clientsLock)
{
foreach (var client in clients)
{
if (!string.IsNullOrEmpty(client.NickName))
{
Console.WriteLine($"- {client.NickName} ({client.EmployeeId}, {client.UserGroup})");
Console.WriteLine($" IP: {client.IpAddress}, Host: {client.HostName}");
Console.WriteLine($" Connected: {client.ConnectedTime:yyyy-MM-dd HH:mm:ss}");
Console.WriteLine($" Last Activity: {client.LastActivity:HH:mm:ss}");
}
}
}
Console.WriteLine("========================================\n");
}
/// <summary>
/// TCP 서버 중지
/// </summary>
public void Stop()
{
if (!isRunning)
return;
isRunning = false;
// Disconnect all clients
lock (clientsLock)
{
foreach (var client in clients.ToList())
{
client.Disconnect();
}
clients.Clear();
}
try
{
tcpListener?.Stop();
}
catch { }
Console.WriteLine("[TCP] Chat server stopped");
}
}
}