C# 仿QQ聊天功能实现 (SQL Server数据库)
基于C#和SQL Server的仿QQ聊天程序的实现
数据库设计
首先,我们需要设计数据库表结构:
-- 创建数据库
CREATE DATABASE QQChatDB;
GOUSE QQChatDB;
GO-- 用户表
CREATE TABLE Users (UserID INT PRIMARY KEY IDENTITY(1,1),QQNumber VARCHAR(20) UNIQUE NOT NULL,Password VARCHAR(50) NOT NULL,NickName NVARCHAR(50) NOT NULL,Gender INT DEFAULT 0, -- 0:未知, 1:男, 2:女Birthday DATE,Signature NVARCHAR(100),Avatar VARCHAR(100),OnlineStatus INT DEFAULT 0, -- 0:离线, 1:在线, 2:忙碌, 3:隐身LastLoginTime DATETIME,RegisterTime DATETIME DEFAULT GETDATE()
);-- 好友关系表
CREATE TABLE Friends (FriendshipID INT PRIMARY KEY IDENTITY(1,1),UserID INT NOT NULL,FriendID INT NOT NULL,GroupName NVARCHAR(50) DEFAULT N'我的好友',RemarkName NVARCHAR(50),AddTime DATETIME DEFAULT GETDATE(),FOREIGN KEY (UserID) REFERENCES Users(UserID),FOREIGN KEY (FriendID) REFERENCES Users(UserID),UNIQUE(UserID, FriendID)
);-- 消息表
CREATE TABLE Messages (MessageID BIGINT PRIMARY KEY IDENTITY(1,1),SenderID INT NOT NULL,ReceiverID INT NOT NULL,MessageType INT DEFAULT 0, -- 0:文本, 1:图片, 2:文件, 3:语音Content NVARCHAR(MAX),FilePath VARCHAR(200),SendTime DATETIME DEFAULT GETDATE(),IsRead BIT DEFAULT 0,FOREIGN KEY (SenderID) REFERENCES Users(UserID),FOREIGN KEY (ReceiverID) REFERENCES Users(UserID)
);-- 群组表
CREATE TABLE Groups (GroupID INT PRIMARY KEY IDENTITY(1,1),GroupName NVARCHAR(50) NOT NULL,CreatorID INT NOT NULL,CreateTime DATETIME DEFAULT GETDATE(),Avatar VARCHAR(100),Description NVARCHAR(200),FOREIGN KEY (CreatorID) REFERENCES Users(UserID)
);-- 群成员表
CREATE TABLE GroupMembers (GroupMemberID INT PRIMARY KEY IDENTITY(1,1),GroupID INT NOT NULL,UserID INT NOT NULL,Role INT DEFAULT 0, -- 0:普通成员, 1:管理员, 2:群主JoinTime DATETIME DEFAULT GETDATE(),FOREIGN KEY (GroupID) REFERENCES Groups(GroupID),FOREIGN KEY (UserID) REFERENCES Users(UserID),UNIQUE(GroupID, UserID)
);-- 群消息表
CREATE TABLE GroupMessages (GroupMessageID BIGINT PRIMARY KEY IDENTITY(1,1),GroupID INT NOT NULL,SenderID INT NOT NULL,MessageType INT DEFAULT 0, -- 0:文本, 1:图片, 2:文件, 3:语音Content NVARCHAR(MAX),FilePath VARCHAR(200),SendTime DATETIME DEFAULT GETDATE(),FOREIGN KEY (GroupID) REFERENCES Groups(GroupID),FOREIGN KEY (SenderID) REFERENCES Users(UserID)
);
C# 项目结构
创建一个Windows Forms应用程序,项目结构如下:
QQChatClient/
├── Forms/
│ ├── LoginForm.cs
│ ├── MainForm.cs
│ ├── ChatForm.cs
│ └── AddFriendForm.cs
├── Models/
│ ├── User.cs
│ ├── Message.cs
│ ├── Friend.cs
│ └── Group.cs
├── Services/
│ ├── DatabaseService.cs
│ ├── ChatService.cs
│ └── UserService.cs
├── Utils/
│ └── Encryption.cs
└── Program.cs
关键代码实现
数据库服务类 (DatabaseService.cs)
using System;
using System.Data;
using System.Data.SqlClient;
using System.Configuration;namespace QQChatClient.Services
{public class DatabaseService{private string connectionString;public DatabaseService(){connectionString = ConfigurationManager.ConnectionStrings["QQChatDB"].ConnectionString;}// 执行查询,返回DataTablepublic DataTable ExecuteQuery(string query, params SqlParameter[] parameters){using (SqlConnection connection = new SqlConnection(connectionString)){using (SqlCommand command = new SqlCommand(query, connection)){if (parameters != null){command.Parameters.AddRange(parameters);}DataTable dataTable = new DataTable();SqlDataAdapter adapter = new SqlDataAdapter(command);connection.Open();adapter.Fill(dataTable);return dataTable;}}}// 执行非查询操作,返回受影响的行数public int ExecuteNonQuery(string query, params SqlParameter[] parameters){using (SqlConnection connection = new SqlConnection(connectionString)){using (SqlCommand command = new SqlCommand(query, connection)){if (parameters != null){command.Parameters.AddRange(parameters);}connection.Open();return command.ExecuteNonQuery();}}}// 执行标量查询,返回第一行第一列public object ExecuteScalar(string query, params SqlParameter[] parameters){using (SqlConnection connection = new SqlConnection(connectionString)){using (SqlCommand command = new SqlCommand(query, connection)){if (parameters != null){command.Parameters.AddRange(parameters);}connection.Open();return command.ExecuteScalar();}}}}
}
用户服务类 (UserService.cs)
using System;
using System.Data;
using System.Data.SqlClient;
using QQChatClient.Models;
using QQChatClient.Utils;namespace QQChatClient.Services
{public class UserService{private DatabaseService dbService;public UserService(){dbService = new DatabaseService();}// 用户登录public User Login(string qqNumber, string password){string encryptedPassword = Encryption.Encrypt(password);string query = "SELECT * FROM Users WHERE QQNumber = @QQNumber AND Password = @Password";SqlParameter[] parameters = {new SqlParameter("@QQNumber", qqNumber),new SqlParameter("@Password", encryptedPassword)};DataTable result = dbService.ExecuteQuery(query, parameters);if (result.Rows.Count > 0){DataRow row = result.Rows[0];User user = new User{UserID = Convert.ToInt32(row["UserID"]),QQNumber = row["QQNumber"].ToString(),NickName = row["NickName"].ToString(),Gender = Convert.ToInt32(row["Gender"]),Birthday = row["Birthday"] != DBNull.Value ? Convert.ToDateTime(row["Birthday"]) : (DateTime?)null,Signature = row["Signature"].ToString(),Avatar = row["Avatar"].ToString(),OnlineStatus = Convert.ToInt32(row["OnlineStatus"])};// 更新最后登录时间UpdateLastLoginTime(user.UserID);return user;}return null;}// 用户注册public bool Register(string qqNumber, string password, string nickName){// 检查QQ号是否已存在string checkQuery = "SELECT COUNT(*) FROM Users WHERE QQNumber = @QQNumber";SqlParameter checkParam = new SqlParameter("@QQNumber", qqNumber);int count = Convert.ToInt32(dbService.ExecuteScalar(checkQuery, checkParam));if (count > 0){return false;}// 注册新用户string encryptedPassword = Encryption.Encrypt(password);string query = @"INSERT INTO Users (QQNumber, Password, NickName, RegisterTime) VALUES (@QQNumber, @Password, @NickName, GETDATE())";SqlParameter[] parameters = {new SqlParameter("@QQNumber", qqNumber),new SqlParameter("@Password", encryptedPassword),new SqlParameter("@NickName", nickName)};int result = dbService.ExecuteNonQuery(query, parameters);return result > 0;}// 更新最后登录时间private void UpdateLastLoginTime(int userID){string query = "UPDATE Users SET LastLoginTime = GETDATE() WHERE UserID = @UserID";SqlParameter parameter = new SqlParameter("@UserID", userID);dbService.ExecuteNonQuery(query, parameter);}// 更新用户状态public bool UpdateUserStatus(int userID, int status){string query = "UPDATE Users SET OnlineStatus = @Status WHERE UserID = @UserID";SqlParameter[] parameters = {new SqlParameter("@Status", status),new SqlParameter("@UserID", userID)};int result = dbService.ExecuteNonQuery(query, parameters);return result > 0;}// 根据用户ID获取用户信息public User GetUserById(int userID){string query = "SELECT * FROM Users WHERE UserID = @UserID";SqlParameter parameter = new SqlParameter("@UserID", userID);DataTable result = dbService.ExecuteQuery(query, parameter);if (result.Rows.Count > 0){DataRow row = result.Rows[0];return new User{UserID = Convert.ToInt32(row["UserID"]),QQNumber = row["QQNumber"].ToString(),NickName = row["NickName"].ToString(),Gender = Convert.ToInt32(row["Gender"]),Birthday = row["Birthday"] != DBNull.Value ? Convert.ToDateTime(row["Birthday"]) : (DateTime?)null,Signature = row["Signature"].ToString(),Avatar = row["Avatar"].ToString(),OnlineStatus = Convert.ToInt32(row["OnlineStatus"])};}return null;}// 搜索用户public DataTable SearchUsers(string keyword){string query = @"SELECT UserID, QQNumber, NickName, Gender, OnlineStatus FROM Users WHERE QQNumber LIKE @Keyword OR NickName LIKE @Keyword";SqlParameter parameter = new SqlParameter("@Keyword", "%" + keyword + "%");return dbService.ExecuteQuery(query, parameter);}}
}
聊天服务类 (ChatService.cs)
using System;
using System.Data;
using System.Data.SqlClient;
using QQChatClient.Models;namespace QQChatClient.Services
{public class ChatService{private DatabaseService dbService;public ChatService(){dbService = new DatabaseService();}// 获取好友列表public DataTable GetFriends(int userID){string query = @"SELECT f.FriendID, u.QQNumber, u.NickName, u.OnlineStatus, f.RemarkName, f.GroupNameFROM Friends fINNER JOIN Users u ON f.FriendID = u.UserIDWHERE f.UserID = @UserIDORDER BY f.GroupName, u.OnlineStatus DESC";SqlParameter parameter = new SqlParameter("@UserID", userID);return dbService.ExecuteQuery(query, parameter);}// 添加好友public bool AddFriend(int userID, int friendID, string groupName = "我的好友", string remarkName = ""){// 检查是否已经是好友string checkQuery = "SELECT COUNT(*) FROM Friends WHERE UserID = @UserID AND FriendID = @FriendID";SqlParameter[] checkParams = {new SqlParameter("@UserID", userID),new SqlParameter("@FriendID", friendID)};int count = Convert.ToInt32(dbService.ExecuteScalar(checkQuery, checkParams));if (count > 0){return false;}// 添加好友关系string query = @"INSERT INTO Friends (UserID, FriendID, GroupName, RemarkName, AddTime) VALUES (@UserID, @FriendID, @GroupName, @RemarkName, GETDATE())";SqlParameter[] parameters = {new SqlParameter("@UserID", userID),new SqlParameter("@FriendID", friendID),new SqlParameter("@GroupName", groupName),new SqlParameter("@RemarkName", remarkName)};int result = dbService.ExecuteNonQuery(query, parameters);return result > 0;}// 发送消息public bool SendMessage(int senderID, int receiverID, string content, int messageType = 0){string query = @"INSERT INTO Messages (SenderID, ReceiverID, MessageType, Content, SendTime) VALUES (@SenderID, @ReceiverID, @MessageType, @Content, GETDATE())";SqlParameter[] parameters = {new SqlParameter("@SenderID", senderID),new SqlParameter("@ReceiverID", receiverID),new SqlParameter("@MessageType", messageType),new SqlParameter("@Content", content)};int result = dbService.ExecuteNonQuery(query, parameters);return result > 0;}// 获取聊天记录public DataTable GetChatHistory(int userID, int friendID, int limit = 100){string query = @"SELECT TOP (@Limit) m.MessageID, m.SenderID, m.ReceiverID, m.Content, m.SendTime, s.NickName as SenderName, r.NickName as ReceiverNameFROM Messages mINNER JOIN Users s ON m.SenderID = s.UserIDINNER JOIN Users r ON m.ReceiverID = r.UserIDWHERE (m.SenderID = @UserID AND m.ReceiverID = @FriendID) OR (m.SenderID = @FriendID AND m.ReceiverID = @UserID)ORDER BY m.SendTime DESC";SqlParameter[] parameters = {new SqlParameter("@UserID", userID),new SqlParameter("@FriendID", friendID),new SqlParameter("@Limit", limit)};return dbService.ExecuteQuery(query, parameters);}// 获取未读消息public DataTable GetUnreadMessages(int userID){string query = @"SELECT m.MessageID, m.SenderID, m.Content, m.SendTime, u.NickName as SenderNameFROM Messages mINNER JOIN Users u ON m.SenderID = u.UserIDWHERE m.ReceiverID = @UserID AND m.IsRead = 0ORDER BY m.SendTime";SqlParameter parameter = new SqlParameter("@UserID", userID);return dbService.ExecuteQuery(query, parameter);}// 标记消息为已读public bool MarkMessagesAsRead(int userID, int friendID){string query = @"UPDATE Messages SET IsRead = 1 WHERE ReceiverID = @UserID AND SenderID = @FriendID AND IsRead = 0";SqlParameter[] parameters = {new SqlParameter("@UserID", userID),new SqlParameter("@FriendID", friendID)};int result = dbService.ExecuteNonQuery(query, parameters);return result > 0;}}
}
加密工具类 (Encryption.cs)
using System;
using System.Security.Cryptography;
using System.Text;namespace QQChatClient.Utils
{public static class Encryption{// 简单的MD5加密public static string Encrypt(string input){using (MD5 md5 = MD5.Create()){byte[] inputBytes = Encoding.UTF8.GetBytes(input);byte[] hashBytes = md5.ComputeHash(inputBytes);StringBuilder sb = new StringBuilder();for (int i = 0; i < hashBytes.Length; i++){sb.Append(hashBytes[i].ToString("x2"));}return sb.ToString();}}}
}
登录窗体 (LoginForm.cs)
using System;
using System.Windows.Forms;
using QQChatClient.Services;namespace QQChatClient.Forms
{public partial class LoginForm : Form{private UserService userService;public LoginForm(){InitializeComponent();userService = new UserService();}private void btnLogin_Click(object sender, EventArgs e){string qqNumber = txtQQNumber.Text.Trim();string password = txtPassword.Text;if (string.IsNullOrEmpty(qqNumber) || string.IsNullOrEmpty(password)){MessageBox.Show("QQ号和密码不能为空!", "提示", MessageBoxButtons.OK, MessageBoxIcon.Warning);return;}var user = userService.Login(qqNumber, password);if (user != null){// 更新用户状态为在线userService.UpdateUserStatus(user.UserID, 1);// 打开主窗体MainForm mainForm = new MainForm(user);mainForm.Show();this.Hide();}else{MessageBox.Show("QQ号或密码错误!", "登录失败", MessageBoxButtons.OK, MessageBoxIcon.Error);}}private void lnkRegister_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e){RegisterForm registerForm = new RegisterForm();registerForm.ShowDialog();}}
}
主窗体 (MainForm.cs)
using System;
using System.Data;
using System.Windows.Forms;
using QQChatClient.Models;
using QQChatClient.Services;namespace QQChatClient.Forms
{public partial class MainForm : Form{private User currentUser;private ChatService chatService;private UserService userService;public MainForm(User user){InitializeComponent();currentUser = user;chatService = new ChatService();userService = new UserService();// 初始化界面InitializeUI();LoadFriends();LoadUnreadMessages();}private void InitializeUI(){this.Text = $"QQ聊天 - {currentUser.NickName} ({currentUser.QQNumber})";lblUserName.Text = currentUser.NickName;lblSignature.Text = string.IsNullOrEmpty(currentUser.Signature) ? "这个家伙很懒,什么也没留下~" : currentUser.Signature;}private void LoadFriends(){DataTable friends = chatService.GetFriends(currentUser.UserID);foreach (DataRow row in friends.Rows){int friendID = Convert.ToInt32(row["FriendID"]);string nickName = row["RemarkName"].ToString();if (string.IsNullOrEmpty(nickName)){nickName = row["NickName"].ToString();}string groupName = row["GroupName"].ToString();int onlineStatus = Convert.ToInt32(row["OnlineStatus"]);// 添加到好友列表// 这里需要根据UI设计添加相应的控件}}private void LoadUnreadMessages(){DataTable unreadMessages = chatService.GetUnreadMessages(currentUser.UserID);foreach (DataRow row in unreadMessages.Rows){int senderID = Convert.ToInt32(row["SenderID"]);string senderName = row["SenderName"].ToString();string content = row["Content"].ToString();DateTime sendTime = Convert.ToDateTime(row["SendTime"]);// 显示未读消息提示// 这里需要根据UI设计添加相应的控件}}private void btnAddFriend_Click(object sender, EventArgs e){AddFriendForm addFriendForm = new AddFriendForm(currentUser.UserID);addFriendForm.ShowDialog();// 刷新好友列表LoadFriends();}private void MainForm_FormClosing(object sender, FormClosingEventArgs e){// 更新用户状态为离线userService.UpdateUserStatus(currentUser.UserID, 0);}}
}
聊天窗体 (ChatForm.cs)
using System;
using System.Data;
using System.Windows.Forms;
using QQChatClient.Models;
using QQChatClient.Services;namespace QQChatClient.Forms
{public partial class ChatForm : Form{private User currentUser;private User friend;private ChatService chatService;public ChatForm(User currentUser, User friend){InitializeComponent();this.currentUser = currentUser;this.friend = friend;chatService = new ChatService();InitializeUI();LoadChatHistory();// 标记消息为已读chatService.MarkMessagesAsRead(currentUser.UserID, friend.UserID);}private void InitializeUI(){this.Text = $"与 {friend.NickName} 聊天中";lblFriendName.Text = friend.NickName;lblFriendStatus.Text = friend.OnlineStatus == 1 ? "[在线]" : "[离线]";}private void LoadChatHistory(){DataTable chatHistory = chatService.GetChatHistory(currentUser.UserID, friend.UserID, 50);foreach (DataRow row in chatHistory.Rows){int senderID = Convert.ToInt32(row["SenderID"]);string senderName = row["SenderName"].ToString();string content = row["Content"].ToString();DateTime sendTime = Convert.ToDateTime(row["SendTime"]);// 添加消息到聊天界面// 这里需要根据UI设计添加相应的控件}}private void btnSend_Click(object sender, EventArgs e){string message = txtMessage.Text.Trim();if (string.IsNullOrEmpty(message)){MessageBox.Show("不能发送空消息!", "提示", MessageBoxButtons.OK, MessageBoxIcon.Warning);return;}bool success = chatService.SendMessage(currentUser.UserID, friend.UserID, message);if (success){// 添加消息到聊天界面// 这里需要根据UI设计添加相应的控件txtMessage.Clear();}else{MessageBox.Show("消息发送失败!", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);}}}
}
添加好友窗体 (AddFriendForm.cs)
using System;
using System.Data;
using System.Windows.Forms;
using QQChatClient.Services;namespace QQChatClient.Forms
{public partial class AddFriendForm : Form{private int currentUserID;private UserService userService;private ChatService chatService;public AddFriendForm(int userID){InitializeComponent();currentUserID = userID;userService = new UserService();chatService = new ChatService();}private void btnSearch_Click(object sender, EventArgs e){string keyword = txtKeyword.Text.Trim();if (string.IsNullOrEmpty(keyword)){MessageBox.Show("请输入QQ号或昵称进行搜索!", "提示", MessageBoxButtons.OK, MessageBoxIcon.Warning);return;}DataTable searchResult = userService.SearchUsers(keyword);dgvSearchResult.DataSource = searchResult;}private void dgvSearchResult_CellContentClick(object sender, DataGridViewCellEventArgs e){if (e.ColumnIndex == dgvSearchResult.Columns["colAdd"].Index && e.RowIndex >= 0){int friendID = Convert.ToInt32(dgvSearchResult.Rows[e.RowIndex].Cells["colUserID"].Value);// 检查是否已经是好友DataTable friends = chatService.GetFriends(currentUserID);foreach (DataRow row in friends.Rows){if (Convert.ToInt32(row["FriendID"]) == friendID){MessageBox.Show("该用户已经是您的好友!", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information);return;}}// 添加好友bool success = chatService.AddFriend(currentUserID, friendID);if (success){MessageBox.Show("好友添加成功!", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information);this.DialogResult = DialogResult.OK;this.Close();}else{MessageBox.Show("好友添加失败!", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);}}}}
}
配置文件 (App.config)
<?xml version="1.0" encoding="utf-8"?>
<configuration><connectionStrings><add name="QQChatDB" connectionString="Data Source=.;Initial Catalog=QQChatDB;Integrated Security=True" providerName="System.Data.SqlClient"/></connectionStrings>
</configuration>
参考项目 C# 仿QQ聊天功能实现(sql数据库) www.youwenfan.com/contentcsj/116432.html
说明
- 首先创建SQL Server数据库,并执行提供的SQL脚本创建表结构。
- 创建一个新的C# Windows Forms应用程序项目。
- 将上述代码文件添加到项目中。
- 根据UI设计,创建相应的窗体界面并添加控件。
- 在App.config中配置数据库连接字符串。
- 运行应用程序,注册新用户或使用现有账户登录。
功能扩展建议
- 实时通信:使用Socket或SignalR实现实时消息推送。
- 文件传输:实现文件上传和下载功能。
- 群聊功能:完善群组创建、管理和群聊功能。
- 表情支持:添加表情符号和自定义表情功能。
- 聊天记录导出:支持将聊天记录导出为文本或HTML文件。
- 界面美化:使用自定义控件和皮肤库美化界面。
- 安全性增强:使用更安全的加密算法和防止SQL注入。