C# 即时通讯工具实现

C#即时通讯工具,包含客户端和服务器端,支持TCP/UDP双协议、群聊、私聊、文件传输等功能。

项目结构

InstantMessenger/
├── Server/
│   ├── Program.cs (服务器主程序)
│   ├── ServerCore.cs (服务器核心逻辑)
│   └── Models/ (数据模型)
├── Client/
│   ├── Program.cs (客户端主程序)
│   ├── ClientCore.cs (客户端核心逻辑)
│   └── UI/ (用户界面)
└── Shared/
    └── Protocol.cs (通信协议定义)

1. 共享协议定义 (Shared/Protocol.cs)

using System;
using System.Collections.Generic;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.Text;

namespace InstantMessenger.Shared
{
    // 消息类型枚举
    public enum MessageType
    {
        Login,              // 登录
        Logout,             // 登出
        TextMessage,        // 文本消息
        FileMessage,        // 文件消息
        UserList,           // 用户列表
        UserStatus,         // 用户状态
        PrivateMessage,     // 私聊消息
        GroupMessage,       // 群聊消息
        VoiceMessage,       // 语音消息
        VideoMessage,       // 视频消息
        Error,              // 错误
        Heartbeat,          // 心跳
        Ack                 // 确认
    }

    // 用户状态
    public enum UserStatus
    {
        Offline,
        Online,
        Away,
        Busy,
        Invisible
    }

    // 基础消息类
    [Serializable]
    public class Message
    {
        public MessageType Type { get; set; }
        public string SenderId { get; set; }
        public string SenderName { get; set; }
        public string ReceiverId { get; set; }
        public string ReceiverName { get; set; }
        public DateTime Timestamp { get; set; }
        public byte[] Data { get; set; }
        public string Content { get; set; }
        public Dictionary<string, object> Metadata { get; set; }

        public Message()
        {
            Timestamp = DateTime.Now;
            Metadata = new Dictionary<string, object>();
        }

        // 序列化消息
        public byte[] Serialize()
        {
            BinaryFormatter formatter = new BinaryFormatter();
            using (var stream = new System.IO.MemoryStream())
            {
                formatter.Serialize(stream, this);
                return stream.ToArray();
            }
        }

        // 反序列化消息
        public static Message Deserialize(byte[] data)
        {
            BinaryFormatter formatter = new BinaryFormatter();
            using (var stream = new System.IO.MemoryStream(data))
            {
                return (Message)formatter.Deserialize(stream);
            }
        }
    }

    // 用户信息
    [Serializable]
    public class UserInfo
    {
        public string UserId { get; set; }
        public string UserName { get; set; }
        public UserStatus Status { get; set; }
        public string IPAddress { get; set; }
        public int Port { get; set; }
        public DateTime LastActive { get; set; }
        public List<string> Groups { get; set; }

        public UserInfo()
        {
            Groups = new List<string>();
            LastActive = DateTime.Now;
        }
    }

    // 文件传输信息
    [Serializable]
    public class FileTransferInfo
    {
        public string FileName { get; set; }
        public long FileSize { get; set; }
        public string FileId { get; set; }
        public string SenderId { get; set; }
        public string ReceiverId { get; set; }
        public DateTime TransferTime { get; set; }
        public int ChunkSize { get; set; } = 8192;
    }

    // 群组信息
    [Serializable]
    public class GroupInfo
    {
        public string GroupId { get; set; }
        public string GroupName { get; set; }
        public string CreatorId { get; set; }
        public DateTime CreatedTime { get; set; }
        public List<string> Members { get; set; }
        public string Description { get; set; }

        public GroupInfo()
        {
            Members = new List<string>();
            CreatedTime = DateTime.Now;
        }
    }
}

2. 服务器端实现

Server/ServerCore.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.Threading.Tasks;
using InstantMessenger.Shared;

namespace InstantMessenger.Server
{
    public class ServerCore
    {
        // 服务器配置
        private readonly IPAddress _ipAddress;
        private readonly int _port;
        private readonly TcpListener _tcpListener;
        private readonly UdpClient _udpListener;
        private bool _isRunning;
        
        // 客户端管理
        private readonly Dictionary<string, TcpClient> _connectedClients;
        private readonly Dictionary<string, UserInfo> _userDatabase;
        private readonly Dictionary<string, GroupInfo> _groups;
        
        // 线程安全锁
        private readonly object _lock = new object();
        
        // 服务器统计
        private int _totalMessages;
        private DateTime _startTime;
        
        public event EventHandler<string> LogMessage;
        public event EventHandler<string> UserConnected;
        public event EventHandler<string> UserDisconnected;

        public ServerCore(IPAddress ipAddress, int port)
        {
            _ipAddress = ipAddress;
            _port = port;
            _tcpListener = new TcpListener(ipAddress, port);
            _udpListener = new UdpClient(port);
            
            _connectedClients = new Dictionary<string, TcpClient>();
            _userDatabase = new Dictionary<string, UserInfo>();
            _groups = new Dictionary<string, GroupInfo>();
            
            // 创建默认群组
            CreateDefaultGroups();
        }

        private void CreateDefaultGroups()
        {
            var publicGroup = new GroupInfo
            {
                GroupId = "public",
                GroupName = "公共聊天室",
                CreatorId = "system",
                Description = "所有人都可以在这里聊天"
            };
            _groups.Add(publicGroup.GroupId, publicGroup);
        }

        public void Start()
        {
            try
            {
                _isRunning = true;
                _startTime = DateTime.Now;
                
                _tcpListener.Start();
                Log("服务器启动成功,监听端口: " + _port);
                
                // 启动TCP监听线程
                Thread tcpListenerThread = new Thread(ListenForTcpClients);
                tcpListenerThread.IsBackground = true;
                tcpListenerThread.Start();
                
                // 启动UDP监听线程
                Thread udpListenerThread = new Thread(ListenForUdpMessages);
                udpListenerThread.IsBackground = true;
                udpListenerThread.Start();
                
                // 启动心跳检查线程
                Thread heartbeatThread = new Thread(CheckHeartbeats);
                heartbeatThread.IsBackground = true;
                heartbeatThread.Start();
                
                Log("服务器准备就绪,等待客户端连接...");
            }
            catch (Exception ex)
            {
                Log("服务器启动失败: " + ex.Message);
                throw;
            }
        }

        public void Stop()
        {
            _isRunning = false;
            
            // 断开所有客户端连接
            lock (_lock)
            {
                foreach (var client in _connectedClients.Values)
                {
                    try
                    {
                        client.Close();
                    }
                    catch { }
                }
                _connectedClients.Clear();
            }
            
            _tcpListener.Stop();
            _udpListener.Close();
            Log("服务器已停止");
        }

        private void ListenForTcpClients()
        {
            while (_isRunning)
            {
                try
                {
                    TcpClient client = _tcpListener.AcceptTcpClient();
                    Thread clientThread = new Thread(() => HandleClient(client));
                    clientThread.IsBackground = true;
                    clientThread.Start();
                }
                catch (SocketException) when (!_isRunning)
                {
                    break;
                }
                catch (Exception ex)
                {
                    Log("接受客户端连接时出错: " + ex.Message);
                }
            }
        }

        private void ListenForUdpMessages()
        {
            IPEndPoint remoteEndPoint = new IPEndPoint(IPAddress.Any, 0);
            
            while (_isRunning)
            {
                try
                {
                    byte[] data = _udpListener.Receive(ref remoteEndPoint);
                    Task.Run(() => ProcessUdpMessage(data, remoteEndPoint));
                }
                catch (SocketException) when (!_isRunning)
                {
                    break;
                }
                catch (Exception ex)
                {
                    Log("接收UDP消息时出错: " + ex.Message);
                }
            }
        }

        private void HandleClient(TcpClient client)
        {
            string clientId = null;
            
            try
            {
                NetworkStream stream = client.GetStream();
                byte[] buffer = new byte[4096];
                
                while (_isRunning && client.Connected)
                {
                    int bytesRead = stream.Read(buffer, 0, buffer.Length);
                    if (bytesRead == 0) break;
                    
                    byte[] messageData = new byte[bytesRead];
                    Array.Copy(buffer, messageData, bytesRead);
                    
                    Message message = Message.Deserialize(messageData);
                    ProcessMessage(message, client);
                    
                    if (message.Type == MessageType.Login && clientId == null)
                    {
                        clientId = message.SenderId;
                    }
                }
            }
            catch (Exception ex)
            {
                Log($"处理客户端 {clientId} 时出错: {ex.Message}");
            }
            finally
            {
                if (clientId != null)
                {
                    HandleUserDisconnection(clientId);
                }
                client.Close();
            }
        }

