在 `chatclient.sln` 中添加 `chatserver` 项目,配置调试和发布设置。更新 `App.config` 中 `log4net` 的配置路径。修改 `TrayIconManager.cs` 中的分隔符样式引用。更新 `chatapi.cs` 中的服务器地址和相关数据结构,增加 `userid` 和 `token` 字段。优化 `LoginWindow.xaml` 和 `MainWindow.xaml.cs` 的布局和逻辑,确保用户 ID 正确处理。更新 `log4net.config` 日志格式,添加控制台输出。配置 `chatserver.csproj` 的依赖项,添加服务器基本逻辑和消息类型枚举。更新 `launchSettings.json` 启动配置。
264 lines
14 KiB
C#
264 lines
14 KiB
C#
using System.Net.Sockets;
|
|
using System.Net.Http;
|
|
using log4net;
|
|
using log4net.Config;
|
|
using System.Net;
|
|
using System.Data.SQLite;
|
|
using chatserver.Data;
|
|
using System.Text.Json;
|
|
using System.Reflection;
|
|
|
|
[assembly: XmlConfigurator(ConfigFile = "config/log4net.config", Watch = true)]
|
|
namespace chatserver
|
|
{
|
|
internal class ChatServer
|
|
{
|
|
private static readonly ILog log = LogManager.GetLogger(typeof(ChatServer));
|
|
private static List<Socket> Client = new();
|
|
private static List<string> token = new();
|
|
private static Socket? Server;
|
|
private static SQLiteConnection? User_db;
|
|
|
|
static void Main(string[] args)
|
|
{
|
|
//XmlConfigurator.Configure();
|
|
log.Info("Hello World!");
|
|
Server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
|
|
Server.Bind(new IPEndPoint(IPAddress.Any, 52006));
|
|
Server.Listen(20);
|
|
OpenUser_db();
|
|
log.Info("服务器以在52006端口监听");
|
|
while (true)
|
|
{
|
|
try
|
|
{
|
|
Socket client = Server.Accept();
|
|
Client.Add(client);
|
|
log.Info($"用户{client.RemoteEndPoint}连接");
|
|
Thread thread = new Thread(() => HandleClient(client));
|
|
thread.Start();
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
log.Error("Error accepting client connection: " + ex.Message);
|
|
}
|
|
}
|
|
}
|
|
public static void OpenUser_db()
|
|
{
|
|
log.Info("正在打开数据库连接...");
|
|
User_db = new SQLiteConnection("Data Source=ServerUser.db;Version=3;"); //没有数据库则自动创建
|
|
User_db.Open();
|
|
EnsureUsersTableExists(); // 确保users表存在
|
|
log.Info("数据库连接已打开");
|
|
}
|
|
static void HandleClient(Socket socket)
|
|
{
|
|
try
|
|
{
|
|
while (true)
|
|
|
|
{
|
|
byte[] buffer = new byte[1024];
|
|
int received = socket.Receive(buffer);
|
|
if (received == 0) break; // 客户端断开连接
|
|
string message = System.Text.Encoding.UTF8.GetString(buffer, 0, received);
|
|
log.Info("Received message: " + message);
|
|
var Type = JsonSerializer.Deserialize<TypeData>(message);
|
|
if (Type != null)
|
|
{
|
|
if (Type.type == "register")
|
|
{
|
|
var sginuser = JsonSerializer.Deserialize<SignData>(message);
|
|
if (sginuser != null && sginuser.username != null && sginuser.password != null)
|
|
{
|
|
log.Info($"用户 {sginuser.username} 正在注册");
|
|
if (string.IsNullOrEmpty(sginuser.username) || string.IsNullOrEmpty(sginuser.password))
|
|
{
|
|
log.Warn("用户名或密码不能为空");
|
|
var emptyResult = new SignResultData { type = "register", status = "error_-1", message = "用户名或密码不能为空" };
|
|
socket.Send(JsonSerializer.SerializeToUtf8Bytes(emptyResult));
|
|
continue; // 如果用户名或密码为空,则跳过注册流程
|
|
}
|
|
if (sginuser.username.Length < 2 || sginuser.username.Length > 20)
|
|
{
|
|
log.Warn($"用户注册时 {sginuser.username} 用户名长度不符合要求");
|
|
var lengthResult = new SignResultData { type = "register", status = "error_2", message = "用户名长度必须在2到20个字符之间" };
|
|
socket.Send(JsonSerializer.SerializeToUtf8Bytes(lengthResult));
|
|
continue; // 如果用户名长度不符合要求,则跳过注册流程
|
|
}
|
|
if (sginuser.password.Length < 4 || sginuser.password.Length > 20)
|
|
{
|
|
log.Warn($"用户注册时 {sginuser.username} 密码长度不符合要求");
|
|
var weakPwdResult = new SignResultData { type = "register", status = "error_1", message = "密码长度必须在4到20个字符之间" };
|
|
socket.Send(JsonSerializer.SerializeToUtf8Bytes(weakPwdResult));
|
|
continue; // 如果密码过弱,则跳过注册流程
|
|
}
|
|
if (UserExists(sginuser.username))
|
|
{
|
|
log.Warn($"用户 {sginuser.username} 已存在");
|
|
var Result = new SignResultData { type = "register", status = "error_0", message = "用户名已存在" };
|
|
socket.Send(System.Text.Encoding.UTF8.GetBytes(JsonSerializer.Serialize(Result)));
|
|
continue;// 如果用户名已存在,则跳过注册流程
|
|
}
|
|
var cmd = new SQLiteCommand("INSERT INTO users (userid, username, password) VALUES (@userid, @username, @password)", User_db);
|
|
var timedUlid = Ulid.NewUlid(DateTimeOffset.UtcNow);
|
|
cmd.Parameters.AddWithValue("@userid", timedUlid);
|
|
cmd.Parameters.AddWithValue("@username", sginuser.username);
|
|
cmd.Parameters.AddWithValue("@password", sginuser.password);
|
|
cmd.ExecuteNonQuery();
|
|
log.Info($"用户 {sginuser.username} 注册成功(id:{timedUlid})");
|
|
var result = new SignResultData { type = "register", status = "succeed", message = "注册成功" };
|
|
socket.Send(System.Text.Encoding.UTF8.GetBytes(JsonSerializer.Serialize(result)));
|
|
}
|
|
}
|
|
else if (Type.type == "login")
|
|
{
|
|
var loginData = JsonSerializer.Deserialize<LoginData>(message);
|
|
if (loginData != null)
|
|
{
|
|
if (string.IsNullOrEmpty(loginData.username) || string.IsNullOrEmpty(loginData.password))
|
|
{
|
|
log.Warn("用户名或密码不能为空");
|
|
var emptyResult = new LoginResultData { type = "login", status = "error_-1", message = "用户名或密码不能为空" };
|
|
socket.Send(JsonSerializer.SerializeToUtf8Bytes(emptyResult));
|
|
continue; // 如果用户名或密码为空,则跳过登录流程
|
|
}
|
|
if (loginData.username.Length < 2 || loginData.username.Length > 20)
|
|
{
|
|
log.Warn($"用户登录时 {loginData.username} 用户名长度不符合要求");
|
|
var lengthResult = new LoginResultData { type = "login", status = "error_2", message = "用户名长度必须在2到20个字符之间" };
|
|
socket.Send(JsonSerializer.SerializeToUtf8Bytes(lengthResult));
|
|
continue; // 如果用户名长度不符合要求,则跳过登录流程
|
|
}
|
|
var Authentication = UserAuthentication(loginData.username, loginData.password);
|
|
if (Authentication != "")
|
|
{
|
|
log.Info($"用户 {loginData.username} 登录成功 (id:{Authentication})");
|
|
var result = new LoginResultData
|
|
{
|
|
type = "login",
|
|
status = "succeed",
|
|
message = "登录成功",
|
|
//token = Authentication,
|
|
username = loginData.username,
|
|
userid = Authentication
|
|
};
|
|
socket.Send(System.Text.Encoding.UTF8.GetBytes(JsonSerializer.Serialize(result)));
|
|
}
|
|
else
|
|
{
|
|
log.Warn($"用户 {loginData.username} 登录失败,用户名或密码错误");
|
|
var result = new LoginResultData { type = "login", status = "error_0", message = "用户名或密码错误" };
|
|
socket.Send(System.Text.Encoding.UTF8.GetBytes(JsonSerializer.Serialize(result)));
|
|
}
|
|
}
|
|
}
|
|
else if (Type.type == "chat")
|
|
{
|
|
var chatData = JsonSerializer.Deserialize<ChatData>(message);
|
|
if (chatData != null && chatData.message != null)
|
|
{
|
|
log.Info($"接收到聊天消息: {chatData.message}");
|
|
var chatRegisterData = new ChatRegisterData
|
|
{
|
|
type = "chat",
|
|
userid = chatData.userid ?? "Unid",
|
|
user = GetUsernameByUserId(chatData.userid!),
|
|
message = chatData.message,
|
|
msgtype = chatData.msgtype ?? MessageType.Text,
|
|
// 替换为
|
|
timestamp = DateTime.Now
|
|
};
|
|
foreach (var client in Client)
|
|
{
|
|
client.Send(System.Text.Encoding.UTF8.GetBytes(JsonSerializer.Serialize(chatRegisterData)));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
log.Warn("接收到无效的聊天消息");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
log.Warn("未知的请求类型: " + Type.type);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
catch (SocketException ex)
|
|
{
|
|
log.Error("Socket error: " + ex.Message);
|
|
}
|
|
catch (JsonException ex)
|
|
{
|
|
log.Error("JSON parsing error: " + ex.Message);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
log.Error("Error handling client: " + ex.Message);
|
|
}
|
|
finally
|
|
{
|
|
log.Info($"用户{socket.RemoteEndPoint}已断开连接");
|
|
socket.Close();
|
|
Client.Remove(socket);
|
|
}
|
|
}
|
|
/// <summary>
|
|
/// 查询User_db是否有相同用户名
|
|
/// </summary>
|
|
/// <param name="username"></param>
|
|
/// <returns></returns>
|
|
static bool UserExists(string username)
|
|
{
|
|
using var cmd = new SQLiteCommand("SELECT COUNT(*) FROM users WHERE username = @username", User_db);
|
|
cmd.Parameters.AddWithValue("@username", username);
|
|
var count = Convert.ToInt32(cmd.ExecuteScalar());
|
|
return count > 0;
|
|
}
|
|
/// <summary>
|
|
/// 验证用户登录信息并返回userid结果
|
|
/// </summary>
|
|
/// <param name="username"></param>
|
|
/// <param name="password"></param>
|
|
/// <returns></returns>
|
|
static string UserAuthentication(string username, string password)
|
|
{
|
|
using var cmd = new SQLiteCommand("SELECT userid FROM users WHERE username = @username AND password = @password", User_db);
|
|
cmd.Parameters.AddWithValue("@username", username);
|
|
cmd.Parameters.AddWithValue("@password", password);
|
|
var result = cmd.ExecuteScalar();
|
|
return result != null ? result.ToString()! : string.Empty;
|
|
}
|
|
// 在ChatServer类中添加一个方法用于初始化users表
|
|
private static void EnsureUsersTableExists()
|
|
{
|
|
using var cmd = new SQLiteCommand(@"
|
|
CREATE TABLE IF NOT EXISTS users (
|
|
userid TEXT PRIMARY KEY,
|
|
username TEXT NOT NULL UNIQUE,
|
|
password TEXT NOT NULL
|
|
)", User_db);
|
|
cmd.ExecuteNonQuery();
|
|
}
|
|
/// <summary>
|
|
/// 根据userid查询对应的用户名
|
|
/// </summary>
|
|
/// <param name="userid"></param>
|
|
/// <returns>用户名,如果不存在则返回</returns>
|
|
static string GetUsernameByUserId(string userid)
|
|
{
|
|
if (string.IsNullOrEmpty(userid) || userid == "Unid")
|
|
{
|
|
return "Unnamed"; // 如果userid为空或为"Unid",返回默认用户名
|
|
}
|
|
using var cmd = new SQLiteCommand("SELECT username FROM users WHERE userid = @userid", User_db);
|
|
cmd.Parameters.AddWithValue("@userid", userid);
|
|
var result = cmd.ExecuteScalar();
|
|
return result != null ? result.ToString()! : "Unnamed";
|
|
}
|
|
|
|
}
|
|
} |