diff --git a/Clion/.idea/.gitignore b/Clion/.idea/.gitignore new file mode 100644 index 0000000..35410ca --- /dev/null +++ b/Clion/.idea/.gitignore @@ -0,0 +1,8 @@ +# 默认忽略的文件 +/shelf/ +/workspace.xml +# 基于编辑器的 HTTP 客户端请求 +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/Clion/.idea/Clion.iml b/Clion/.idea/Clion.iml new file mode 100644 index 0000000..f08604b --- /dev/null +++ b/Clion/.idea/Clion.iml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/Clion/.idea/editor.xml b/Clion/.idea/editor.xml new file mode 100644 index 0000000..633b554 --- /dev/null +++ b/Clion/.idea/editor.xml @@ -0,0 +1,251 @@ + + + + + \ No newline at end of file diff --git a/Clion/.idea/misc.xml b/Clion/.idea/misc.xml new file mode 100644 index 0000000..db293e8 --- /dev/null +++ b/Clion/.idea/misc.xml @@ -0,0 +1,8 @@ + + + + + + {} + \ No newline at end of file diff --git a/Clion/.idea/modules.xml b/Clion/.idea/modules.xml new file mode 100644 index 0000000..cf65081 --- /dev/null +++ b/Clion/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/Clion/.idea/vcs.xml b/Clion/.idea/vcs.xml new file mode 100644 index 0000000..6c0b863 --- /dev/null +++ b/Clion/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/Clion/CMakeLists.txt b/Clion/CMakeLists.txt new file mode 100644 index 0000000..599356d --- /dev/null +++ b/Clion/CMakeLists.txt @@ -0,0 +1,6 @@ +cmake_minimum_required(VERSION 3.31) +project(Clion C) + +set(CMAKE_C_STANDARD 11) + +add_executable(Clion main.c) diff --git a/Clion/main.c b/Clion/main.c new file mode 100644 index 0000000..4dd72fa --- /dev/null +++ b/Clion/main.c @@ -0,0 +1,7 @@ +#include + +int main(void) +{ + printf("Hello, World!\n"); + return 0; +} \ No newline at end of file diff --git a/Clion/test.c b/Clion/test.c new file mode 100644 index 0000000..ce0ecf2 --- /dev/null +++ b/Clion/test.c @@ -0,0 +1,10 @@ +// +// Created by ZBright on 25-5-29. +// +#include + +int main() +{ + printf("Hello World\n"); + return 0; +} \ No newline at end of file diff --git a/Clion/test.exe b/Clion/test.exe new file mode 100644 index 0000000..ac41184 Binary files /dev/null and b/Clion/test.exe differ diff --git a/Code/Project_Client.c b/Code/Project_Client.c new file mode 100644 index 0000000..e69de29 diff --git a/Code/Project_Server.c b/Code/Project_Server.c new file mode 100644 index 0000000..e69de29 diff --git a/Code/cet.c b/Code/cet.c new file mode 100644 index 0000000..c7939b6 --- /dev/null +++ b/Code/cet.c @@ -0,0 +1,54 @@ +#include +#include +#include /* See NOTES */ +#include +#include +#include +#include + +#define OWNADDR "192.168.0.17" //我自己电脑的ip地址 +#define OWNPORT 10000 //我自己电脑的该程序的端口号 + +#define SERVERADDR "192.168.0.114" //对方的服务器的IP地址 +#define SERVERPORT 20000 //对方的服务器的端口号 + +int main() +{ + //1、买手机(建立套接字) + int socketfd = socket(AF_INET, SOCK_STREAM, 0); + if(socketfd == -1) + { + printf("没钱了....,失败\n"); + return -1; + } + //2、绑定自己的电话号码(绑定自己的IP地址 和端口号) + //定义一个IPV4结构体变量,初始化自己的IP地址和端口号 + struct sockaddr_in ownAddr; + ownAddr.sin_family = AF_INET;/*地址族  IPV4*/ + ownAddr.sin_port = htons(OWNPORT);//htons 将本地端口号转为网络端口号 + ownAddr.sin_addr.s_addr = inet_addr(OWNADDR);//将本地IP地址转为网络IP地址 + + bind(socketfd, (struct sockaddr *)&ownAddr,sizeof(struct sockaddr_in)); + + //3、开始打电话(发起连接) + struct sockaddr_in serverAddr; + serverAddr.sin_family = AF_INET;/*地址族  IPV4*/ + serverAddr.sin_port = htons(SERVERPORT); //htons 将本地端口号转为网络端口号 + serverAddr.sin_addr.s_addr = inet_addr(SERVERADDR); //将本地IP地址转为网络IP地址 + + connect(socketfd,(struct sockaddr *)&serverAddr,sizeof(struct sockaddr_in)); + + //4、聊天 + while(1) + { + printf("data:"); + char buf[1024]={0}; + scanf("%s",buf); + //发送数据 + send(socketfd, buf, strlen(buf), 0); + } + //5、关闭 + close(socketfd); + + return 0; +} \ No newline at end of file diff --git a/Code/output/cet b/Code/output/cet new file mode 100644 index 0000000..1453414 Binary files /dev/null and b/Code/output/cet differ diff --git a/Code/ser.c b/Code/ser.c new file mode 100644 index 0000000..efdec25 --- /dev/null +++ b/Code/ser.c @@ -0,0 +1,65 @@ +#include +#include /* See NOTES */ +#include +#include +#include +#include +#include + + + +#define SERVER_ADDR "172.29.147.16" //服务器的IP地址 +#define SERVER_PORT 20000 //port 服务器的端口号 + + +int main() +{ + int ret; + //1、买手机(建立套接字) + int socketFd = socket(AF_INET, SOCK_STREAM, 0); + if(socketFd == -1){ + perror("socket error"); + exit(0); + } + //2、绑定自己的电话号码(绑定自己的IP地址和端口号) + //定义一个IPV4结构体变量,存储IP地址和端口号 + struct sockaddr_in serverAddr; + serverAddr.sin_family = AF_INET ;//IPv4 + serverAddr.sin_port = htons(SERVER_PORT);//16端口号 --本地端口号--网络端口号 + serverAddr.sin_addr.s_addr = inet_addr(SERVER_ADDR); //32IP地址 --本地IP--网络IP + + ret = bind(socketFd, (struct sockaddr*)&serverAddr, sizeof(struct sockaddr_in)); + if(ret == -1){ + perror("bind error"); + exit(0); + } + //3、设置铃声(设置监听) + ret = listen(socketFd, 20); //20表示同时连接上来的客户端的最大数量 + if(ret == -1){ + perror("listen error"); + exit(0); + } + printf("阻塞等待新的客户端连接......\n"); + //4、坐等电话(阻塞等待客户端的连接) + int newClientFd = accept(socketFd, NULL,NULL); + if(ret == -1){ + perror("accept error"); + exit(0); + } + //注意 accept函数的返回值 表示 新的客户端的文件描述符 ,后面与客户端通信 必须使用该文件描述符 + printf("有新的客户端连接上来....\n"); + + //5、聊天 接收 数据 + while(1) + { + char buf[1024]={0}; + read(newClientFd,buf,sizeof(buf)); + printf("buf:%s\n",buf); + } + + //6、关闭 断开连接 + close(socketFd); + close(newClientFd); + + return 0; +} diff --git a/Project/client b/Project/client new file mode 100644 index 0000000..3f5f29a Binary files /dev/null and b/Project/client differ diff --git a/Project/client.c b/Project/client.c new file mode 100644 index 0000000..53d748a --- /dev/null +++ b/Project/client.c @@ -0,0 +1,270 @@ +#include +#include +#include +#include +#include +#include +#include + +#define BUFFER_SIZE 1024 +#define SERVER_IP "127.0.0.1" +#define PORT 8888 + +int client_socket; +pthread_t receive_thread; +int running = 1; + +// 函数声明 +void *receive_messages(void *arg); +void handle_signal(int sig); +void login_register_menu(); + +int main() { + struct sockaddr_in server_addr; + + // 创建socket + client_socket = socket(AF_INET, SOCK_STREAM, 0); + if (client_socket == -1) { + perror("Socket creation failed"); + exit(EXIT_FAILURE); + } + + // 准备服务器地址 + memset(&server_addr, 0, sizeof(server_addr)); + server_addr.sin_family = AF_INET; + server_addr.sin_port = htons(PORT); + + // 将IPv4地址从点分十进制转换为二进制形式 + if (inet_pton(AF_INET, SERVER_IP, &server_addr.sin_addr) <= 0) { + perror("Invalid address/ Address not supported"); + exit(EXIT_FAILURE); + } + + // 连接到服务器 + if (connect(client_socket, (struct sockaddr *)&server_addr, sizeof(server_addr)) == -1) { + perror("Connection failed"); + exit(EXIT_FAILURE); + } + + printf("Connected to server.\n"); + + // 设置信号处理 + signal(SIGINT, handle_signal); + + // 创建接收消息的线程 + if (pthread_create(&receive_thread, NULL, receive_messages, NULL) != 0) { + perror("Thread creation failed"); + close(client_socket); + exit(EXIT_FAILURE); + } + + // 登录/注册菜单 + login_register_menu(); + + // 聊天循环 + printf("\nWelcome to the chat! Type /help for commands.\n"); + char message[BUFFER_SIZE]; + while (running) { + memset(message, 0, BUFFER_SIZE); + fgets(message, BUFFER_SIZE - 1, stdin); + + // 移除换行符 + size_t len = strlen(message); + if (len > 0 && message[len - 1] == '\n') { + message[len - 1] = '\0'; + } + + // 检查是否是退出命令 + if (strcmp(message, "/quit") == 0) { + running = 0; + break; + } + + // 发送消息到服务器 + if (send(client_socket, message, strlen(message), 0) == -1) { + perror("Send failed"); + running = 0; + } + } + + // 等待接收线程结束 + pthread_join(receive_thread, NULL); + + // 关闭socket + close(client_socket); + printf("Disconnected from server.\n"); + + return 0; +} + +void *receive_messages(void *arg) { + char buffer[BUFFER_SIZE]; + int bytes_received; + + while (running) { + memset(buffer, 0, BUFFER_SIZE); + bytes_received = recv(client_socket, buffer, BUFFER_SIZE - 1, 0); + + if (bytes_received <= 0) { + // 服务器断开连接 + printf("Server disconnected.\n"); + running = 0; + break; + } + + // 打印接收到的消息 + printf("%s", buffer); + } + + pthread_exit(NULL); +} + +void handle_signal(int sig) { + printf("\nExiting...\n"); + running = 0; + close(client_socket); + exit(EXIT_SUCCESS); +} + +void login_register_menu() { + char buffer[BUFFER_SIZE]; + int choice; + char username[BUFFER_SIZE]; + char password[BUFFER_SIZE]; + + while (running) { + // 接收菜单 + memset(buffer, 0, BUFFER_SIZE); + if (recv(client_socket, buffer, BUFFER_SIZE - 1, 0) <= 0) { + printf("Server disconnected.\n"); + running = 0; + return; + } + printf("%s", buffer); + + // 发送选择 + scanf("%d", &choice); + getchar(); // 消耗换行符 + sprintf(buffer, "%d", choice); + if (send(client_socket, buffer, strlen(buffer), 0) == -1) { + perror("Send failed"); + running = 0; + return; + } + + // 处理登录 + if (choice == 1) { + // 接收用户名提示 + memset(buffer, 0, BUFFER_SIZE); + if (recv(client_socket, buffer, BUFFER_SIZE - 1, 0) <= 0) { + printf("Server disconnected.\n"); + running = 0; + return; + } + printf("%s", buffer); + + // 发送用户名 + fgets(username, BUFFER_SIZE - 1, stdin); + username[strcspn(username, "\n")] = 0; // 移除换行符 + if (send(client_socket, username, strlen(username), 0) == -1) { + perror("Send failed"); + running = 0; + return; + } + + // 接收密码提示 + memset(buffer, 0, BUFFER_SIZE); + if (recv(client_socket, buffer, BUFFER_SIZE - 1, 0) <= 0) { + printf("Server disconnected.\n"); + running = 0; + return; + } + printf("%s", buffer); + + // 发送密码 + fgets(password, BUFFER_SIZE - 1, stdin); + password[strcspn(password, "\n")] = 0; // 移除换行符 + if (send(client_socket, password, strlen(password), 0) == -1) { + perror("Send failed"); + running = 0; + return; + } + + // 接收登录结果 + memset(buffer, 0, BUFFER_SIZE); + if (recv(client_socket, buffer, BUFFER_SIZE - 1, 0) <= 0) { + printf("Server disconnected.\n"); + running = 0; + return; + } + printf("%s", buffer); + + // 检查是否登录成功 + if (strstr(buffer, "Login successful") != NULL) { + // 接收帮助信息 + memset(buffer, 0, BUFFER_SIZE); + if (recv(client_socket, buffer, BUFFER_SIZE - 1, 0) > 0) { + printf("%s", buffer); + } + break; + } + } + // 处理注册 + else if (choice == 2) { + // 接收用户名提示 + memset(buffer, 0, BUFFER_SIZE); + if (recv(client_socket, buffer, BUFFER_SIZE - 1, 0) <= 0) { + printf("Server disconnected.\n"); + running = 0; + return; + } + printf("%s", buffer); + + // 发送用户名 + fgets(username, BUFFER_SIZE - 1, stdin); + username[strcspn(username, "\n")] = 0; // 移除换行符 + if (send(client_socket, username, strlen(username), 0) == -1) { + perror("Send failed"); + running = 0; + return; + } + + // 接收密码提示 + memset(buffer, 0, BUFFER_SIZE); + if (recv(client_socket, buffer, BUFFER_SIZE - 1, 0) <= 0) { + printf("Server disconnected.\n"); + running = 0; + return; + } + printf("%s", buffer); + + // 发送密码 + fgets(password, BUFFER_SIZE - 1, stdin); + password[strcspn(password, "\n")] = 0; // 移除换行符 + if (send(client_socket, password, strlen(password), 0) == -1) { + perror("Send failed"); + running = 0; + return; + } + + // 接收注册结果 + memset(buffer, 0, BUFFER_SIZE); + if (recv(client_socket, buffer, BUFFER_SIZE - 1, 0) <= 0) { + printf("Server disconnected.\n"); + running = 0; + return; + } + printf("%s", buffer); + } + else { + // 接收无效选择提示 + memset(buffer, 0, BUFFER_SIZE); + if (recv(client_socket, buffer, BUFFER_SIZE - 1, 0) <= 0) { + printf("Server disconnected.\n"); + running = 0; + return; + } + printf("%s", buffer); + } + } +} \ No newline at end of file diff --git a/Project/server b/Project/server new file mode 100644 index 0000000..10e3d80 Binary files /dev/null and b/Project/server differ diff --git a/Project/server.c b/Project/server.c new file mode 100644 index 0000000..c3702f5 --- /dev/null +++ b/Project/server.c @@ -0,0 +1,471 @@ +#include +#include +#include +#include +#include +#include +#include + +#define MAX_CLIENTS 10 +#define BUFFER_SIZE 1024 +#define USERNAME_LENGTH 50 +#define PASSWORD_LENGTH 50 +#define FILENAME "./users.txt" + +typedef struct { + int socket; + char username[USERNAME_LENGTH]; + pthread_t thread; + int active; +} Client; + +Client clients[MAX_CLIENTS]; +pthread_mutex_t clients_mutex = PTHREAD_MUTEX_INITIALIZER; +int server_socket; + +// 函数声明 +void handle_signal(int sig); +void *handle_client(void *arg); +int find_client_index(int socket); +int find_client_index_by_username(const char *username); +void send_private_message(const char *sender, const char *recipient, const char *message); +void list_users(int client_socket); +int authenticate_user(const char *username, const char *password); +int register_user(const char *username, const char *password); +void save_user(const char *username, const char *password); + +int main() { + int client_socket; + struct sockaddr_in server_addr, client_addr; + socklen_t client_addr_len = sizeof(client_addr); + + // 创建socket + server_socket = socket(AF_INET, SOCK_STREAM, 0); + if (server_socket == -1) { + perror("Socket creation failed"); + exit(EXIT_FAILURE); + } + + // 设置socket选项 + int opt = 1; + if (setsockopt(server_socket, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) == -1) { + perror("Setsockopt failed"); + exit(EXIT_FAILURE); + } + + // 准备服务器地址 + memset(&server_addr, 0, sizeof(server_addr)); + server_addr.sin_family = AF_INET; + server_addr.sin_addr.s_addr = INADDR_ANY; + server_addr.sin_port = htons(8888); + + // 绑定socket + if (bind(server_socket, (struct sockaddr *)&server_addr, sizeof(server_addr)) == -1) { + perror("Bind failed"); + exit(EXIT_FAILURE); + } + + // 监听连接 + if (listen(server_socket, 5) == -1) { + perror("Listen failed"); + exit(EXIT_FAILURE); + } + + printf("Server started. Waiting for connections...\n"); + + // 初始化客户端数组 + for (int i = 0; i < MAX_CLIENTS; i++) { + clients[i].socket = -1; + clients[i].active = 0; + } + + // 设置信号处理 + signal(SIGINT, handle_signal); + + // 接受客户端连接 + while (1) { + client_socket = accept(server_socket, (struct sockaddr *)&client_addr, &client_addr_len); + if (client_socket == -1) { + perror("Accept failed"); + continue; + } + + // 查找空闲位置 + int i; + for (i = 0; i < MAX_CLIENTS; i++) { + if (clients[i].socket == -1) { + pthread_mutex_lock(&clients_mutex); + clients[i].socket = client_socket; + clients[i].active = 1; + pthread_mutex_unlock(&clients_mutex); + + // 创建线程处理客户端 + if (pthread_create(&clients[i].thread, NULL, handle_client, &clients[i]) != 0) { + perror("Thread creation failed"); + pthread_mutex_lock(&clients_mutex); + clients[i].socket = -1; + clients[i].active = 0; + pthread_mutex_unlock(&clients_mutex); + close(client_socket); + } + pthread_detach(clients[i].thread); + break; + } + } + + if (i == MAX_CLIENTS) { + // 服务器已满 + char response[] = "Server is full. Try again later.\n"; + send(client_socket, response, strlen(response), 0); + close(client_socket); + } + } + + // 关闭服务器socket + close(server_socket); + return 0; +} + +void handle_signal(int sig) { + printf("\nShutting down server...\n"); + + // 关闭所有客户端socket + pthread_mutex_lock(&clients_mutex); + for (int i = 0; i < MAX_CLIENTS; i++) { + if (clients[i].socket != -1) { + close(clients[i].socket); + clients[i].socket = -1; + clients[i].active = 0; + } + } + pthread_mutex_unlock(&clients_mutex); + + // 关闭服务器socket + close(server_socket); + exit(EXIT_SUCCESS); +} + +void *handle_client(void *arg) { + Client *client = (Client *)arg; + char buffer[BUFFER_SIZE]; + int bytes_received; + + // 登录/注册流程 + while (1) { + // 发送菜单 + char menu[] = "1. Login\n2. Register\nEnter choice: "; + send(client->socket, menu, strlen(menu), 0); + + // 接收选择 + memset(buffer, 0, BUFFER_SIZE); + bytes_received = recv(client->socket, buffer, BUFFER_SIZE - 1, 0); + if (bytes_received <= 0) { + pthread_mutex_lock(&clients_mutex); + client->active = 0; + pthread_mutex_unlock(&clients_mutex); + close(client->socket); + pthread_exit(NULL); + } + + int choice = atoi(buffer); + + // 处理登录 + if (choice == 1) { + // 请求用户名 + char username_prompt[] = "Username: "; + send(client->socket, username_prompt, strlen(username_prompt), 0); + + // 接收用户名 + memset(buffer, 0, BUFFER_SIZE); + bytes_received = recv(client->socket, buffer, BUFFER_SIZE - 1, 0); + if (bytes_received <= 0) { + pthread_mutex_lock(&clients_mutex); + client->active = 0; + pthread_mutex_unlock(&clients_mutex); + close(client->socket); + pthread_exit(NULL); + } + char username[USERNAME_LENGTH]; + strncpy(username, buffer, USERNAME_LENGTH - 1); + + // 请求密码 + char password_prompt[] = "Password: "; + send(client->socket, password_prompt, strlen(password_prompt), 0); + + // 接收密码 + memset(buffer, 0, BUFFER_SIZE); + bytes_received = recv(client->socket, buffer, BUFFER_SIZE - 1, 0); + if (bytes_received <= 0) { + pthread_mutex_lock(&clients_mutex); + client->active = 0; + pthread_mutex_unlock(&clients_mutex); + close(client->socket); + pthread_exit(NULL); + } + char password[PASSWORD_LENGTH]; + strncpy(password, buffer, PASSWORD_LENGTH - 1); + + // 验证用户 + if (authenticate_user(username, password)) { + // 检查用户名是否已被使用 + int username_in_use = 0; + pthread_mutex_lock(&clients_mutex); + for (int i = 0; i < MAX_CLIENTS; i++) { + if (i != find_client_index(client->socket) && clients[i].active && + strcmp(clients[i].username, username) == 0) { + username_in_use = 1; + break; + } + } + pthread_mutex_unlock(&clients_mutex); + + if (username_in_use) { + char response[] = "Username already in use. Please try again.\n"; + send(client->socket, response, strlen(response), 0); + } else { + // 登录成功 + strcpy(client->username, username); + char success[] = "Login successful. Welcome to the chat!\n"; + send(client->socket, success, strlen(success), 0); + + // 通知用户可用命令 + char help[] = "Available commands:\n/private - Send private message\n/list - List online users\n/quit - Exit chat\n"; + send(client->socket, help, strlen(help), 0); + + break; + } + } else { + char response[] = "Authentication failed. Please try again.\n"; + send(client->socket, response, strlen(response), 0); + } + } + // 处理注册 + else if (choice == 2) { + // 请求用户名 + char username_prompt[] = "Enter username: "; + send(client->socket, username_prompt, strlen(username_prompt), 0); + + // 接收用户名 + memset(buffer, 0, BUFFER_SIZE); + bytes_received = recv(client->socket, buffer, BUFFER_SIZE - 1, 0); + if (bytes_received <= 0) { + pthread_mutex_lock(&clients_mutex); + client->active = 0; + pthread_mutex_unlock(&clients_mutex); + close(client->socket); + pthread_exit(NULL); + } + char username[USERNAME_LENGTH]; + strncpy(username, buffer, USERNAME_LENGTH - 1); + + // 请求密码 + char password_prompt[] = "Enter password: "; + send(client->socket, password_prompt, strlen(password_prompt), 0); + + // 接收密码 + memset(buffer, 0, BUFFER_SIZE); + bytes_received = recv(client->socket, buffer, BUFFER_SIZE - 1, 0); + if (bytes_received <= 0) { + pthread_mutex_lock(&clients_mutex); + client->active = 0; + pthread_mutex_unlock(&clients_mutex); + close(client->socket); + pthread_exit(NULL); + } + char password[PASSWORD_LENGTH]; + strncpy(password, buffer, PASSWORD_LENGTH - 1); + + // 注册用户 + if (register_user(username, password)) { + char success[] = "Registration successful. You can now login.\n"; + send(client->socket, success, strlen(success), 0); + } else { + char response[] = "Registration failed. Username already exists.\n"; + send(client->socket, response, strlen(response), 0); + } + } + else { + char response[] = "Invalid choice. Please try again.\n"; + send(client->socket, response, strlen(response), 0); + } + } + + // 聊天循环 + while (1) { + memset(buffer, 0, BUFFER_SIZE); + bytes_received = recv(client->socket, buffer, BUFFER_SIZE - 1, 0); + + if (bytes_received <= 0) { + // 客户端断开连接 + break; + } + + // 检查是否是退出命令 + if (strncmp(buffer, "/quit", 5) == 0) { + break; + } + + // 检查是否是列出用户命令 + if (strncmp(buffer, "/list", 5) == 0) { + list_users(client->socket); + continue; + } + + // 检查是否是私聊命令 + if (strncmp(buffer, "/private", 7) == 0) { + char recipient[USERNAME_LENGTH]; + char message[BUFFER_SIZE - USERNAME_LENGTH - 8]; + + // 解析命令 + sscanf(buffer, "/private %s %[^\n]", recipient, message); + + // 发送私聊消息 + send_private_message(client->username, recipient, message); + continue; + } + + // 发送帮助信息 + char help[] = "Invalid command. Available commands:\n/private - Send private message\n/list - List online users\n/quit - Exit chat\n"; + send(client->socket, help, strlen(help), 0); + } + + // 客户端断开连接 + char leave_message[BUFFER_SIZE]; + snprintf(leave_message, BUFFER_SIZE, "%s has left the chat.\n", client->username); + pthread_mutex_lock(&clients_mutex); + for (int i = 0; i < MAX_CLIENTS; i++) { + if (i != find_client_index(client->socket) && clients[i].active) { + send(clients[i].socket, leave_message, strlen(leave_message), 0); + } + } + pthread_mutex_unlock(&clients_mutex); + + // 关闭客户端socket + pthread_mutex_lock(&clients_mutex); + close(client->socket); + client->socket = -1; + client->active = 0; + pthread_mutex_unlock(&clients_mutex); + + pthread_exit(NULL); +} + +int find_client_index(int socket) { + for (int i = 0; i < MAX_CLIENTS; i++) { + if (clients[i].socket == socket) { + return i; + } + } + return -1; +} + +int find_client_index_by_username(const char *username) { + pthread_mutex_lock(&clients_mutex); + for (int i = 0; i < MAX_CLIENTS; i++) { + if (clients[i].active && strcmp(clients[i].username, username) == 0) { + pthread_mutex_unlock(&clients_mutex); + return i; + } + } + pthread_mutex_unlock(&clients_mutex); + return -1; +} + +void send_private_message(const char *sender, const char *recipient, const char *message) { + int recipient_index = find_client_index_by_username(recipient); + + if (recipient_index == -1) { + char error[BUFFER_SIZE]; + snprintf(error, BUFFER_SIZE, "Error: User '%s' not found or offline.\n", recipient); + + int sender_index = find_client_index_by_username(sender); + if (sender_index != -1) { + send(clients[sender_index].socket, error, strlen(error), 0); + } + return; + } + + // 发送消息给接收者 + char private_message[BUFFER_SIZE]; + snprintf(private_message, BUFFER_SIZE, "[Private from %s]: %s\n", sender, message); + send(clients[recipient_index].socket, private_message, strlen(private_message), 0); + + // 发送确认给发送者 + char confirmation[BUFFER_SIZE]; + snprintf(confirmation, BUFFER_SIZE, "[Private to %s]: %s\n", recipient, message); + int sender_index = find_client_index_by_username(sender); + if (sender_index != -1) { + send(clients[sender_index].socket, confirmation, strlen(confirmation), 0); + } +} + +void list_users(int client_socket) { + char response[BUFFER_SIZE] = "Online users:\n"; + + pthread_mutex_lock(&clients_mutex); + for (int i = 0; i < MAX_CLIENTS; i++) { + if (clients[i].active) { + strncat(response, clients[i].username, USERNAME_LENGTH - strlen(response) - 2); + strcat(response, "\n"); + } + } + pthread_mutex_unlock(&clients_mutex); + + send(client_socket, response, strlen(response), 0); +} + +int authenticate_user(const char *username, const char *password) { + FILE *file = fopen(FILENAME, "r"); + if (file == NULL) { + return 0; + } + + char stored_username[USERNAME_LENGTH]; + char stored_password[PASSWORD_LENGTH]; + int found = 0; + + while (fscanf(file, "%s %s", stored_username, stored_password) != EOF) { + if (strcmp(username, stored_username) == 0 && strcmp(password, stored_password) == 0) { + found = 1; + break; + } + } + + fclose(file); + return found; +} + +int register_user(const char *username, const char *password) { + FILE *file = fopen(FILENAME, "r"); + if (file == NULL) { + file = fopen(FILENAME, "w"); + if (file == NULL) { + return 0; + } + fclose(file); + file = fopen(FILENAME, "r"); + } + + char stored_username[USERNAME_LENGTH]; + while (fscanf(file, "%s", stored_username) != EOF) { + if (strcmp(username, stored_username) == 0) { + fclose(file); + return 0; // 用户名已存在 + } + // 跳过密码 + fscanf(file, "%*s"); + } + fclose(file); + + // 添加新用户 + save_user(username, password); + return 1; +} + +void save_user(const char *username, const char *password) { + FILE *file = fopen(FILENAME, "a"); + if (file != NULL) { + fprintf(file, "%s %s\n", username, password); + fclose(file); + } +} \ No newline at end of file diff --git a/Project/test/Client.c b/Project/test/Client.c new file mode 100644 index 0000000..b93060c --- /dev/null +++ b/Project/test/Client.c @@ -0,0 +1,422 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#define MAX_BUFFER 1024 +#define PORT 8888 +#define SERVER_IP "127.0.0.1" +#define MAX_FRIENDS 50 + +// 好友结构体 +typedef struct { + char username[50]; + int index; +} Friend; + +// 全局变量 +int client_socket; +int current_user_index = -1; +char current_username[50]; +pthread_t receive_thread; +Friend friends[MAX_FRIENDS]; +int friend_count = 0; + +// 函数声明 +void connect_to_server(); +void* receive_messages(void* arg); +void show_login_menu(); +void show_main_menu(); +void login(); +void register_user(); +void add_friend(); +void remove_friend(); +void chat_with_friend(); +void logout(); +void get_friends_list(); +void display_friends_list(); + +int main() { + connect_to_server(); + show_login_menu(); + return 0; +} + +void connect_to_server() { + struct sockaddr_in server_addr; + + // 创建socket + client_socket = socket(AF_INET, SOCK_STREAM, 0); + if (client_socket < 0) { + perror("创建Socket失败"); + exit(EXIT_FAILURE); + } + + // 设置服务器地址 + memset(&server_addr, 0, sizeof(server_addr)); + server_addr.sin_family = AF_INET; + server_addr.sin_port = htons(PORT); + server_addr.sin_addr.s_addr = inet_addr(SERVER_IP); + + // 连接服务器 + if (connect(client_socket, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0) { + perror("连接服务器失败"); + exit(EXIT_FAILURE); + } + + printf("成功连接到服务器!\n"); + // 不要在这里启动接收线程 +} + +void* receive_messages(void* arg) { + char buffer[MAX_BUFFER]; + while (1) { + memset(buffer, 0, MAX_BUFFER); + if (recv(client_socket, buffer, MAX_BUFFER, 0) <= 0) { + printf("\n连接已断开\n"); + current_user_index = -1; + close(client_socket); + connect_to_server(); + return NULL; + } + + // 处理PING消息 + if (strcmp(buffer, "PING") == 0) { + continue; + } + + // 处理好友列表消息 + if (strncmp(buffer, "FRIENDLIST", 10) == 0) { + friend_count = 0; // 重置好友计数 + char* token = strtok(buffer + 11, " "); + while (token != NULL && friend_count < MAX_FRIENDS) { + char* comma = strchr(token, ','); + if (comma != NULL) { + *comma = '\0'; + strncpy(friends[friend_count].username, token, sizeof(friends[friend_count].username)-1); + friends[friend_count].username[sizeof(friends[friend_count].username)-1] = '\0'; + friends[friend_count].index = atoi(comma + 1); + friend_count++; + } + token = strtok(NULL, " "); + } + printf("\n好友列表已更新\n"); + continue; + } + + // 处理好友操作响应 + if (strncmp(buffer, "FRIEND_ADD_SUCCESS", 17) == 0 || + strncmp(buffer, "FRIEND_REMOVE_SUCCESS", 20) == 0) { + printf("\n%s\n", buffer); + get_friends_list(); + continue; + } + + // 处理聊天消息 + if (strncmp(buffer, "CHAT", 4) == 0) { + printf("\n%s\n", buffer + 5); + } + // 处理系统消息 + else if (strncmp(buffer, "系统消息", 8) == 0 || + strncmp(buffer, "FRIEND_", 7) == 0) { + printf("\n%s\n", buffer); + } + // 处理其他消息 + else { + printf("\n%s\n", buffer); + } + + if (current_user_index != -1) { + printf("> "); + fflush(stdout); + } + } + return NULL; +} + +void show_login_menu() { + int choice; + while (1) { + printf("\n=== 登录菜单 ===\n"); + printf("1. 登录\n"); + printf("2. 注册\n"); + printf("3. 退出\n"); + printf("请选择: "); + scanf("%d", &choice); + getchar(); // 清除输入缓冲 + + switch (choice) { + case 1: + login(); + break; + case 2: + register_user(); + break; + case 3: + logout(); // 确保在退出前发送登出消息 + close(client_socket); + exit(EXIT_SUCCESS); + default: + printf("无效选择\n"); + } + } +} + +void show_main_menu() { + int choice; + while (current_user_index != -1) { // 只在用户已登录时显示菜单 + printf("\n=== 主菜单 ===\n"); + printf("1. 添加好友\n"); + printf("2. 删除好友\n"); + printf("3. 与好友聊天\n"); + printf("4. 查看好友列表\n"); + printf("5. 退出登录\n"); + printf("6. 退出程序\n"); + printf("请选择: "); + + if (scanf("%d", &choice) != 1) { + // 清除输入缓冲 + int c; + while ((c = getchar()) != '\n' && c != EOF); + printf("无效输入,请重新选择\n"); + continue; + } + getchar(); // 清除输入缓冲 + + switch (choice) { + case 1: + add_friend(); + break; + case 2: + remove_friend(); + break; + case 3: + chat_with_friend(); + break; + case 4: + get_friends_list(); + display_friends_list(); + break; + case 5: + logout(); + return; + case 6: + logout(); + close(client_socket); + exit(EXIT_SUCCESS); + default: + printf("无效选择\n"); + } + } +} + +void login() { + char username[50], password[50]; + char buffer[MAX_BUFFER]; + char response[MAX_BUFFER]; + + printf("用户名: "); + scanf("%s", username); + printf("密码: "); + scanf("%s", password); + getchar(); // 清除输入缓冲 + + snprintf(buffer, MAX_BUFFER, "LOGIN %s %s", username, password); + send(client_socket, buffer, MAX_BUFFER, 0); + + // 等待服务器响应 + memset(response, 0, MAX_BUFFER); + if (recv(client_socket, response, MAX_BUFFER, 0) <= 0) { + printf("登录失败:连接已断开\n"); + return; + } + + if (strncmp(response, "SUCCESS", 7) == 0) { + sscanf(response, "SUCCESS %d", ¤t_user_index); + strncpy(current_username, username, sizeof(current_username) - 1); + current_username[sizeof(current_username) - 1] = '\0'; + printf("登录成功!\n"); + // 登录成功后再启动接收线程 + if (pthread_create(&receive_thread, NULL, receive_messages, NULL) != 0) { + perror("创建线程失败"); + exit(EXIT_FAILURE); + } + get_friends_list(); + show_main_menu(); + } else { + printf("登录失败: %s\n", response + 5); + } +} + +void register_user() { + char username[50], password[50]; + char buffer[MAX_BUFFER]; + + printf("用户名: "); + scanf("%s", username); + printf("密码: "); + scanf("%s", password); + + snprintf(buffer, MAX_BUFFER, "REGISTER %s %s", username, password); + send(client_socket, buffer, MAX_BUFFER, 0); + + // 等待服务器响应 + memset(buffer, 0, MAX_BUFFER); + recv(client_socket, buffer, MAX_BUFFER, 0); + + if (strcmp(buffer, "SUCCESS") == 0) { + printf("注册成功!\n"); + } else { + printf("注册失败: %s\n", buffer + 5); + } +} + +void get_friends_list() { + char buffer[MAX_BUFFER]; + snprintf(buffer, MAX_BUFFER, "GETFRIENDS %d", current_user_index); + send(client_socket, buffer, MAX_BUFFER, 0); + // 等待接收线程处理好友列表 + usleep(100000); // 等待100ms +} + +void display_friends_list() { + printf("\n=== 好友列表 ===\n"); + if (friend_count == 0) { + printf("暂无好友\n"); + } else { + for (int i = 0; i < friend_count; i++) { + printf("%d. %s\n", i + 1, friends[i].username); + } + } + printf("----------------\n"); +} + +void chat_with_friend() { + char buffer[MAX_BUFFER]; + char message[MAX_BUFFER]; + int choice; + + get_friends_list(); + if (friend_count == 0) { + printf("您还没有好友,请先添加好友!\n"); + return; + } + + display_friends_list(); + printf("请选择要聊天的好友编号: "); + scanf("%d", &choice); + getchar(); // 清除输入缓冲 + + if (choice < 1 || choice > friend_count) { + printf("无效的选择!\n"); + return; + } + + int friend_index = friends[choice - 1].index; + printf("\n开始与 %s 聊天(输入'exit'退出):\n", friends[choice - 1].username); + printf("> "); + fflush(stdout); + + while (1) { + fgets(message, sizeof(message), stdin); + message[strcspn(message, "\n")] = 0; // 移除换行符 + // 限制 message 长度 + message[sizeof(message)-1] = '\0'; + if (strcmp(message, "exit") == 0) { + break; + } + int n = snprintf(buffer, MAX_BUFFER, "CHAT %d %d %s", current_user_index, friend_index, message); + if (n < 0 || n >= MAX_BUFFER) { + strcpy(&buffer[MAX_BUFFER-5], "..."); + } + send(client_socket, buffer, MAX_BUFFER, 0); + printf("> "); + fflush(stdout); + } +} + +void add_friend() { + char friend_name[50]; + char buffer[MAX_BUFFER]; + + printf("请输入要添加的好友用户名: "); + if (scanf("%49s", friend_name) != 1) { + getchar(); + printf("输入无效\n"); + return; + } + getchar(); + + snprintf(buffer, MAX_BUFFER, "ADDFRIEND %d %s", current_user_index, friend_name); + + // 设置发送超时 + struct timeval tv; + tv.tv_sec = 5; + tv.tv_usec = 0; + setsockopt(client_socket, SOL_SOCKET, SO_SNDTIMEO, (const char*)&tv, sizeof tv); + + if (send(client_socket, buffer, MAX_BUFFER, 0) <= 0) { + printf("发送请求失败\n"); + return; + } + + printf("添加好友请求已发送,等待服务器响应...\n"); +} + +void remove_friend() { + char buffer[MAX_BUFFER]; + + get_friends_list(); + usleep(100000); // 等待好友列表更新 + + if (friend_count == 0) { + printf("您还没有好友!\n"); + return; + } + + display_friends_list(); + printf("请输入要删除的好友编号: "); + int choice; + if (scanf("%d", &choice) != 1) { + getchar(); + printf("输入无效!\n"); + return; + } + getchar(); + + if (choice < 1 || choice > friend_count) { + printf("无效的选择!\n"); + return; + } + + // 设置发送超时 + struct timeval tv; + tv.tv_sec = 5; + tv.tv_usec = 0; + setsockopt(client_socket, SOL_SOCKET, SO_SNDTIMEO, (const char*)&tv, sizeof tv); + + snprintf(buffer, MAX_BUFFER, "REMOVEFRIEND %d %s", + current_user_index, + friends[choice - 1].username); + + if (send(client_socket, buffer, MAX_BUFFER, 0) <= 0) { + printf("发送请求失败\n"); + return; + } + + printf("删除好友请求已发送,等待服务器响应...\n"); +} + +void logout() { + if (current_user_index != -1) { + char buffer[MAX_BUFFER]; + snprintf(buffer, MAX_BUFFER, "LOGOUT"); + send(client_socket, buffer, MAX_BUFFER, 0); + current_user_index = -1; + printf("已退出登录\n"); + } +} \ No newline at end of file diff --git a/Project/test/Server.c b/Project/test/Server.c new file mode 100644 index 0000000..b764959 --- /dev/null +++ b/Project/test/Server.c @@ -0,0 +1,517 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MAX_BUFFER 1024 +#define MAX_USERS 100 +#define MAX_FRIENDS 50 +#define PORT 8888 + +// 用户结构体 +typedef struct { + char username[50]; + char password[50]; + int friends[MAX_FRIENDS]; + int friend_count; + int online; + int socket; // 添加socket字段 +} User; + +// 全局变量 +User users[MAX_USERS]; +int user_count = 0; +pthread_mutex_t users_mutex = PTHREAD_MUTEX_INITIALIZER; + +// 函数声明 +void load_users(); +void save_users(); +int find_user(const char* username); +void* handle_client(void* arg); +void process_login(int client_socket, char* buffer); +void process_register(int client_socket, char* buffer); +void process_add_friend(int client_socket, char* buffer); +void process_remove_friend(int client_socket, char* buffer); +void process_chat(int client_socket, char* buffer); +void display_online_users(); // 新增函数声明 +void process_get_friends(int client_socket, char* buffer); + +int main() { + int server_socket, client_socket; + struct sockaddr_in server_addr, client_addr; + socklen_t client_len = sizeof(client_addr); + pthread_t thread_id; + + // 加载用户数据 + load_users(); + + // 创建socket + server_socket = socket(AF_INET, SOCK_STREAM, 0); + if (server_socket < 0) { + perror("创建Socket失败"); + exit(EXIT_FAILURE); + } + + // 设置服务器地址 + memset(&server_addr, 0, sizeof(server_addr)); + server_addr.sin_family = AF_INET; + server_addr.sin_addr.s_addr = INADDR_ANY; + server_addr.sin_port = htons(PORT); + + // 绑定socket + if (bind(server_socket, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0) { + perror("绑定失败"); + exit(EXIT_FAILURE); + } + + // 监听连接 + if (listen(server_socket, 5) < 0) { + perror("监听失败"); + exit(EXIT_FAILURE); + } + + printf("服务器已启动,监听端口 %d\n", PORT); + + // 接受客户端连接 + while (1) { + client_socket = accept(server_socket, (struct sockaddr*)&client_addr, &client_len); + if (client_socket < 0) { + perror("接受连接失败"); + continue; + } + + printf("新客户端连接:%s:%d\n",inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port)); + + // 为每个客户端创建新线程 + if (pthread_create(&thread_id, NULL, handle_client, (void*)&client_socket) != 0) { + perror("创建线程失败"); + close(client_socket); + } + } + + close(server_socket); + return 0; +} + +void load_users() { + FILE* file = fopen("Users.txt", "r"); + if (file == NULL) { + printf("未找到用户文件,将创建新文件。\n"); + return; + } + + while (fscanf(file, "%s %s %d", users[user_count].username, + users[user_count].password, &users[user_count].friend_count) == 3) { + for (int i = 0; i < users[user_count].friend_count; i++) { + fscanf(file, "%d", &users[user_count].friends[i]); + } + users[user_count].online = 0; + users[user_count].socket = -1; + user_count++; + } + + fclose(file); +} + +void save_users() { + FILE* file = fopen("Users.txt", "w"); + if (file == NULL) { + perror("无法打开用户文件"); + return; + } + + for (int i = 0; i < user_count; i++) { + fprintf(file, "%s %s %d", users[i].username, + users[i].password, users[i].friend_count); + for (int j = 0; j < users[i].friend_count; j++) { + fprintf(file, " %d", users[i].friends[j]); + } + fprintf(file, "\n"); + } + + fclose(file); +} + +int find_user(const char* username) { + for (int i = 0; i < user_count; i++) { + if (strcmp(users[i].username, username) == 0) { + return i; + } + } + return -1; +} + +void* handle_client(void* arg) { + int client_socket = *((int*)arg); + char buffer[MAX_BUFFER]; + int user_index = -1; + + // 设置socket选项,启用保活机制 + int keepalive = 1; + int keepidle = 5; // 5秒没有数据交互就开始探测 + int keepinterval = 2; // 探测间隔2秒 + int keepcount = 3; // 探测3次无响应就断开 + + setsockopt(client_socket, SOL_SOCKET, SO_KEEPALIVE, &keepalive, sizeof(keepalive)); + setsockopt(client_socket, IPPROTO_TCP, TCP_KEEPIDLE, &keepidle, sizeof(keepidle)); + setsockopt(client_socket, IPPROTO_TCP, TCP_KEEPINTVL, &keepinterval, sizeof(keepinterval)); + setsockopt(client_socket, IPPROTO_TCP, TCP_KEEPCNT, &keepcount, sizeof(keepcount)); + + while (1) { + memset(buffer, 0, MAX_BUFFER); + int bytes_received = recv(client_socket, buffer, MAX_BUFFER, 0); + + // 检查连接状态 + if (bytes_received <= 0) { + pthread_mutex_lock(&users_mutex); + // 查找并更新断开连接的用户状态 + for (int i = 0; i < user_count; i++) { + if (users[i].socket == client_socket) { + user_index = i; + users[i].online = 0; + users[i].socket = -1; + printf("\n用户 %s 断开连接\n", users[i].username); + + // 通知所有在线好友 + for (int j = 0; j < users[i].friend_count; j++) { + int friend_index = users[i].friends[j]; + if (users[friend_index].online && users[friend_index].socket != -1) { + char notify[MAX_BUFFER]; + sprintf(notify, "系统消息: 您的好友 %s 已离线", users[i].username); + send(users[friend_index].socket, notify, MAX_BUFFER, 0); + } + } + break; + } + } + save_users(); + display_online_users(); + pthread_mutex_unlock(&users_mutex); + break; + } + + char command[20]; + sscanf(buffer, "%s", command); + + if (strcmp(command, "LOGIN") == 0) { + process_login(client_socket, buffer); + // 更新user_index + for (int i = 0; i < user_count; i++) { + if (users[i].socket == client_socket) { + user_index = i; + break; + } + } + } + else if (strcmp(command, "REGISTER") == 0) { + process_register(client_socket, buffer); + } + else if (strcmp(command, "ADDFRIEND") == 0) { + process_add_friend(client_socket, buffer); + } + else if (strcmp(command, "REMOVEFRIEND") == 0) { + process_remove_friend(client_socket, buffer); + } + else if (strcmp(command, "CHAT") == 0) { + process_chat(client_socket, buffer); + } + else if (strcmp(command, "LOGOUT") == 0) { + pthread_mutex_lock(&users_mutex); + // 查找当前用户 + for (int i = 0; i < user_count; i++) { + if (users[i].socket == client_socket) { + user_index = i; + break; + } + } + + if (user_index != -1) { + users[user_index].online = 0; + users[user_index].socket = -1; + // 通知所有在线好友 + for (int i = 0; i < users[user_index].friend_count; i++) { + int friend_index = users[user_index].friends[i]; + if (users[friend_index].online && users[friend_index].socket != -1) { + char notify[MAX_BUFFER]; + sprintf(notify, "系统消息: 您的好友 %s 已离线", users[user_index].username); + send(users[friend_index].socket, notify, MAX_BUFFER, 0); + } + } + save_users(); + printf("\n用户 %s 退出登录\n", users[user_index].username); + display_online_users(); + } + pthread_mutex_unlock(&users_mutex); + break; + } + else if (strcmp(command, "GETFRIENDS") == 0) { + process_get_friends(client_socket, buffer); + } + } + + // 确保在断开连接时清理用户状态 + if (user_index != -1) { + pthread_mutex_lock(&users_mutex); + users[user_index].online = 0; + users[user_index].socket = -1; + save_users(); + display_online_users(); + pthread_mutex_unlock(&users_mutex); + } + + close(client_socket); + return NULL; +} + +void process_login(int client_socket, char* buffer) { + char username[50], password[50]; + sscanf(buffer, "LOGIN %s %s", username, password); + + pthread_mutex_lock(&users_mutex); + int user_index = find_user(username); + + if (user_index == -1) { + send(client_socket, "FAIL 用户不存在", MAX_BUFFER, 0); + } + else if (strcmp(users[user_index].password, password) != 0) { + send(client_socket, "FAIL 密码错误", MAX_BUFFER, 0); + } + else { + // 如果用户已经在线,先将其标记为离线 + if (users[user_index].online) { + users[user_index].online = 0; + users[user_index].socket = -1; + } + + // 更新用户状态 + users[user_index].online = 1; + users[user_index].socket = client_socket; + save_users(); + + char response[MAX_BUFFER]; + snprintf(response, MAX_BUFFER, "SUCCESS %d", user_index); + send(client_socket, response, MAX_BUFFER, 0); + printf("\n用户 %s 登录成功\n", username); + + // 通知所有在线好友 + for (int i = 0; i < users[user_index].friend_count; i++) { + int friend_index = users[user_index].friends[i]; + if (users[friend_index].online && users[friend_index].socket != -1) { + char notify[MAX_BUFFER]; + snprintf(notify, MAX_BUFFER, "系统消息: 您的好友 %s 已上线", username); + send(users[friend_index].socket, notify, MAX_BUFFER, 0); + } + } + display_online_users(); + } + pthread_mutex_unlock(&users_mutex); +} + +void process_register(int client_socket, char* buffer) { + char username[50], password[50]; + sscanf(buffer, "REGISTER %s %s", username, password); + + pthread_mutex_lock(&users_mutex); + if (find_user(username) != -1) { + send(client_socket, "FAIL 用户名已存在", MAX_BUFFER, 0); + } + else if (user_count >= MAX_USERS) { + send(client_socket, "FAIL 用户数量已达上限", MAX_BUFFER, 0); + } + else { + strcpy(users[user_count].username, username); + strcpy(users[user_count].password, password); + users[user_count].friend_count = 0; + users[user_count].online = 0; + users[user_count].socket = -1; + user_count++; + save_users(); + send(client_socket, "SUCCESS", MAX_BUFFER, 0); + } + pthread_mutex_unlock(&users_mutex); +} + +void process_add_friend(int client_socket, char* buffer) { + int user_index; + char friend_name[50]; + sscanf(buffer, "ADDFRIEND %d %s", &user_index, friend_name); + + pthread_mutex_lock(&users_mutex); + int friend_index = find_user(friend_name); + + if (friend_index == -1) { + send(client_socket, "FAIL 好友不存在", MAX_BUFFER, 0); + } + else if (user_index == friend_index) { + send(client_socket, "FAIL 不能添加自己为好友", MAX_BUFFER, 0); + } + else { + // 检查是否已经是好友 + for (int i = 0; i < users[user_index].friend_count; i++) { + if (users[user_index].friends[i] == friend_index) { + send(client_socket, "FAIL 已经是好友关系", MAX_BUFFER, 0); + pthread_mutex_unlock(&users_mutex); + return; + } + } + + // 添加好友 + users[user_index].friends[users[user_index].friend_count++] = friend_index; + users[friend_index].friends[users[friend_index].friend_count++] = user_index; + save_users(); + send(client_socket, "SUCCESS", MAX_BUFFER, 0); + + // 通知被添加的用户 + if (users[friend_index].online && users[friend_index].socket != -1) { + char notify[MAX_BUFFER]; + snprintf(notify, MAX_BUFFER, "系统消息: %s 添加您为好友", users[user_index].username); + send(users[friend_index].socket, notify, MAX_BUFFER, 0); + } + } + pthread_mutex_unlock(&users_mutex); +} + +void process_remove_friend(int client_socket, char* buffer) { + int user_index; + char friend_name[50]; + sscanf(buffer, "REMOVEFRIEND %d %s", &user_index, friend_name); + + pthread_mutex_lock(&users_mutex); + int friend_index = find_user(friend_name); + + if (friend_index == -1) { + send(client_socket, "FAIL 好友不存在", MAX_BUFFER, 0); + pthread_mutex_unlock(&users_mutex); + return; + } + + int found = 0; + // 从用户的好友列表中移除 + for (int i = 0; i < users[user_index].friend_count; i++) { + if (users[user_index].friends[i] == friend_index) { + for (int j = i; j < users[user_index].friend_count - 1; j++) { + users[user_index].friends[j] = users[user_index].friends[j + 1]; + } + users[user_index].friend_count--; + found = 1; + break; + } + } + + if (found) { + // 从好友的好友列表中移除 + for (int i = 0; i < users[friend_index].friend_count; i++) { + if (users[friend_index].friends[i] == user_index) { + for (int j = i; j < users[friend_index].friend_count - 1; j++) { + users[friend_index].friends[j] = users[friend_index].friends[j + 1]; + } + users[friend_index].friend_count--; + break; + } + } + + save_users(); + + // 发送成功响应 + send(client_socket, "FRIEND_REMOVE_SUCCESS", MAX_BUFFER, 0); + + // 发送通知给被删除的好友 + if (users[friend_index].online && users[friend_index].socket != -1) { + char notify[MAX_BUFFER]; + snprintf(notify, MAX_BUFFER, "FRIEND_REMOVE %s 将您从好友列表中移除", + users[user_index].username); + send(users[friend_index].socket, notify, MAX_BUFFER, 0); + } + } else { + send(client_socket, "FAIL 不是好友关系", MAX_BUFFER, 0); + } + + pthread_mutex_unlock(&users_mutex); +} + +void process_chat(int client_socket, char* buffer) { + int user_index, friend_index; + char message[MAX_BUFFER]; + sscanf(buffer, "CHAT %d %d %[^\n]", &user_index, &friend_index, message); + pthread_mutex_lock(&users_mutex); + // 验证是否是好友关系 + int is_friend = 0; + for (int i = 0; i < users[user_index].friend_count; i++) { + if (users[user_index].friends[i] == friend_index) { + is_friend = 1; + break; + } + } + + if (!is_friend) { + send(client_socket, "FAIL 不是好友关系", MAX_BUFFER, 0); + } + else { + char response[MAX_BUFFER]; + // 限制 message 长度,防止溢出 + char safe_message[MAX_BUFFER/2]; + strncpy(safe_message, message, sizeof(safe_message) - 1); + safe_message[sizeof(safe_message) - 1] = '\0'; + int n = snprintf(response, MAX_BUFFER, "CHAT %s: %s", users[user_index].username, safe_message); + if (n < 0 || n >= MAX_BUFFER) { + // 被截断,补上 ... + strcpy(&response[MAX_BUFFER-5], "..."); + } + // 发送给发送者 + send(client_socket, response, MAX_BUFFER, 0); + // 发送给接收者 + if (users[friend_index].online && users[friend_index].socket != -1) { + send(users[friend_index].socket, response, MAX_BUFFER, 0); + } else { + // 好友不在线,给发送者提示 + send(client_socket, "系统消息: 对方不在线,消息未送达", MAX_BUFFER, 0); + } + } + pthread_mutex_unlock(&users_mutex); +} + +// 新增函数:显示在线用户 +void display_online_users() { + printf("\n当前在线用户:\n"); + int online_count = 0; + for (int i = 0; i < user_count; i++) { + if (users[i].online) { + printf("%d. %s\n", ++online_count, users[i].username); + } + } + if (online_count == 0) { + printf("暂无在线用户\n"); + } + printf("------------------------\n"); +} + +// 新增函数:处理获取好友列表请求 +void process_get_friends(int client_socket, char* buffer) { + int user_index; + sscanf(buffer, "GETFRIENDS %d", &user_index); + + pthread_mutex_lock(&users_mutex); + char response[MAX_BUFFER] = "FRIENDLIST "; + char temp[64]; + + for (int i = 0; i < users[user_index].friend_count; i++) { + int friend_index = users[user_index].friends[i]; + int n = snprintf(temp, sizeof(temp), "%s,%d ", users[friend_index].username, friend_index); + if (n < 0 || n >= sizeof(temp)) { + strcpy(&temp[sizeof(temp)-5], "..."); + } + if (strlen(response) + strlen(temp) < MAX_BUFFER) { + strcat(response, temp); + } else { + strcat(response, "..."); + break; + } + } + send(client_socket, response, MAX_BUFFER, 0); + pthread_mutex_unlock(&users_mutex); +} \ No newline at end of file diff --git a/Project/test/client b/Project/test/client new file mode 100644 index 0000000..1aa97e6 Binary files /dev/null and b/Project/test/client differ diff --git a/Project/test/server b/Project/test/server new file mode 100644 index 0000000..8c18d30 Binary files /dev/null and b/Project/test/server differ diff --git a/Project/users.txt b/Project/users.txt new file mode 100644 index 0000000..7edc84d --- /dev/null +++ b/Project/users.txt @@ -0,0 +1 @@ +test password diff --git a/Project/新建文件夹/Client_Cursor.c b/Project/新建文件夹/Client_Cursor.c new file mode 100644 index 0000000..bcdd5a0 --- /dev/null +++ b/Project/新建文件夹/Client_Cursor.c @@ -0,0 +1,108 @@ +// client.c +#include +#include +#include +#include +#include +#include +#include "protocol.h" + +#define SERVER_IP "127.0.0.1" +#define PORT 8888 + +int sockfd; +char username[MAX_NAME_LEN]; + +// 接收消息线程 +void *recv_thread(void *arg) { + Message msg; + while (1) { + int n = recv(sockfd, &msg, sizeof(msg), 0); + if (n <= 0) break; + if (msg.type == MSG_PRIVATE_CHAT) { + printf("\n[私聊]%s: %s\n", msg.from, msg.data); + printf(">> "); + fflush(stdout); + } + } + return NULL; +} + +void menu() { + printf("1. 注册\n2. 登录\n选择: "); +} + +void chat_menu() { + printf("1. 添加好友\n2. 私聊\n3. 退出\n选择: "); +} + +int main() { + sockfd = socket(AF_INET, SOCK_STREAM, 0); + struct sockaddr_in servaddr = {0}; + servaddr.sin_family = AF_INET; + servaddr.sin_port = htons(PORT); + inet_pton(AF_INET, SERVER_IP, &servaddr.sin_addr); + connect(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr)); + + int choice; + Message msg, reply; + while (1) { + menu(); + scanf("%d", &choice); + getchar(); + memset(&msg, 0, sizeof(msg)); + if (choice == 1) { + msg.type = MSG_REGISTER; + printf("用户名: "); fgets(msg.from, MAX_NAME_LEN, stdin); msg.from[strcspn(msg.from, "\n")] = 0; + printf("密码: "); fgets(msg.data, MAX_MSG_LEN, stdin); msg.data[strcspn(msg.data, "\n")] = 0; + send(sockfd, &msg, sizeof(msg), 0); + recv(sockfd, &reply, sizeof(reply), 0); + if (reply.type == MSG_OK) printf("注册成功\n"); + else printf("注册失败: %s\n", reply.data); + } else if (choice == 2) { + msg.type = MSG_LOGIN; + printf("用户名: "); fgets(msg.from, MAX_NAME_LEN, stdin); msg.from[strcspn(msg.from, "\n")] = 0; + printf("密码: "); fgets(msg.data, MAX_MSG_LEN, stdin); msg.data[strcspn(msg.data, "\n")] = 0; + send(sockfd, &msg, sizeof(msg), 0); + recv(sockfd, &reply, sizeof(reply), 0); + if (reply.type == MSG_OK) { + strcpy(username, msg.from); + printf("登录成功\n"); + break; + } else { + printf("登录失败: %s\n", reply.data); + } + } + } + + pthread_t tid; + pthread_create(&tid, NULL, recv_thread, NULL); + + while (1) { + chat_menu(); + scanf("%d", &choice); + getchar(); + if (choice == 1) { + msg.type = MSG_ADD_FRIEND; + strcpy(msg.from, username); + printf("好友用户名: "); fgets(msg.to, MAX_NAME_LEN, stdin); msg.to[strcspn(msg.to, "\n")] = 0; + send(sockfd, &msg, sizeof(msg), 0); + recv(sockfd, &reply, sizeof(reply), 0); + if (reply.type == MSG_OK) printf("添加好友成功\n"); + else printf("添加失败: %s\n", reply.data); + } else if (choice == 2) { + msg.type = MSG_PRIVATE_CHAT; + strcpy(msg.from, username); + printf("好友用户名: "); fgets(msg.to, MAX_NAME_LEN, stdin); msg.to[strcspn(msg.to, "\n")] = 0; + printf("消息内容: "); fgets(msg.data, MAX_MSG_LEN, stdin); msg.data[strcspn(msg.data, "\n")] = 0; + send(sockfd, &msg, sizeof(msg), 0); + recv(sockfd, &reply, sizeof(reply), 0); + if (reply.type == MSG_OK) printf("消息已发送\n"); + else printf("发送失败: %s\n", reply.data); + } else if (choice == 3) { + break; + } + } + close(sockfd); + return 0; +} \ No newline at end of file diff --git a/Project/新建文件夹/Server_Cursor.c b/Project/新建文件夹/Server_Cursor.c new file mode 100644 index 0000000..9835825 --- /dev/null +++ b/Project/新建文件夹/Server_Cursor.c @@ -0,0 +1,138 @@ +// server.c +#include +#include +#include +#include +#include +#include +#include "protocol.h" +#include "user.h" + +#define PORT 8888 +#define MAX_USERS 100 +#define MAX_CLIENTS 100 + +typedef struct { + int sockfd; + char username[MAX_NAME_LEN]; +} Client; + +User users[MAX_USERS]; +int user_count = 0; +Client clients[MAX_CLIENTS]; +int client_count = 0; +pthread_mutex_t user_mutex = PTHREAD_MUTEX_INITIALIZER; +pthread_mutex_t client_mutex = PTHREAD_MUTEX_INITIALIZER; + +// 查找在线客户端 +int find_client(const char *username) { + for (int i = 0; i < client_count; i++) { + if (strcmp(clients[i].username, username) == 0) + return i; + } + return -1; +} + +// 处理客户端请求 +void *client_handler(void *arg) { + int sockfd = *(int *)arg; + char curr_user[MAX_NAME_LEN] = ""; + Message msg, reply; + while (1) { + int n = recv(sockfd, &msg, sizeof(msg), 0); + if (n <= 0) break; + memset(&reply, 0, sizeof(reply)); + switch (msg.type) { + case MSG_REGISTER: + pthread_mutex_lock(&user_mutex); + if (add_user(users, &user_count, msg.from, msg.data)) { + save_users(users, user_count); + reply.type = MSG_OK; + } else { + reply.type = MSG_FAIL; + strcpy(reply.data, "用户名已存在"); + } + pthread_mutex_unlock(&user_mutex); + send(sockfd, &reply, sizeof(reply), 0); + break; + case MSG_LOGIN: + pthread_mutex_lock(&user_mutex); + if (check_password(users, user_count, msg.from, msg.data)) { + strcpy(curr_user, msg.from); + pthread_mutex_lock(&client_mutex); + strcpy(clients[client_count].username, curr_user); + clients[client_count++].sockfd = sockfd; + pthread_mutex_unlock(&client_mutex); + reply.type = MSG_OK; + } else { + reply.type = MSG_FAIL; + strcpy(reply.data, "用户名或密码错误"); + } + pthread_mutex_unlock(&user_mutex); + send(sockfd, &reply, sizeof(reply), 0); + break; + case MSG_ADD_FRIEND: + pthread_mutex_lock(&user_mutex); + if (add_friend(users, user_count, curr_user, msg.to)) { + save_users(users, user_count); + reply.type = MSG_OK; + } else { + reply.type = MSG_FAIL; + strcpy(reply.data, "添加好友失败"); + } + pthread_mutex_unlock(&user_mutex); + send(sockfd, &reply, sizeof(reply), 0); + break; + case MSG_PRIVATE_CHAT: + pthread_mutex_lock(&client_mutex); + int idx = find_client(msg.to); + if (idx != -1) { + send(clients[idx].sockfd, &msg, sizeof(msg), 0); + reply.type = MSG_OK; + } else { + reply.type = MSG_FAIL; + strcpy(reply.data, "对方不在线"); + } + pthread_mutex_unlock(&client_mutex); + send(sockfd, &reply, sizeof(reply), 0); + break; + default: + break; + } + } + // 客户端断开,移除 + pthread_mutex_lock(&client_mutex); + for (int i = 0; i < client_count; i++) { + if (clients[i].sockfd == sockfd) { + clients[i] = clients[--client_count]; + break; + } + } + pthread_mutex_unlock(&client_mutex); + close(sockfd); + return NULL; +} + +int main() { + int listenfd = socket(AF_INET, SOCK_STREAM, 0); + struct sockaddr_in servaddr = {0}; + servaddr.sin_family = AF_INET; + servaddr.sin_addr.s_addr = INADDR_ANY; + servaddr.sin_port = htons(PORT); + bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr)); + listen(listenfd, 5); + + user_count = load_users(users, MAX_USERS); + printf("服务器启动,监听端口%d\n", PORT); + + while (1) { + struct sockaddr_in cliaddr; + socklen_t len = sizeof(cliaddr); + int connfd = accept(listenfd, (struct sockaddr *)&cliaddr, &len); + pthread_t tid; + pthread_create(&tid, NULL, client_handler, &connfd); + pthread_detach(tid); + } + close(listenfd); + return 0; +} \ No newline at end of file diff --git a/Project/新建文件夹/User.c b/Project/新建文件夹/User.c new file mode 100644 index 0000000..2fc10bf --- /dev/null +++ b/Project/新建文件夹/User.c @@ -0,0 +1,94 @@ +// user.c +#include "user.h" +#include +#include + +#define USER_FILE "users.txt" +#define MAX_LINE 256 + +// 加载所有用户 +int load_users(User users[], int max_users) { + FILE *fp = fopen(USER_FILE, "r"); + if (!fp) return 0; + int count = 0; + char line[MAX_LINE]; + while (fgets(line, sizeof(line), fp) && count < max_users) { + User *u = &users[count]; + char *p = strtok(line, " \n"); + if (!p) continue; + strcpy(u->username, p); + p = strtok(NULL, " \n"); + if (!p) continue; + strcpy(u->password, p); + u->friend_count = 0; + p = strtok(NULL, " \n"); + if (p) { + char *f = strtok(p, ","); + while (f && u->friend_count < MAX_FRIENDS) { + strcpy(u->friends[u->friend_count++], f); + f = strtok(NULL, ","); + } + } + count++; + } + fclose(fp); + return count; +} + +// 保存所有用户 +int save_users(User users[], int user_count) { + FILE *fp = fopen(USER_FILE, "w"); + if (!fp) return -1; + for (int i = 0; i < user_count; i++) { + fprintf(fp, "%s %s", users[i].username, users[i].password); + for (int j = 0; j < users[i].friend_count; j++) { + fprintf(fp, "%c%s", j == 0 ? ' ' : ',', users[i].friends[j]); + } + fprintf(fp, "\n"); + } + fclose(fp); + return 0; +} + +// 查找用户 +int find_user(User users[], int user_count, const char *username) { + for (int i = 0; i < user_count; i++) { + if (strcmp(users[i].username, username) == 0) + return i; + } + return -1; +} + +// 检查密码 +int check_password(User users[], int user_count, const char *username, const char *password) { + int idx = find_user(users, user_count, username); + if (idx == -1) return 0; + return strcmp(users[idx].password, password) == 0; +} + +// 添加用户 +int add_user(User users[], int *user_count, const char *username, const char *password) { + if (find_user(users, *user_count, username) != -1) return 0; + User *u = &users[(*user_count)++]; + strcpy(u->username, username); + strcpy(u->password, password); + u->friend_count = 0; + return 1; +} + +// 添加好友 +int add_friend(User users[], int user_count, const char *username, const char *friendname) { + int idx = find_user(users, user_count, username); + int fidx = find_user(users, user_count, friendname); + if (idx == -1 || fidx == -1) return 0; + User *u = &users[idx]; + for (int i = 0; i < u->friend_count; i++) { + if (strcmp(u->friends[i], friendname) == 0) + return 0; // 已是好友 + } + if (u->friend_count < MAX_FRIENDS) { + strcpy(u->friends[u->friend_count++], friendname); + return 1; + } + return 0; +} \ No newline at end of file diff --git a/Project/新建文件夹/User.h b/Project/新建文件夹/User.h new file mode 100644 index 0000000..42e8aa1 --- /dev/null +++ b/Project/新建文件夹/User.h @@ -0,0 +1,21 @@ +// user.h +#ifndef USER_H +#define USER_H + +#define MAX_FRIENDS 10 + +typedef struct { + char username[32]; + char password[32]; + char friends[MAX_FRIENDS][32]; + int friend_count; +} User; + +int load_users(User users[], int max_users); +int save_users(User users[], int user_count); +int find_user(User users[], int user_count, const char *username); +int check_password(User users[], int user_count, const char *username, const char *password); +int add_user(User users[], int *user_count, const char *username, const char *password); +int add_friend(User users[], int user_count, const char *username, const char *friendname); + +#endif \ No newline at end of file diff --git a/Project/新建文件夹/protocol.h b/Project/新建文件夹/protocol.h new file mode 100644 index 0000000..ef0ce70 --- /dev/null +++ b/Project/新建文件夹/protocol.h @@ -0,0 +1,22 @@ +// protocol.h +#ifndef PROTOCOL_H +#define PROTOCOL_H + +#define MSG_REGISTER 1 +#define MSG_LOGIN 2 +#define MSG_ADD_FRIEND 3 +#define MSG_PRIVATE_CHAT 4 +#define MSG_OK 100 +#define MSG_FAIL 101 + +#define MAX_NAME_LEN 32 +#define MAX_MSG_LEN 256 + +typedef struct { + int type; // 消息类型 + char from[MAX_NAME_LEN]; + char to[MAX_NAME_LEN]; + char data[MAX_MSG_LEN]; +} Message; + +#endif \ No newline at end of file diff --git a/Project/新建文件夹/users.txt b/Project/新建文件夹/users.txt new file mode 100644 index 0000000..7edc84d --- /dev/null +++ b/Project/新建文件夹/users.txt @@ -0,0 +1 @@ +test password