        private void ProcessMessage(Message message, TcpClient client)
        {
            _totalMessages++;
            
            switch (message.Type)
            {
                case MessageType.Login:
                    HandleLogin(message, client);
                    break;
                    
                case MessageType.Logout:
                    HandleLogout(message);
                    break;
                    
                case MessageType.TextMessage:
                    HandleTextMessage(message);
                    break;
                    
                case MessageType.PrivateMessage:
                    HandlePrivateMessage(message);
                    break;
                    
                case MessageType.GroupMessage:
                    HandleGroupMessage(message);
                    break;
                    
                case MessageType.FileMessage:
                    HandleFileMessage(message, client);
                    break;
                    
                case MessageType.Heartbeat:
                    HandleHeartbeat(message);
                    break;
                    
                case MessageType.UserList:
                    SendUserList(message.SenderId);
                    break;
            }
        }

        private void HandleLogin(Message message, TcpClient client)
        {
            lock (_lock)
            {
                string userId = message.SenderId;
                
                if (_connectedClients.ContainsKey(userId))
                {
                    // 用户已登录,发送错误消息
                    SendErrorMessage(userId, "该用户已在线");
                    return;
                }
                
                // 添加用户到数据库(如果不存在)
                if (!_userDatabase.ContainsKey(userId))
                {
                    var userInfo = new UserInfo
                    {
                        UserId = userId,
                        UserName = message.SenderName,
                        Status = UserStatus.Online,
                        IPAddress = ((IPEndPoint)client.Client.RemoteEndPoint).Address.ToString(),
                        Port = ((IPEndPoint)client.Client.RemoteEndPoint).Port
                    };
                    _userDatabase[userId] = userInfo;
                }
                else
                {
                    _userDatabase[userId].Status = UserStatus.Online;
                    _userDatabase[userId].LastActive = DateTime.Now;
                }
                
                // 保存客户端连接
                _connectedClients[userId] = client;
                
                // 添加到公共群组
                if (_groups.ContainsKey("public") && !_groups["public"].Members.Contains(userId))
                {
                    _groups["public"].Members.Add(userId);
                }
                
                // 通知所有用户有新用户上线
                BroadcastUserStatusChange(userId, UserStatus.Online);
                
                Log($"用户登录: {message.SenderName} ({userId})");
                UserConnected?.Invoke(this, message.SenderName);
                
                // 发送登录成功消息
                SendLoginSuccess(userId);
                
                // 发送当前在线用户列表
                SendUserList(userId);
            }
        }

        private void HandleLogout(Message message)
        {
            string userId = message.SenderId;
            HandleUserDisconnection(userId);
        }

        private void HandleUserDisconnection(string userId)
        {
            lock (_lock)
            {
                if (_connectedClients.ContainsKey(userId))
                {
                    _connectedClients.Remove(userId);
                    
                    if (_userDatabase.ContainsKey(userId))
                    {
                        _userDatabase[userId].Status = UserStatus.Offline;
                        _userDatabase[userId].LastActive = DateTime.Now;
                    }
                    
                    // 通知所有用户有用户下线
                    BroadcastUserStatusChange(userId, UserStatus.Offline);
                    
                    Log($"用户断开连接: {userId}");
                    UserDisconnected?.Invoke(this, userId);
                }
            }
        }

        private void HandleTextMessage(Message message)
        {
            // 广播消息到所有用户
            BroadcastMessage(message);
        }

        private void HandlePrivateMessage(Message message)
        {
            // 发送私聊消息到指定用户
            SendToUser(message.ReceiverId, message);
        }

        private void HandleGroupMessage(Message message)
        {
            string groupId = message.ReceiverId;
            
            if (_groups.ContainsKey(groupId))
            {
                // 发送消息给群组所有成员
                foreach (var memberId in _groups[groupId].Members)
                {
                    if (memberId != message.SenderId) // 不发送给自己
                    {
                        SendToUser(memberId, message);
                    }
                }
            }
        }

        private void HandleFileMessage(Message message, TcpClient senderClient)
        {
            // 这里实现文件传输逻辑
            // 由于文件可能很大,需要分块传输
            SendToUser(message.ReceiverId, message);
        }

        private void HandleHeartbeat(Message message)
        {
            string userId = message.SenderId;
            
            lock (_lock)
            {
                if (_userDatabase.ContainsKey(userId))
                {
                    _userDatabase[userId].LastActive = DateTime.Now;
                }
            }
        }

        private void ProcessUdpMessage(byte[] data, IPEndPoint remoteEndPoint)
        {
            // 处理UDP消息(用于音视频通话等实时性要求高的场景)
            try
            {
                Message message = Message.Deserialize(data);
                
                switch (message.Type)
                {
                    case MessageType.VoiceMessage:
                    case MessageType.VideoMessage:
                        // 转发实时媒体流
                        ForwardMediaMessage(message);
                        break;
                }
            }
            catch (Exception ex)
            {
                Log($"处理UDP消息时出错: {ex.Message}");
            }
        }

        private void ForwardMediaMessage(Message message)
        {
            // 转发音视频消息
            if (!string.IsNullOrEmpty(message.ReceiverId))
            {
                // 发送给指定用户
                SendUdpToUser(message.ReceiverId, message);
            }
            else if (!string.IsNullOrEmpty(message.Metadata?.ContainsKey("GroupId")?.ToString()))
            {
                // 发送给群组
                string groupId = message.Metadata["GroupId"].ToString();
                if (_groups.ContainsKey(groupId))
                {
                    foreach (var memberId in _groups[groupId].Members)
                    {
                        if (memberId != message.SenderId)
                        {
                            SendUdpToUser(memberId, message);
                        }
                    }
                }
            }
        }

        private void SendLoginSuccess(string userId)
        {
            var response = new Message
            {
                Type = MessageType.Ack,
                Content = "登录成功",
                ReceiverId = userId,
                Timestamp = DateTime.Now
            };
            
            SendToUser(userId, response);
        }

        private void SendErrorMessage(string userId, string errorMessage)
        {
            var error = new Message
            {
                Type = MessageType.Error,
                Content = errorMessage,
                ReceiverId = userId,
                Timestamp = DateTime.Now
            };
            
            SendToUser(userId, error);
        }

        private void SendUserList(string userId)
        {
            lock (_lock)
            {
                var userList = _userDatabase.Values
                    .Where(u => u.Status != UserStatus.Offline)
                    .Select(u => new { u.UserId, u.UserName, u.Status })
                    .ToList();
                
                var message = new Message
                {
                    Type = MessageType.UserList,
                    ReceiverId = userId,
                    Data = System.Text.Encoding.UTF8.GetBytes(
                        Newtonsoft.Json.JsonConvert.SerializeObject(userList)),
                    Timestamp = DateTime.Now
                };
                
                SendToUser(userId, message);
            }
        }

        private void BroadcastMessage(Message message)
        {
            lock (_lock)
            {
                foreach (var clientPair in _connectedClients)
                {
                    if (clientPair.Key != message.SenderId) // 不发送给自己
                    {
                        try
                        {
                            NetworkStream stream = clientPair.Value.GetStream();
                            byte[] data = message.Serialize();
                            stream.Write(data, 0, data.Length);
                        }
                        catch
                        {
                            // 发送失败,可能客户端已断开
                        }
                    }
                }
            }
        }

        private void BroadcastUserStatusChange(string userId, UserStatus status)
        {
            var statusMessage = new Message
            {
                Type = MessageType.UserStatus,
                SenderId = userId,
                Content = status.ToString(),
                Timestamp = DateTime.Now
            };
            
            BroadcastMessage(statusMessage);
        }

        private void SendToUser(string userId, Message message)
        {
            lock (_lock)
            {
                if (_connectedClients.TryGetValue(userId, out TcpClient client))
                {
                    try
                    {
                        NetworkStream stream = client.GetStream();
                        byte[] data = message.Serialize();
                        stream.Write(data, 0, data.Length);
                    }
                    catch
                    {
                        // 发送失败
                    }
                }
            }
        }

        private void SendUdpToUser(string userId, Message message)
        {
            lock (_lock)
            {
                if (_userDatabase.TryGetValue(userId, out UserInfo userInfo))
                {
                    try
                    {
                        using (UdpClient udpClient = new UdpClient())
                        {
                            byte[] data = message.Serialize();
                            udpClient.Send(data, data.Length, userInfo.IPAddress, userInfo.Port + 1); // UDP端口通常是TCP端口+1
                        }
                    }
                    catch
                    {
                        // 发送失败
                    }
                }
            }
        }

        private void CheckHeartbeats()
        {
            while (_isRunning)
            {
                Thread.Sleep(30000); // 30秒检查一次
                
                lock (_lock)
                {
                    var inactiveUsers = _userDatabase.Values
                        .Where(u => u.Status == UserStatus.Online && 
                                   (DateTime.Now - u.LastActive).TotalMinutes > 2)
                        .ToList();
                    
                    foreach (var user in inactiveUsers)
                    {
                        Log($"用户 {user.UserName} 心跳超时,断开连接");
                        HandleUserDisconnection(user.UserId);
                    }
                }
            }
        }

