【server.c】
/* 服务器.c */
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <pthread.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <sys/epoll.h>
#include <sqlite3.h>
typedef struct sockaddr_in addr_in_t;
typedef struct sockaddr addr_t;
enum Type {
TYPE_REGIST,
TYPE_LOGIN,
TYPE_CHAT,
TYPE_FILE_UPLOAD_REQUEST
};
typedef struct Pack {
enum Type type;
char name[20];
char pswd[20];
char filename[20];
long filesize;
char tarname[20];
char text[1024];
} pack_t;
typedef struct User {
char name[20];
int sock;
int hasMsg;
char msg[1024];
} user_t;
user_t user_arr[50] = {0};
int user_len = 0;
sqlite3 *db;
void read_data(int client);
void insert_user(user_t user);
int find_user(const char* username);
int main(int argc, const char *argv[]) {
if (argc != 2) {
printf("请输入端口号\n");
return 1;
}
// 初始化数据库
int rc = sqlite3_open("users.db", &db);
if (rc != SQLITE_OK) {
fprintf(stderr, "无法打开数据库: %s\n", sqlite3_errmsg(db));
return 1;
}
// 创建用户表
const char *sql = "CREATE TABLE IF NOT EXISTS users ("
"username TEXT PRIMARY KEY, "
"password TEXT NOT NULL);";
char *err_msg = NULL;
rc = sqlite3_exec(db, sql, 0, 0, &err_msg);
if (rc != SQLITE_OK) {
fprintf(stderr, "数据库错误: %s\n", err_msg);
sqlite3_free(err_msg);
sqlite3_close(db);
return 1;
}
int port = atoi(argv[1]);
int server = socket(AF_INET, SOCK_STREAM, 0);
addr_in_t addr = {0};
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
addr.sin_addr.s_addr = inet_addr("0.0.0.0");
if (bind(server, (addr_t*)&addr, sizeof(addr)) == -1) {
perror("bind");
return 1;
}
listen(server, 10);
int epfd = epoll_create1(EPOLL_CLOEXEC);
struct epoll_event epoll_stdin = { .events = EPOLLIN, .data.fd = 0 };
struct epoll_event epoll_server = { .events = EPOLLIN, .data.fd = server };
epoll_ctl(epfd, EPOLL_CTL_ADD, 0, &epoll_stdin);
epoll_ctl(epfd, EPOLL_CTL_ADD, server, &epoll_server);
struct epoll_event arr[50] = {0};
while (1) {
int len = epoll_wait(epfd, arr, 50, -1);
for (int i = 0; i < len; i++) {
int fd = arr[i].data.fd;
if (fd == server) {
int client = accept(server, 0, 0);
struct epoll_event epoll_client = { .events = EPOLLIN, .data.fd = client };
epoll_ctl(epfd, EPOLL_CTL_ADD, client, &epoll_client);
} else if (fd == 0) {
char buf[64] = "";
scanf("%63s", buf);
while (getchar() != '\n');
printf("键盘输入数据: %s\n", buf);
} else {
read_data(fd);
}
}
}
sqlite3_close(db);
return 0;
}
void insert_user(user_t user) {
user_arr[user_len] = user;
user_len++;
}
int find_user(const char* username) {
for (int i = 0; i < user_len; i++) {
if (strcmp(username, user_arr[i].name) == 0) {
return i;
}
}
return -1;
}
void read_data(int client) {
pack_t pack = {0};
int res = read(client, &pack, sizeof(pack));
if (res <= 0) {
close(client);
// 从在线列表中移除用户
for (int i = 0; i < user_len; i++) {
if (user_arr[i].sock == client) {
user_arr[i] = user_arr[user_len - 1];
user_len--;
break;
}
}
return;
}
switch (pack.type) {
case TYPE_REGIST: {
sqlite3_stmt *stmt;
const char *sql = "INSERT INTO users (username, password) VALUES (?, ?);";
int rc = sqlite3_prepare_v2(db, sql, -1, &stmt, NULL);
if (rc != SQLITE_OK) {
strcpy(pack.text, "注册失败");
} else {
sqlite3_bind_text(stmt, 1, pack.name, -1, SQLITE_STATIC);
sqlite3_bind_text(stmt, 2, pack.pswd, -1, SQLITE_STATIC);
rc = sqlite3_step(stmt);
if (rc == SQLITE_DONE) {
strcpy(pack.text, "注册成功");
} else {
strcpy(pack.text, "该账号已存在");
}
sqlite3_finalize(stmt);
}
write(client, &pack, sizeof(pack));
break;
}
case TYPE_LOGIN: {
sqlite3_stmt *stmt;
const char *sql = "SELECT password FROM users WHERE username = ?;";
int rc = sqlite3_prepare_v2(db, sql, -1, &stmt, NULL);
if (rc != SQLITE_OK) {
strcpy(pack.text, "登录失败");
} else {
sqlite3_bind_text(stmt, 1, pack.name, -1, SQLITE_STATIC);
rc = sqlite3_step(stmt);
if (rc == SQLITE_ROW) {
const char *stored_pswd = (const char*)sqlite3_column_text(stmt, 0);
if (strcmp(stored_pswd, pack.pswd) == 0) {
user_t user = {0};
strcpy(user.name, pack.name);
user.sock = client;
int idx = find_user(pack.name);
if (idx == -1) {
insert_user(user);
} else {
user_arr[idx].sock = client;
}
strcpy(pack.text, "登录成功");
} else {
strcpy(pack.text, "密码错误");
}
} else {
strcpy(pack.text, "该账号不存在");
}
sqlite3_finalize(stmt);
}
write(client, &pack, sizeof(pack));
break;
}
case TYPE_CHAT: {
int idx = find_user(pack.tarname);
if (idx == -1) {
strcpy(pack.text, "该用户不存在");
write(client, &pack, sizeof(pack));
} else {
user_t user = user_arr[idx];
if (user.sock == 0) {
user_arr[idx].hasMsg = 1;
strcpy(user_arr[idx].msg, pack.text);
} else {
write(user.sock, &pack, sizeof(pack));
}
}
break;
}
case TYPE_FILE_UPLOAD_REQUEST: {
int idx = find_user(pack.tarname);
if (idx == -1) {
strcpy(pack.text, "用户不存在");
write(client, &pack, sizeof(pack));
} else {
user_t user = user_arr[idx];
if (user.sock == 0) {
user_arr[idx].hasMsg = 1;
strcpy(user_arr[idx].msg, "用户不在线,文件上传请求已保存");
strcpy(pack.text, "用户不在线,文件上传请求已保存");
write(client, &pack, sizeof(pack));
} else {
write(user.sock, &pack, sizeof(pack));
}
}
break;
}
}
}
【client.c】
/* 客户端.c */
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <pthread.h>
#include <sys/socket.h>
#include <arpa/inet.h>
typedef struct sockaddr_in addr_in_t;
typedef struct sockaddr addr_t;
enum Type {
TYPE_REGIST,
TYPE_LOGIN,
TYPE_CHAT,
TYPE_FILE_UPLOAD_REQUEST
};
typedef struct Pack {
enum Type type;
char name[20];
char pswd[20];
char filename[20];
long filesize;
char tarname[20];
char text[1024];
} pack_t;
void* thread_main(void* arg) {
int client = *(int*)arg;
while (1) {
pack_t pack = {0};
int res = read(client, &pack, sizeof(pack));
if (res == 0) break;
switch (pack.type) {
case TYPE_REGIST:
case TYPE_LOGIN:
printf("%s\n", pack.text);
break;
case TYPE_CHAT:
printf("接收到消息: %s\n", pack.text);
break;
case TYPE_FILE_UPLOAD_REQUEST: {
char filename[128] = "./client_file_system/";
strcat(filename, pack.filename);
int fd = open(filename, O_CREAT | O_TRUNC | O_WRONLY, 0666);
long filesize = pack.filesize;
long readed_size = 0;
while (1) {
pack_t filepack = {0};
res = read(client, &filepack, sizeof(filepack));
if (res != sizeof(filepack)) {
printf("发生分包\n");
}
int size = strlen(filepack.text);
write(fd, filepack.text, size);
readed_size += size;
if (readed_size >= filesize) {
close(fd);
break;
}
}
break;
}
}
}
return NULL;
}
int main(int argc, const char *argv[]) {
if (argc != 2) {
printf("请输入端口号\n");
return 1;
}
int port = atoi(argv[1]);
int client = socket(AF_INET, SOCK_STREAM, 0);
addr_in_t addr = {0};
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
addr.sin_addr.s_addr = inet_addr("127.0.0.1");
if (connect(client, (addr_t*)&addr, sizeof(addr)) == -1) {
perror("connect");
return 1;
}
pthread_t id;
pthread_create(&id, 0, thread_main, &client);
pthread_detach(id);
while (1) {
int ch = -1;
printf("1: 注册\n2: 登录\n3: 聊天\n4: 发送文件\n0: 退出\n请选择: ");
scanf("%d", &ch);
while (getchar() != '\n');
pack_t pack = {0};
switch (ch) {
case 1:
printf("请输入账号: ");
scanf("%19s", pack.name);
while (getchar() != '\n');
printf("请输入密码: ");
scanf("%19s", pack.pswd);
while (getchar() != '\n');
pack.type = TYPE_REGIST;
write(client, &pack, sizeof(pack));
break;
case 2:
printf("请输入账号: ");
scanf("%19s", pack.name);
while (getchar() != '\n');
printf("请输入密码: ");
scanf("%19s", pack.pswd);
while (getchar() != '\n');
pack.type = TYPE_LOGIN;
write(client, &pack, sizeof(pack));
break;
case 3:
printf("请输入目标用户名和消息: ");
scanf("%19s %1023s", pack.tarname, pack.text);
while (getchar() != '\n');
pack.type = TYPE_CHAT;
write(client, &pack, sizeof(pack));
break;
case 4:
printf("请输入接收文件的用户名: ");
scanf("%19s", pack.tarname);
while (getchar() != '\n');
printf("请输入想要发送的文件名: ");
scanf("%19s", pack.filename);
while (getchar() != '\n');
int fd = open(pack.filename, O_RDONLY);
if (fd == -1) {
printf("该文件不存在\n");
break;
}
struct stat buf;
stat(pack.filename, &buf);
pack.filesize = buf.st_size;
pack.type = TYPE_FILE_UPLOAD_REQUEST;
write(client, &pack, sizeof(pack));
while (1) {
pack_t filepack = {0};
filepack.type = TYPE_FILE_UPLOAD_REQUEST;
strcpy(filepack.tarname, pack.tarname);
int res = read(fd, filepack.text, 1023);
if (res == 0) break;
write(client, &filepack, sizeof(filepack));
}
close(fd);
break;
case 0:
close(client);
return 0;
default:
printf("无效选项\n");
break;
}
}
return 0;
}