项目要求:
(1)采用 Client/Server 架构 。
(2) Client A 登陆聊天服务器前,需要注册自己的 ID 和密码。
(3)注册成功后,ClientA 就可以通过自己的 ID 和密码登陆聊天服务器 。
(4). 多个 Client X 可以同时登陆聊天服务器之后,与其他用户进行通讯聊天。
(5).Client A 成功登陆后可以查看当前聊天室内其他在线用户 Client x。
(6). Client A 可以选择发消息给某个特定的 Client X,即”悄悄话”功能。
(7). Client A 可以选择发消息全部的在线用户,即”群发消息”功能。
(8). Client A 在退出时需要保存聊天记录。
(9). Server 端维护一个所有登陆用户的聊天会的记录文件,以便备查。
1、服务器
#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <arpa/inet.h>
#include <pthread.h>
#include <sqlite3.h>
#define PORT 9999
struct User //用户信息结构体
{
char name[20]; //储存用户名
char password[20]; //储存密码
char signature[50]; //储存个性签名
int socket; //储存套接字
int cmd; // 消息类型
char msg[1024]; // 消息内容
char toname[20];
char fromname[20];
char logout;
char file_name[20];
char file_msg[1024];
};
struct User user[5];
sqlite3 * database;
sqlite3 * server_sqlite() //数据库函数
{
sqlite3 * database;
// 打开数据库
int ret = sqlite3_open("usermes.db", &database);
if (ret != SQLITE_OK)
{
printf ("错误代码\n");
perror("sqlite3_open");
return NULL;
}
char *errmsg = NULL;
char *sql = "create table if not exists usermes(name TEXT, password TEXT, signature TEXT, primary key(name))";
ret = sqlite3_exec(database, sql, NULL, NULL, &errmsg);
if (ret != SQLITE_OK)
{
printf ("数据库操作失败:%s\n", errmsg);
return NULL;
}
return database;
}
int init_socket() //套接字初始化
{
int listen_socket = socket (AF_INET, SOCK_STREAM, 0);
if(listen_socket == -1)
{
printf("错误代码55\n");
perror("socket");
return -1;
}
struct sockaddr_in addr;
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET; // 设置地址族
addr.sin_port = htons(PORT); // 设置本地端口
addr.sin_addr.s_addr = htonl(INADDR_ANY); // 使用本地的任意IP地址
int ret = bind(listen_socket, (struct sockaddr *)&addr, sizeof(addr)); //绑定套接字
if (ret == -1)
{
printf("错误代码69\n");
perror ("bind");
return -1;
}
// 3、监听本地套接字
ret = listen(listen_socket, 5);
if (ret == -1)
{
printf("错误代码78\n");
perror ("listen");
return -1;
}
printf("等待客户连接。。。。\n");
return listen_socket;
}
int MyAccept(int listen_socket) //连接客户端
{
struct sockaddr_in client_addr; // 用来保存客户端的ip和端口信息
int len = sizeof(client_addr);
int client_socket = accept(listen_socket, (struct sockaddr *)&client_addr, &len);
if (client_socket == -1)
{
printf("错误代码93\n");
perror ("accept");
}
printf ("成功接收一个客户端: %s\n", inet_ntoa(client_addr.sin_addr));
return client_socket;
}
void reg(int client_socket, struct User *buf) //注册
{
char str[1024];
sqlite3 * database;
int i;
database = server_sqlite(); //打开数据库
char *errmsg = NULL;
sprintf (str, "insert into usermes values('%s', '%s', '666')", buf->name, buf->password);
int ret = sqlite3_exec(database, str, NULL, NULL, &errmsg);
if (ret != SQLITE_OK)
{
printf ("数据库操作失败:%s\n", errmsg);
buf->cmd = -2;
write(client_socket, buf, sizeof(struct User));
}
else
{
printf("注册成功\n");
buf->cmd = 1000;
write(client_socket, buf, sizeof(struct User));
}
sqlite3_close(database); // 关闭数据库
}
void log_in(int client_socket, struct User *buf) //登录
{
sqlite3 * database = server_sqlite();
char *errmsg = NULL;
char **resultp = NULL;
int nrow, ncolumn;
char *sql = "select * from usermes";
int ret = sqlite3_get_table(database, sql, &resultp, &nrow, &ncolumn, &errmsg);
if (ret != SQLITE_OK)
{
printf ("数据库操作失败:%s\n", errmsg);
return;
}
printf("正在匹配账户和密码....\n");
int i;
int k;
for (i = 3; i < (nrow+1)*ncolumn; i++)
{
if(strncmp(resultp[i], buf->name, strlen(buf->name)) == 0)
{
if(strncmp(resultp[i+1], buf->password, strlen(buf->password)) == 0)
{
for(k = 0; k < 5; k++)
{
if(user[k].socket == 0)
{
strcpy (user[k].name , buf->name);
user[k].so