        public Dictionary<string, UserInfo> GetOnlineUsers()
        {
            lock (_lock)
            {
                return _userDatabase
                    .Where(u => u.Value.Status != UserStatus.Offline)
                    .ToDictionary(k => k.Key, v => v.Value);
            }
        }

        public Dictionary<string, GroupInfo> GetGroups()
        {
            lock (_lock)
            {
                return new Dictionary<string, GroupInfo>(_groups);
            }
        }

        public ServerStatistics GetStatistics()
        {
            lock (_lock)
            {
                return new ServerStatistics
                {
                    Uptime = DateTime.Now - _startTime,
                    TotalMessages = _totalMessages,
                    OnlineUsers = _connectedClients.Count,
                    TotalUsers = _userDatabase.Count,
                    TotalGroups = _groups.Count
                };
            }
        }

        private void Log(string message)
        {
            string logMessage = $"[{DateTime.Now:HH:mm:ss}] {message}";
            LogMessage?.Invoke(this, logMessage);
            Console.WriteLine(logMessage);
        }
    }

    public class ServerStatistics
    {
        public TimeSpan Uptime { get; set; }
        public int TotalMessages { get; set; }
        public int OnlineUsers { get; set; }
        public int TotalUsers { get; set; }
        public int TotalGroups { get; set; }
    }
}

Server/Program.cs

using System;
using System.Net;
using System.Threading;
using InstantMessenger.Server;

namespace InstantMessenger.Server
{
    class Program
    {
        static ServerCore server;
        static bool isRunning = true;

        static void Main(string[] args)
        {
            Console.Title = "即时通讯服务器";
            Console.WriteLine("╔════════════════════════════════════════╗");
            Console.WriteLine("║      即时通讯服务器 v1.0              ║");
            Console.WriteLine("╚════════════════════════════════════════╝");
            Console.WriteLine();
            
            // 配置服务器参数
            IPAddress ipAddress = IPAddress.Any;
            int port = 8888;
            
            if (args.Length >= 2)
            {
                if (IPAddress.TryParse(args[0], out IPAddress parsedIp))
                    ipAddress = parsedIp;
                if (int.TryParse(args[1], out int parsedPort))
                    port = parsedPort;
            }
            
            try
            {
                // 创建并启动服务器
                server = new ServerCore(ipAddress, port);
                server.LogMessage += OnLogMessage;
                server.UserConnected += OnUserConnected;
                server.UserDisconnected += OnUserDisconnected;
                
                server.Start();
                
                // 启动控制台命令处理线程
                Thread commandThread = new Thread(ProcessCommands);
                commandThread.IsBackground = true;
                commandThread.Start();
                
                // 等待退出
                while (isRunning)
                {
                    Thread.Sleep(100);
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine($"服务器错误: {ex.Message}");
            }
            finally
            {
                server?.Stop();
                Console.WriteLine("按任意键退出...");
                Console.ReadKey();
            }
        }

        static void OnLogMessage(object sender, string message)
        {
            Console.WriteLine(message);
        }

        static void OnUserConnected(object sender, string username)
        {
            Console.ForegroundColor = ConsoleColor.Green;
            Console.WriteLine($"[{DateTime.Now:HH:mm:ss}] 用户上线: {username}");
            Console.ResetColor();
        }

        static void OnUserDisconnected(object sender, string username)
        {
            Console.ForegroundColor = ConsoleColor.Yellow;
            Console.WriteLine($"[{DateTime.Now:HH:mm:ss}] 用户下线: {username}");
            Console.ResetColor();
        }

        static void ProcessCommands()
        {
            while (isRunning)
            {
                Console.Write("服务器> ");
                string command = Console.ReadLine()?.Trim().ToLower();
                
                if (string.IsNullOrEmpty(command))
                    continue;
                    
                switch (command)
                {
                    case "help":
                        ShowHelp();
                        break;
                        
                    case "users":
                        ShowOnlineUsers();
                        break;
                        
                    case "stats":
                        ShowStatistics();
                        break;
                        
                    case "groups":
                        ShowGroups();
                        break;
                        
                    case "clear":
                        Console.Clear();
                        break;
                        
                    case "exit":
                    case "quit":
                        isRunning = false;
                        break;
                        
                    default:
                        Console.WriteLine($"未知命令: {command}");
                        break;
                }
            }
        }

        static void ShowHelp()
        {
            Console.WriteLine("可用命令:");
            Console.WriteLine("  help     - 显示帮助信息");
            Console.WriteLine("  users    - 显示在线用户");
            Console.WriteLine("  stats    - 显示服务器统计");
            Console.WriteLine("  groups   - 显示群组列表");
            Console.WriteLine("  clear    - 清屏");
            Console.WriteLine("  exit/quit - 退出服务器");
        }

        static void ShowOnlineUsers()
        {
            var users = server.GetOnlineUsers();
            Console.WriteLine($"在线用户 ({users.Count}):");
            Console.WriteLine("┌──────┬────────────┬────────┬──────────────┐");
            Console.WriteLine("│ ID   │ 用户名     │ 状态   │ 最后活动     │");
            Console.WriteLine("├──────┼────────────┼────────┼──────────────┤");
            
            foreach (var user in users.Values)
            {
                Console.WriteLine($"│ {user.UserId,-4} │ {user.UserName,-10} │ {user.Status,-6} │ {user.LastActive:HH:mm:ss} │");
            }
            Console.WriteLine("└──────┴────────────┴────────┴──────────────┘");
        }

        static void ShowStatistics()
        {
            var stats = server.GetStatistics();
            Console.WriteLine("服务器统计:");
            Console.WriteLine($"运行时间: {stats.Uptime:hh\\:mm\\:ss}");
            Console.WriteLine($"总消息数: {stats.TotalMessages}");
            Console.WriteLine($"在线用户: {stats.OnlineUsers}");
            Console.WriteLine($"注册用户: {stats.TotalUsers}");
            Console.WriteLine($"群组数量: {stats.TotalGroups}");
        }

        static void ShowGroups()
        {
            var groups = server.GetGroups();
            Console.WriteLine($"群组列表 ({groups.Count}):");
            
            foreach (var group in groups.Values)
            {
                Console.WriteLine($"  [{group.GroupId}] {group.GroupName}");
                Console.WriteLine($"     创建者: {group.CreatorId}, 成员: {group.Members.Count}");
                Console.WriteLine($"     描述: {group.Description}");
                Console.WriteLine();
            }
        }
    }
}

3. 客户端实现

Client/ClientCore.cs

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.Threading.Tasks;
using InstantMessenger.Shared;

namespace InstantMessenger.Client
{
    public class ClientCore
    {
        // 网络组件
        private TcpClient _tcpClient;
        private UdpClient _udpClient;
        private NetworkStream _networkStream;
        
        // 配置
        private string _serverIp;
        private int _serverPort;
        private int _localUdpPort;
        
        // 状态
        private bool _isConnected;
        private bool _isRunning;
        private string _userId;
        private string _userName;
        private UserStatus _status;
        
        // 数据存储
        private Dictionary<string, UserInfo> _onlineUsers;
        private Dictionary<string, List<Message>> _chatHistory;
        private Dictionary<string, FileTransferInfo> _fileTransfers;
        
        // 事件
        public event EventHandler<Message> MessageReceived;
        public event EventHandler<string> StatusChanged;
        public event EventHandler<List<UserInfo>> UserListUpdated;
        public event EventHandler<FileTransferInfo> FileTransferRequested;
        public event EventHandler<string> ConnectionStatusChanged;
        
        // 线程
        private Thread _receiveThread;
        private Thread _heartbeatThread;
        private Thread _udpReceiveThread;
        
        // 锁
        private readonly object _lock = new object();

        public ClientCore()
        {
            _onlineUsers = new Dictionary<string, UserInfo>();
            _chatHistory = new Dictionary<string, List<Message>>();
            _fileTransfers = new Dictionary<string, FileTransferInfo>();
            _status = UserStatus.Offline;
        }

        public bool Connect(string serverIp, int serverPort, string userId, string userName)
        {
            try
            {
                _serverIp = serverIp;
                _serverPort = serverPort;
                _userId = userId;
                _userName = userName;
                
                // 连接TCP服务器
                _tcpClient = new TcpClient();
                _tcpClient.Connect(serverIp, serverPort);
                _networkStream = _tcpClient.GetStream();
                
                // 初始化UDP客户端
                _localUdpPort = serverPort + 1;
                _udpClient = new UdpClient(_localUdpPort);
                
                // 发送登录消息
                var loginMessage = new Message
                {
                    Type = MessageType.Login,
                    SenderId = _userId,
                    SenderName = _userName,
                    Timestamp = DateTime.Now
                };
                
                SendMessage(loginMessage);
                
                _isConnected = true;
                _isRunning = true;
                _status = UserStatus.Online;
                
                // 启动接收线程
                _receiveThread = new Thread(ReceiveMessages);
                _receiveThread.IsBackground = true;
                _receiveThread.Start();
                
                // 启动UDP接收线程
                _udpReceiveThread = new Thread(ReceiveUdpMessages);
                _udpReceiveThread.IsBackground = true;
                _udpReceiveThread.Start();
                
                // 启动心跳线程
                _heartbeatThread = new Thread(SendHeartbeats);
                _heartbeatThread.IsBackground = true;
                _heartbeatThread.Start();
                
                ConnectionStatusChanged?.Invoke(this, "连接成功");
                StatusChanged?.Invoke(this, _status.ToString());
                
                return true;
            }
            catch (Exception ex)
            {
                ConnectionStatusChanged?.Invoke(this, $"连接失败: {ex.Message}");
                return false;
            }
        }

        public void Disconnect()
        {
            try
            {
                _isRunning = false;
                
                // 发送退出消息
                if (_isConnected)
                {
                    var logoutMessage = new Message
                    {
                        Type = MessageType.Logout,
                        SenderId = _userId,
                        Timestamp = DateTime.Now
                    };
                    
                    SendMessage(logoutMessage);
                }
                
                // 关闭连接
                _networkStream?.Close();
                _tcpClient?.Close();
                _udpClient?.Close();
                
                _isConnected = false;
                _status = UserStatus.Offline;
                
                StatusChanged?.Invoke(this, _status.ToString());
                ConnectionStatusChanged?.Invoke(this, "已断开连接");
            }
            catch (Exception ex)
            {
                ConnectionStatusChanged?.Invoke(this, $"断开连接时出错: {ex.Message}");
            }
        }

        public void SendTextMessage(string receiverId, string receiverName, string content, bool isPrivate = true)
        {
            if (!_isConnected) return;
            
            var message = new Message
            {
                Type = isPrivate ? MessageType.PrivateMessage : MessageType.GroupMessage,
                SenderId = _userId,
                SenderName = _userName,
                ReceiverId = receiverId,
                ReceiverName = receiverName,
                Content = content,
                Timestamp = DateTime.Now
            };
            
            SendMessage(message);
            StoreMessage(message);
        }

        public void SendFile(string receiverId, string filePath)
        {
            if (!_isConnected || !File.Exists(filePath)) return;
            
            var fileInfo = new FileInfo(filePath);
            var transferInfo = new FileTransferInfo
            {
                FileName = fileInfo.Name,
                FileSize = fileInfo.Length,
                FileId = Guid.NewGuid().ToString(),
                SenderId = _userId,
                ReceiverId = receiverId,
                TransferTime = DateTime.Now
            };
            
            _fileTransfers[transferInfo.FileId] = transferInfo;
            
            // 发送文件传输请求
            var requestMessage = new Message
            {
                Type = MessageType.FileMessage,
                SenderId = _userId,
                ReceiverId = receiverId,
                Content = "文件传输请求",
                Data = System.Text.Encoding.UTF8.GetBytes(
                    Newtonsoft.Json.JsonConvert.SerializeObject(transferInfo)),
                Timestamp = DateTime.Now
            };
            
            SendMessage(requestMessage);
        }

        public void AcceptFileTransfer(string fileId, string savePath)
        {
            if (_fileTransfers.TryGetValue(fileId, out FileTransferInfo transferInfo))
            {
                Task.Run(() => ReceiveFile(transferInfo, savePath));
            }
        }

        public void RejectFileTransfer(string fileId)
        {
            _fileTransfers.Remove(fileId);
        }

        private async Task ReceiveFile(FileTransferInfo transferInfo, string savePath)
        {
            // 实现文件接收逻辑
            // 这里需要实现分块接收和重组
        }

        public void ChangeStatus(UserStatus newStatus)
        {
            _status = newStatus;
            StatusChanged?.Invoke(this, newStatus.ToString());
        }

        public void RequestUserList()
        {
            if (!_isConnected) return;
            
            var request = new Message
            {
                Type = MessageType.UserList,
                SenderId = _userId,
                Timestamp = DateTime.Now
            };
            
            SendMessage(request);
        }

        private void SendMessage(Message message)
        {
            try
            {
                byte[] data = message.Serialize();
                _networkStream.Write(data, 0, data.Length);
                _networkStream.Flush();
            }
            catch (Exception ex)
            {
                ConnectionStatusChanged?.Invoke(this, $"发送消息失败: {ex.Message}");
            }
        }

        private void SendUdpMessage(Message message, string ipAddress, int port)
        {
            try
            {
                byte[] data = message.Serialize();
                _udpClient.Send(data, data.Length, ipAddress, port);
            }
            catch (Exception ex)
            {
                ConnectionStatusChanged?.Invoke(this, $"发送UDP消息失败: {ex.Message}");
            }
        }

        private void ReceiveMessages()
        {
            byte[] buffer = new byte[4096];
            
            while (_isRunning && _isConnected)
            {
                try
                {
                    int bytesRead = _networkStream.Read(buffer, 0, buffer.Length);
                    if (bytesRead == 0)
                    {
                        // 连接断开
                        Disconnect();
                        break;
                    }
                    
                    byte[] messageData = new byte[bytesRead];
                    Array.Copy(buffer, messageData, bytesRead);
                    
                    Message message = Message.Deserialize(messageData);
                    ProcessReceivedMessage(message);
                }
                catch (IOException)
                {
                    Disconnect();
                    break;
                }
                catch (Exception ex)
                {
                    ConnectionStatusChanged?.Invoke(this, $"接收消息时出错: {ex.Message}");
                }
            }
        }

        private void ReceiveUdpMessages()
        {
            IPEndPoint remoteEndPoint = new IPEndPoint(IPAddress.Any, 0);
            
            while (_isRunning && _isConnected)
            {
                try
                {
                    byte[] data = _udpClient.Receive(ref remoteEndPoint);
                    Message message = Message.Deserialize(data);
                    ProcessUdpMessage(message);
                }
                catch (SocketException) when (!_isRunning)
                {
                    break;
                }
                catch (Exception ex)
                {
                    ConnectionStatusChanged?.Invoke(this, $"接收UDP消息时出错: {ex.Message}");
                }
            }
        }

        private void ProcessReceivedMessage(Message message)
        {
            switch (message.Type)
            {
                case MessageType.Ack:
                    // 确认消息
                    break;
                    
                case MessageType.TextMessage:
                case MessageType.PrivateMessage:
                case MessageType.GroupMessage:
                    StoreMessage(message);
                    MessageReceived?.Invoke(this, message);
                    break;
                    
                case MessageType.UserList:
                    UpdateUserList(message);
                    break;
                    
                case MessageType.UserStatus:
                    UpdateUserStatus(message);
                    break;
                    
                case MessageType.FileMessage:
                    HandleFileMessage(message);
                    break;
                    
                case MessageType.Error:
                    ConnectionStatusChanged?.Invoke(this, $"服务器错误: {message.Content}");
                    break;
            }
        }

        private void ProcessUdpMessage(Message message)
        {
            switch (message.Type)
            {
                case MessageType.VoiceMessage:
                case MessageType.VideoMessage:
                    // 处理实时音视频流
                    MessageReceived?.Invoke(this, message);
                    break;
            }
        }

        private void StoreMessage(Message message)
        {
            string chatId = message.Type == MessageType.GroupMessage 
                ? message.ReceiverId 
                : message.SenderId == _userId ? message.ReceiverId : message.SenderId;
            
            lock (_lock)
            {
                if (!_chatHistory.ContainsKey(chatId))
                {
                    _chatHistory[chatId] = new List<Message>();
                }
                
                _chatHistory[chatId].Add(message);
            }
        }

        private void UpdateUserList(Message message)
        {
            try
            {
                string json = System.Text.Encoding.UTF8.GetString(message.Data);
                var users = Newtonsoft.Json.JsonConvert.DeserializeObject<List<UserInfo>>(json);
                
                lock (_lock)
                {
                    _onlineUsers.Clear();
                    foreach (var user in users)
                    {
                        _onlineUsers[user.UserId] = user;
                    }
                }
                
                UserListUpdated?.Invoke(this, users);
            }
            catch (Exception ex)
            {
                ConnectionStatusChanged?.Invoke(this, $"更新用户列表失败: {ex.Message}");
            }
        }

        private void UpdateUserStatus(Message message)
        {
            string userId = message.SenderId;
            if (Enum.TryParse<UserStatus>(message.Content, out UserStatus newStatus))
            {
                lock (_lock)
                {
                    if (_onlineUsers.ContainsKey(userId))
                    {
                        _onlineUsers[userId].Status = newStatus;
                        _onlineUsers[userId].LastActive = DateTime.Now;
                    }
                }
            }
        }

        private void HandleFileMessage(Message message)
        {
            try
            {
                var transferInfo = Newtonsoft.Json.JsonConvert.DeserializeObject<FileTransferInfo>(
                    System.Text.Encoding.UTF8.GetString(message.Data));
                
                _fileTransfers[transferInfo.FileId] = transferInfo;
                FileTransferRequested?.Invoke(this, transferInfo);
            }
            catch (Exception ex)
            {
                ConnectionStatusChanged?.Invoke(this, $"处理文件消息失败: {ex.Message}");
            }
        }

        private void SendHeartbeats()
        {
            while (_isRunning && _isConnected)
            {
                try
                {
                    Thread.Sleep(15000); // 15秒发送一次心跳
                    
                    var heartbeat = new Message
                    {
                        Type = MessageType.Heartbeat,
                        SenderId = _userId,
                        Timestamp = DateTime.Now
                    };
                    
                    SendMessage(heartbeat);
                }
                catch
                {
                    // 心跳发送失败
                }
            }
        }

        public List<Message> GetChatHistory(string chatId)
        {
            lock (_lock)
            {
                return _chatHistory.ContainsKey(chatId) 
                    ? new List<Message>(_chatHistory[chatId]) 
                    : new List<Message>();
            }
        }

        public List<UserInfo> GetOnlineUsers()
        {
            lock (_lock)
            {
                return _onlineUsers.Values.ToList();
            }
        }

        public bool IsConnected => _isConnected;
        public string UserId => _userId;
        public string UserName => _userName;
        public UserStatus Status => _status;
    }
}

Client/UI/MainForm.cs

using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Windows.Forms;
using InstantMessenger.Client;
using InstantMessenger.Shared;

namespace InstantMessenger.Client.UI
{
    public partial class MainForm : Form
    {
        private ClientCore _client;
        private string _currentChatId;
        private Dictionary<string, TreeNode> _userNodes;
        
        public MainForm()
        {
            InitializeComponent();
            InitializeClient();
        }

        private void InitializeClient()
        {
            _client = new ClientCore();
            _client.MessageReceived += OnMessageReceived;
            _client.StatusChanged += OnStatusChanged;
            _client.UserListUpdated += OnUserListUpdated;
            _client.FileTransferRequested += OnFileTransferRequested;
            _client.ConnectionStatusChanged += OnConnectionStatusChanged;
            
            _userNodes = new Dictionary<string, TreeNode>();
        }

        private void MainForm_Load(object sender, EventArgs e)
        {
            ShowLoginDialog();
        }

        private void ShowLoginDialog()
        {
            using (var loginForm = new LoginForm())
            {
                if (loginForm.ShowDialog() == DialogResult.OK)
                {
                    ConnectToServer(loginForm.ServerIp, loginForm.ServerPort, 
                                  loginForm.UserId, loginForm.UserName);
                }
                else
                {
                    Close();
                }
            }
        }

        private void ConnectToServer(string serverIp, int serverPort, string userId, string userName)
        {
            Text = $"即时通讯 - {userName}";
            
            if (_client.Connect(serverIp, serverPort, userId, userName))
            {
                // 创建公共群组节点
                var publicGroupNode = new TreeNode("公共聊天室")
                {
                    Tag = "public",
                    ImageKey = "group",
                    SelectedImageKey = "group"
                };
                treeViewUsers.Nodes.Add(publicGroupNode);
                
                // 创建在线用户节点
                var onlineNode = new TreeNode("在线用户")
                {
                    ImageKey = "users",
                    SelectedImageKey = "users"
                };
                treeViewUsers.Nodes.Add(onlineNode);
                
                // 请求用户列表
                _client.RequestUserList();
            }
            else
            {
                MessageBox.Show("连接服务器失败", "错误", 
                    MessageBoxButtons.OK, MessageBoxIcon.Error);
                ShowLoginDialog();
            }
        }

        private void OnConnectionStatusChanged(object sender, string status)
        {
            Invoke(new Action(() =>
            {
                toolStripStatusLabel.Text = status;
            }));
        }

        private void OnStatusChanged(object sender, string status)
        {
            Invoke(new Action(() =>
            {
                lblStatus.Text = $"状态: {status}";
            }));
        }

        private void OnUserListUpdated(object sender, List<UserInfo> users)
        {
            Invoke(new Action(() =>
            {
                UpdateUserTree(users);
            }));
        }

        private void UpdateUserTree(List<UserInfo> users)
        {
            var onlineNode = treeViewUsers.Nodes["在线用户"];
            if (onlineNode == null) return;
            
            onlineNode.Nodes.Clear();
            _userNodes.Clear();
            
            foreach (var user in users)
            {
                if (user.UserId == _client.UserId) continue;
                
                var userNode = new TreeNode($"{user.UserName} ({user.UserId})")
                {
                    Tag = user.UserId,
                    ImageKey = GetStatusImageKey(user.Status),
                    SelectedImageKey = GetStatusImageKey(user.Status),
                    ToolTipText = $"状态: {user.Status}\n最后活动: {user.LastActive:HH:mm:ss}"
                };
                
                onlineNode.Nodes.Add(userNode);
                _userNodes[user.UserId] = userNode;
            }
            
            onlineNode.Expand();
        }

        private string GetStatusImageKey(UserStatus status)
        {
            return status switch
            {
                UserStatus.Online => "online",
                UserStatus.Away => "away",
                UserStatus.Busy => "busy",
                UserStatus.Invisible => "invisible",
                _ => "offline"
            };
        }

        private void OnMessageReceived(object sender, Message message)
        {
            Invoke(new Action(() =>
            {
                DisplayMessage(message);
            }));
        }

        private void DisplayMessage(Message message)
        {
            string chatId = message.Type == MessageType.GroupMessage 
                ? message.ReceiverId 
                : message.SenderId == _client.UserId ? message.ReceiverId : message.SenderId;
            
            string displayText;
            
            if (message.Type == MessageType.GroupMessage)
            {
                displayText = $"[{message.Timestamp:HH:mm:ss}] [{message.SenderName}] {message.Content}";
            }
            else
            {
                displayText = $"[{message.Timestamp:HH:mm:ss}] {message.Content}";
            }
            
            // 添加到聊天记录
            if (_currentChatId == chatId)
            {
                txtChat.AppendText(displayText + Environment.NewLine);
                txtChat.ScrollToCaret();
            }
        }

        private void OnFileTransferRequested(object sender, FileTransferInfo transferInfo)
        {
            Invoke(new Action(() =>
            {
                using (var dialog = new FileTransferDialog(transferInfo))
                {
                    if (dialog.ShowDialog() == DialogResult.OK)
                    {
                        _client.AcceptFileTransfer(transferInfo.FileId, dialog.SavePath);
                    }
                    else
                    {
                        _client.RejectFileTransfer(transferInfo.FileId);
                    }
                }
            }));
        }

        private void treeViewUsers_AfterSelect(object sender, TreeViewEventArgs e)
        {
            if (e.Node.Tag == null) return;
            
            _currentChatId = e.Node.Tag.ToString();
            txtChat.Clear();
            
            // 加载聊天历史
            var history = _client.GetChatHistory(_currentChatId);
            foreach (var message in history)
            {
                DisplayMessage(message);
            }
            
            // 更新UI状态
            bool isGroup = e.Node.ImageKey == "group";
            txtMessage.Enabled = true;
            btnSend.Enabled = true;
            btnSendFile.Enabled = !isGroup; // 群聊暂不支持文件传输
            lblCurrentChat.Text = isGroup 
                ? $"群聊: {e.Node.Text}" 
                : $"私聊: {e.Node.Text}";
        }

        private void btnSend_Click(object sender, EventArgs e)
        {
            if (string.IsNullOrWhiteSpace(txtMessage.Text) || 
                string.IsNullOrEmpty(_currentChatId)) return;
            
            string message = txtMessage.Text;
            bool isGroup = _currentChatId == "public";
            
            if (isGroup)
            {
                _client.SendTextMessage(_currentChatId, "公共聊天室", message, false);
            }
            else
            {
                var selectedNode = treeViewUsers.SelectedNode;
                string receiverName = selectedNode?.Text.Split('(')[0].Trim();
                _client.SendTextMessage(_currentChatId, receiverName, message, true);
            }
            
            txtMessage.Clear();
            txtMessage.Focus();
        }

        private void btnSendFile_Click(object sender, EventArgs e)
        {
            if (string.IsNullOrEmpty(_currentChatId) || _currentChatId == "public") return;
            
            using (OpenFileDialog dialog = new OpenFileDialog())
            {
                dialog.Title = "选择要发送的文件";
                dialog.Filter = "所有文件 (*.*)|*.*";
                
                if (dialog.ShowDialog() == DialogResult.OK)
                {
                    _client.SendFile(_currentChatId, dialog.FileName);
                }
            }
        }

        private void txtMessage_KeyPress(object sender, KeyPressEventArgs e)
        {
            if (e.KeyChar == (char)Keys.Enter && !ModifierKeys.HasFlag(Keys.Shift))
            {
                e.Handled = true;
                btnSend_Click(sender, e);
            }
        }

        private void 连接CToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (_client.IsConnected)
            {
                _client.Disconnect();
            }
            ShowLoginDialog();
        }

        private void 退出XToolStripMenuItem_Click(object sender, EventArgs e)
        {
            Close();
        }

        private void 在线OToolStripMenuItem_Click(object sender, EventArgs e)
        {
            _client.ChangeStatus(UserStatus.Online);
        }

        private void 忙碌BToolStripMenuItem_Click(object sender, EventArgs e)
        {
            _client.ChangeStatus(UserStatus.Busy);
        }

        private void 离开AToolStripMenuItem_Click(object sender, EventArgs e)
        {
            _client.ChangeStatus(UserStatus.Away);
        }

        private void 隐身IToolStripMenuItem_Click(object sender, EventArgs e)
        {
            _client.ChangeStatus(UserStatus.Invisible);
        }

        private void 关于AToolStripMenuItem_Click(object sender, EventArgs e)
        {
            MessageBox.Show("即时通讯工具 v1.0\n\n功能:\n- 实时文字聊天\n- 私聊和群聊\n- 文件传输\n- 用户状态管理\n\n作者:AI助手", 
                "关于", MessageBoxButtons.OK, MessageBoxIcon.Information);
        }

        private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
        {
            _client?.Disconnect();
        }

        #region Windows Form Designer generated code

        private void InitializeComponent()
        {
            this.menuStrip1 = new System.Windows.Forms.MenuStrip();
            this.文件FToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
            this.连接CToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
            this.toolStripSeparator1 = new System.Windows.Forms.ToolStripSeparator();
            this.退出XToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
            this.状态SToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
            this.在线OToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
            this.忙碌BToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
            this.离开AToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
            this.隐身IToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
            this.帮助HToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
            this.关于AToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
            this.splitContainer1 = new System.Windows.Forms.SplitContainer();
            this.treeViewUsers = new System.Windows.Forms.TreeView();
            this.splitContainer2 = new System.Windows.Forms.SplitContainer();
            this.txtChat = new System.Windows.Forms.TextBox();
            this.panel1 = new System.Windows.Forms.Panel();
            this.txtMessage = new System.Windows.Forms.TextBox();
            this.btnSendFile = new System.Windows.Forms.Button();
            this.btnSend = new System.Windows.Forms.Button();
            this.statusStrip1 = new System.Windows.Forms.StatusStrip();
            this.toolStripStatusLabel = new System.Windows.Forms.ToolStripStatusLabel();
            this.lblStatus = new System.Windows.Forms.Label();
            this.lblCurrentChat = new System.Windows.Forms.Label();
            this.imageList1 = new System.Windows.Forms.ImageList();
            this.menuStrip1.SuspendLayout();
            ((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).BeginInit();
            this.splitContainer1.Panel1.SuspendLayout();
            this.splitContainer1.Panel2.SuspendLayout();
            this.splitContainer1.SuspendLayout();
            ((System.ComponentModel.ISupportInitialize)(this.splitContainer2)).BeginInit();
            this.splitContainer2.Panel1.SuspendLayout();
            this.splitContainer2.Panel2.SuspendLayout();
            this.splitContainer2.SuspendLayout();
            this.panel1.SuspendLayout();
            this.statusStrip1.SuspendLayout();
            this.SuspendLayout();
            
            // menuStrip1
            this.menuStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
                this.文件FToolStripMenuItem,
                this.状态SToolStripMenuItem,
                this.帮助HToolStripMenuItem});
            this.menuStrip1.Location = new System.Drawing.Point(0, 0);
            this.menuStrip1.Name = "menuStrip1";
            this.menuStrip1.Size = new System.Drawing.Size(800, 24);
            this.menuStrip1.TabIndex = 0;
            this.menuStrip1.Text = "menuStrip1";
            
            // 文件FToolStripMenuItem
            this.文件FToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
                this.连接CToolStripMenuItem,
                this.toolStripSeparator1,
                this.退出XToolStripMenuItem});
            this.文件FToolStripMenuItem.Name = "文件FToolStripMenuItem";
            this.文件FToolStripMenuItem.Size = new System.Drawing.Size(57, 20);
            this.文件FToolStripMenuItem.Text = "文件(&F)";
            
            // 连接CToolStripMenuItem
            this.连接CToolStripMenuItem.Name = "连接CToolStripMenuItem";
            this.连接CToolStripMenuItem.Size = new System.Drawing.Size(120, 22);
            this.连接CToolStripMenuItem.Text = "连接(&C)";
            
            // toolStripSeparator1
            this.toolStripSeparator1.Name = "toolStripSeparator1";
            this.toolStripSeparator1.Size = new System.Drawing.Size(117, 6);
            
            // 退出XToolStripMenuItem
            this.退出XToolStripMenuItem.Name = "退出XToolStripMenuItem";
            this.退出XToolStripMenuItem.Size = new System.Drawing.Size(120, 22);
            this.退出XToolStripMenuItem.Text = "退出(&X)";
            
            // 状态SToolStripMenuItem
            this.状态SToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
                this.在线OToolStripMenuItem,
                this.忙碌BToolStripMenuItem,
                this.离开AToolStripMenuItem,
                this.隐身IToolStripMenuItem});
            this.状态SToolStripMenuItem.Name = "状态SToolStripMenuItem";
            this.状态SToolStripMenuItem.Size = new System.Drawing.Size(58, 20);
            this.状态SToolStripMenuItem.Text = "状态(&S)";
            
            // 在线OToolStripMenuItem
            this.在线OToolStripMenuItem.Name = "在线OToolStripMenuItem";
            this.在线OToolStripMenuItem.Size = new System.Drawing.Size(116, 22);
            this.在线OToolStripMenuItem.Text = "在线(&O)";
            
            // 忙碌BToolStripMenuItem
            this.忙碌BToolStripMenuItem.Name = "忙碌BToolStripMenuItem";
            this.忙碌BToolStripMenuItem.Size = new System.Drawing.Size(116, 22);
            this.忙碌BToolStripMenuItem.Text = "忙碌(&B)";
            
            // 离开AToolStripMenuItem
            this.离开AToolStripMenuItem.Name = "离开AToolStripMenuItem";
            this.离开AToolStripMenuItem.Size = new System.Drawing.Size(116, 22);
            this.离开AToolStripMenuItem.Text = "离开(&A)";
            
            // 隐身IToolStripMenuItem
            this.隐身IToolStripMenuItem.Name = "隐身IToolStripMenuItem";
            this.隐身IToolStripMenuItem.Size = new System.Drawing.Size(116, 22);
            this.隐身IToolStripMenuItem.Text = "隐身(&I)";
            
            // 帮助HToolStripMenuItem
            this.帮助HToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
                this.关于AToolStripMenuItem});
            this.帮助HToolStripMenuItem.Name = "帮助HToolStripMenuItem";
            this.帮助HToolStripMenuItem.Size = new System.Drawing.Size(61, 20);
            this.帮助HToolStripMenuItem.Text = "帮助(&H)";
            
            // 关于AToolStripMenuItem
            this.关于AToolStripMenuItem.Name = "关于AToolStripMenuItem";
            this.关于AToolStripMenuItem.Size = new System.Drawing.Size(110, 22);
            this.关于AToolStripMenuItem.Text = "关于(&A)";
            
            // splitContainer1
            this.splitContainer1.Dock = System.Windows.Forms.DockStyle.Fill;
            this.splitContainer1.Location = new System.Drawing.Point(0, 24);
            this.splitContainer1.Name = "splitContainer1";
            // splitContainer1.Panel1
            this.splitContainer1.Panel1.Controls.Add(this.treeViewUsers);
            // splitContainer1.Panel2
            this.splitContainer1.Panel2.Controls.Add(this.splitContainer2);
            this.splitContainer1.Size = new System.Drawing.Size(800, 426);
            this.splitContainer1.SplitterDistance = 200;
            this.splitContainer1.TabIndex = 1;
            
            // treeViewUsers
            this.treeViewUsers.Dock = System.Windows.Forms.DockStyle.Fill;
            this.treeViewUsers.Location = new System.Drawing.Point(0, 0);
            this.treeViewUsers.Name = "treeViewUsers";
            this.treeViewUsers.Size = new System.Drawing.Size(200, 426);
            this.treeViewUsers.TabIndex = 0;
            
            // splitContainer2
            this.splitContainer2.Dock = System.Windows.Forms.DockStyle.Fill;
            this.splitContainer2.Location = new System.Drawing.Point(0, 0);
            this.splitContainer2.Name = "splitContainer2";
            this.splitContainer2.Orientation = System.Windows.Forms.Orientation.Horizontal;
            // splitContainer2.Panel1
            this.splitContainer2.Panel1.Controls.Add(this.txtChat);
            this.splitContainer2.Panel1.Controls.Add(this.lblCurrentChat);
            // splitContainer2.Panel2
            this.splitContainer2.Panel2.Controls.Add(this.panel1);
            this.splitContainer2.Size = new System.Drawing.Size(596, 426);
            this.splitContainer2.SplitterDistance = 350;
            this.splitContainer2.TabIndex = 0;
            
            // txtChat
            this.txtChat.Dock = System.Windows.Forms.DockStyle.Fill;
            this.txtChat.Location = new System.Drawing.Point(0, 20);
            this.txtChat.Multiline = true;
            this.txtChat.Name = "txtChat";
            this.txtChat.ReadOnly = true;
            this.txtChat.ScrollBars = System.Windows.Forms.ScrollBars.Vertical;
            this.txtChat.Size = new System.Drawing.Size(596, 330);
            this.txtChat.TabIndex = 0;
            
            // panel1
            this.panel1.Controls.Add(this.lblStatus);
            this.panel1.Controls.Add(this.txtMessage);
            this.panel1.Controls.Add(this.btnSendFile);
            this.panel1.Controls.Add(this.btnSend);
            this.panel1.Dock = System.Windows.Forms.DockStyle.Fill;
            this.panel1.Location = new System.Drawing.Point(0, 0);
            this.panel1.Name = "panel1";
            this.panel1.Size = new System.Drawing.Size(596, 72);
            this.panel1.TabIndex = 0;
            
            // txtMessage
            this.txtMessage.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
            | System.Windows.Forms.AnchorStyles.Left) 
            | System.Windows.Forms.AnchorStyles.Right)));
            this.txtMessage.Location = new System.Drawing.Point(3, 3);
            this.txtMessage.Multiline = true;
            this.txtMessage.Name = "txtMessage";
            this.txtMessage.Size = new System.Drawing.Size(490, 66);
            this.txtMessage.TabIndex = 0;
            
            // btnSendFile
            this.btnSendFile.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
            this.btnSendFile.Enabled = false;
            this.btnSendFile.Location = new System.Drawing.Point(499, 32);
            this.btnSendFile.Name = "btnSendFile";
            this.btnSendFile.Size = new System.Drawing.Size(94, 37);
            this.btnSendFile.TabIndex = 2;
            this.btnSendFile.Text = "发送文件";
            this.btnSendFile.UseVisualStyleBackColor = true;
            
            // btnSend
            this.btnSend.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
            this.btnSend.Enabled = false;
            this.btnSend.Location = new System.Drawing.Point(499, 3);
            this.btnSend.Name = "btnSend";
            this.btnSend.Size = new System.Drawing.Size(94, 23);
            this.btnSend.TabIndex = 1;
            this.btnSend.Text = "发送";
            this.btnSend.UseVisualStyleBackColor = true;
            
            // statusStrip1
            this.statusStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
                this.toolStripStatusLabel});
            this.statusStrip1.Location = new System.Drawing.Point(0, 450);
            this.statusStrip1.Name = "statusStrip1";
            this.statusStrip1.Size = new System.Drawing.Size(800, 22);
            this.statusStrip1.TabIndex = 2;
            this.statusStrip1.Text = "statusStrip1";
            
            // toolStripStatusLabel
            this.toolStripStatusLabel.Name = "toolStripStatusLabel";
            this.toolStripStatusLabel.Size = new System.Drawing.Size(59, 17);
            this.toolStripStatusLabel.Text = "未连接";
            
            // lblStatus
            this.lblStatus.AutoSize = true;
            this.lblStatus.Location = new System.Drawing.Point(3, 50);
            this.lblStatus.Name = "lblStatus";
            this.lblStatus.Size = new System.Drawing.Size(47, 12);
            this.lblStatus.TabIndex = 3;
            this.lblStatus.Text = "状态: ";
            
            // lblCurrentChat
            this.lblCurrentChat.Dock = System.Windows.Forms.DockStyle.Top;
            this.lblCurrentChat.Location = new System.Drawing.Point(0, 0);
            this.lblCurrentChat.Name = "lblCurrentChat";
            this.lblCurrentChat.Size = new System.Drawing.Size(596, 20);
            this.lblCurrentChat.TabIndex = 1;
            this.lblCurrentChat.Text = "未选择聊天";
            this.lblCurrentChat.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
            
            // imageList1
            this.imageList1.ColorDepth = System.Windows.Forms.ColorDepth.Depth32Bit;
            this.imageList1.ImageSize = new System.Drawing.Size(16, 16);
            this.imageList1.TransparentColor = System.Drawing.Color.Transparent;
            
            // MainForm
            this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F);
            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
            this.ClientSize = new System.Drawing.Size(800, 472);
            this.Controls.Add(this.splitContainer1);
            this.Controls.Add(this.statusStrip1);
            this.Controls.Add(this.menuStrip1);
            this.MainMenuStrip = this.menuStrip1;
            this.Name = "MainForm";
            this.Text = "即时通讯工具";
            this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.MainForm_FormClosing);
            this.Load += new System.EventHandler(this.MainForm_Load);
            this.menuStrip1.ResumeLayout(false);
            this.menuStrip1.PerformLayout();
            this.splitContainer1.Panel1.ResumeLayout(false);
            this.splitContainer1.Panel2.ResumeLayout(false);
            ((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).EndInit();
            this.splitContainer1.ResumeLayout(false);
            this.splitContainer2.Panel1.ResumeLayout(false);
            this.splitContainer2.Panel1.PerformLayout();
            this.splitContainer2.Panel2.ResumeLayout(false);
            ((System.ComponentModel.ISupportInitialize)(this.splitContainer2)).EndInit();
            this.splitContainer2.ResumeLayout(false);
            this.panel1.ResumeLayout(false);
            this.panel1.PerformLayout();
            this.statusStrip1.ResumeLayout(false);
            this.statusStrip1.PerformLayout();
            this.ResumeLayout(false);
            this.PerformLayout();

        }

        #endregion

        private System.Windows.Forms.MenuStrip menuStrip1;
        private System.Windows.Forms.ToolStripMenuItem 文件FToolStripMenuItem;
        private System.Windows.Forms.ToolStripMenuItem 连接CToolStripMenuItem;
        private System.Windows.Forms.ToolStripSeparator toolStripSeparator1;
        private System.Windows.Forms.ToolStripMenuItem 退出XToolStripMenuItem;
        private System.Windows.Forms.ToolStripMenuItem 状态SToolStripMenuItem;
        private System.Windows.Forms.ToolStripMenuItem 在线OToolStripMenuItem;
        private System.Windows.Forms.ToolStripMenuItem 忙碌BToolStripMenuItem;
        private System.Windows.Forms.ToolStripMenuItem 离开AToolStripMenuItem;
        private System.Windows.Forms.ToolStripMenuItem 隐身IToolStripMenuItem;
        private System.Windows.Forms.ToolStripMenuItem 帮助HToolStripMenuItem;
        private System.Windows.Forms.ToolStripMenuItem 关于AToolStripMenuItem;
        private System.Windows.Forms.SplitContainer splitContainer1;
        private System.Windows.Forms.TreeView treeViewUsers;
        private System.Windows.Forms.SplitContainer splitContainer2;
        private System.Windows.Forms.TextBox txtChat;
        private System.Windows.Forms.Panel panel1;
        private System.Windows.Forms.TextBox txtMessage;
        private System.Windows.Forms.Button btnSendFile;
        private System.Windows.Forms.Button btnSend;
        private System.Windows.Forms.StatusStrip statusStrip1;
        private System.Windows.Forms.ToolStripStatusLabel toolStripStatusLabel;
        private System.Windows.Forms.Label lblStatus;
        private System.Windows.Forms.Label lblCurrentChat;
        private System.Windows.Forms.ImageList imageList1;
    }
}

4. 辅助窗体

Client/UI/LoginForm.cs

using System;
using System.Windows.Forms;

namespace InstantMessenger.Client.UI
{
    public partial class LoginForm : Form
    {
        public string ServerIp { get; private set; }
        public int ServerPort { get; private set; }
        public string UserId { get; private set; }
        public string UserName { get; private set; }

        public LoginForm()
        {
            InitializeComponent();
        }

        private void btnConnect_Click(object sender, EventArgs e)
        {
            if (ValidateInput())
            {
                ServerIp = txtServerIp.Text;
                ServerPort = int.Parse(txtServerPort.Text);
                UserId = txtUserId.Text;
                UserName = txtUserName.Text;
                
                DialogResult = DialogResult.OK;
                Close();
            }
        }

        private bool ValidateInput()
        {
            if (string.IsNullOrWhiteSpace(txtServerIp.Text))
            {
                MessageBox.Show("请输入服务器IP地址", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
                return false;
            }
            
            if (!int.TryParse(txtServerPort.Text, out int port) || port < 1 || port > 65535)
            {
                MessageBox.Show("请输入有效的端口号", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
                return false;
            }
            
            if (string.IsNullOrWhiteSpace(txtUserId.Text))
            {
                MessageBox.Show("请输入用户ID", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
                return false;
            }
            
            if (string.IsNullOrWhiteSpace(txtUserName.Text))
            {
                MessageBox.Show("请输入用户名", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
                return false;
            }
            
            return true;
        }

        private void btnCancel_Click(object sender, EventArgs e)
        {
            DialogResult = DialogResult.Cancel;
            Close();
        }

        private void InitializeComponent()
        {
            this.lblServerIp = new System.Windows.Forms.Label();
            this.txtServerIp = new System.Windows.Forms.TextBox();
            this.lblServerPort = new System.Windows.Forms.Label();
            this.txtServerPort = new System.Windows.Forms.TextBox();
            this.lblUserId = new System.Windows.Forms.Label();
            this.txtUserId = new System.Windows.Forms.TextBox();
            this.lblUserName = new System.Windows.Forms.Label();
            this.txtUserName = new System.Windows.Forms.TextBox();
            this.btnConnect = new System.Windows.Forms.Button();
            this.btnCancel = new System.Windows.Forms.Button();
            this.SuspendLayout();
            
            // lblServerIp
            this.lblServerIp.AutoSize = true;
            this.lblServerIp.Location = new System.Drawing.Point(12, 15);
            this.lblServerIp.Name = "lblServerIp";
            this.lblServerIp.Size = new System.Drawing.Size(65, 12);
            this.lblServerIp.TabIndex = 0;
            this.lblServerIp.Text = "服务器IP:";
            
            // txtServerIp
            this.txtServerIp.Location = new System.Drawing.Point(83, 12);
            this.txtServerIp.Name = "txtServerIp";
            this.txtServerIp.Size = new System.Drawing.Size(150, 21);
            this.txtServerIp.TabIndex = 1;
            this.txtServerIp.Text = "127.0.0.1";
            
            // lblServerPort
            this.lblServerPort.AutoSize = true;
            this.lblServerPort.Location = new System.Drawing.Point(12, 42);
            this.lblServerPort.Name = "lblServerPort";
            this.lblServerPort.Size = new System.Drawing.Size(65, 12);
            this.lblServerPort.TabIndex = 2;
            this.lblServerPort.Text = "服务器端口:";
            
            // txtServerPort
            this.txtServerPort.Location = new System.Drawing.Point(83, 39);
            this.txtServerPort.Name = "txtServerPort";
            this.txtServerPort.Size = new System.Drawing.Size(150, 21);
            this.txtServerPort.TabIndex = 3;
            this.txtServerPort.Text = "8888";
            
            // lblUserId
            this.lblUserId.AutoSize = true;
            this.lblUserId.Location = new System.Drawing.Point(12, 69);
            this.lblUserId.Name = "lblUserId";
            this.lblUserId.Size = new System.Drawing.Size(53, 12);
            this.lblUserId.TabIndex = 4;
            this.lblUserId.Text = "用户ID:";
            
            // txtUserId
            this.txtUserId.Location = new System.Drawing.Point(83, 66);
            this.txtUserId.Name = "txtUserId";
            this.txtUserId.Size = new System.Drawing.Size(150, 21);
            this.txtUserId.TabIndex = 5;
            
            // lblUserName
            this.lblUserName.AutoSize = true;
            this.lblUserName.Location = new System.Drawing.Point(12, 96);
            this.lblUserName.Name = "lblUserName";
            this.lblUserName.Size = new System.Drawing.Size(53, 12);
            this.lblUserName.TabIndex = 6;
            this.lblUserName.Text = "用户名:";
            
            // txtUserName
            this.txtUserName.Location = new System.Drawing.Point(83, 93);
            this.txtUserName.Name = "txtUserName";
            this.txtUserName.Size = new System.Drawing.Size(150, 21);
            this.txtUserName.TabIndex = 7;
            
            // btnConnect
            this.btnConnect.Location = new System.Drawing.Point(77, 130);
            this.btnConnect.Name = "btnConnect";
            this.btnConnect.Size = new System.Drawing.Size(75, 23);
            this.btnConnect.TabIndex = 8;
            this.btnConnect.Text = "连接";
            this.btnConnect.UseVisualStyleBackColor = true;
            
            // btnCancel
            this.btnCancel.Location = new System.Drawing.Point(158, 130);
            this.btnCancel.Name = "btnCancel";
            this.btnCancel.Size = new System.Drawing.Size(75, 23);
            this.btnCancel.TabIndex = 9;
            this.btnCancel.Text = "取消";
            this.btnCancel.UseVisualStyleBackColor = true;
            
            // LoginForm
            this.AcceptButton = this.btnConnect;
            this.CancelButton = this.btnCancel;
            this.ClientSize = new System.Drawing.Size(250, 165);
            this.Controls.Add(this.btnCancel);
            this.Controls.Add(this.btnConnect);
            this.Controls.Add(this.txtUserName);
            this.Controls.Add(this.lblUserName);
            this.Controls.Add(this.txtUserId);
            this.Controls.Add(this.lblUserId);
            this.Controls.Add(this.txtServerPort);
            this.Controls.Add(this.lblServerPort);
            this.Controls.Add(this.txtServerIp);
            this.Controls.Add(this.lblServerIp);
            this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog;
            this.MaximizeBox = false;
            this.MinimizeBox = false;
            this.Name = "LoginForm";
            this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
            this.Text = "连接到服务器";
            this.ResumeLayout(false);
            this.PerformLayout();
        }

        private System.Windows.Forms.Label lblServerIp;
        private System.Windows.Forms.TextBox txtServerIp;
        private System.Windows.Forms.Label lblServerPort;
        private System.Windows.Forms.TextBox txtServerPort;
        private System.Windows.Forms.Label lblUserId;
        private System.Windows.Forms.TextBox txtUserId;
        private System.Windows.Forms.Label lblUserName;
        private System.Windows.Forms.TextBox txtUserName;
        private System.Windows.Forms.Button btnConnect;
        private System.Windows.Forms.Button btnCancel;
    }
}

参考代码 c#即时通讯工具 www.youwenfan.com/contentcnp/112225.html

5. 项目配置和运行说明

创建项目步骤:

  1. 创建解决方案和项目

    InstantMessenger/
    ├── InstantMessenger.sln
    ├── Server/ (控制台应用)
    ├── Client/ (Windows窗体应用)
    └── Shared/ (类库)
    
  2. 添加引用

    • Client项目引用Shared项目
    • Server项目引用Shared项目
  3. 添加NuGet包

    • Newtonsoft.Json (用于JSON序列化)

运行步骤:

  1. 启动服务器

    cd Server/bin/Debug
    InstantMessenger.Server.exe
    # 或带参数运行
    InstantMessenger.Server.exe 0.0.0.0 8888
    
  2. 启动客户端

    • 运行Client项目
    • 在登录界面输入:
      • 服务器IP: 127.0.0.1
      • 端口: 8888
      • 用户ID和用户名

功能特点:

  1. 双协议支持

    • TCP:可靠的消息传输(文字、文件)
    • UDP:实时音视频流传输
  2. 多种聊天模式

    • 私聊
    • 群聊(公共聊天室)
    • 文件传输
  3. 用户状态管理

    • 在线、离开、忙碌、隐身、离线
  4. 心跳机制

    • 自动检测连接状态
    • 清理不活跃用户
  5. 可扩展架构

    • 模块化设计
    • 易于添加新功能

扩展建议:

  1. 数据库支持

    • 使用SQLite或SQL Server存储用户信息和聊天记录
  2. 加密通信

    • 使用SSL/TLS加密TCP连接
    • 端到端加密消息内容
  3. 多媒体功能

    • 语音通话
    • 视频通话
    • 屏幕共享
  4. 高级功能

    • 消息撤回
    • 已读回执
    • 离线消息
    • 群组管理
posted @ 2026-01-16 15:36  w199899899  阅读(3)  评论(0)    收藏  举报