(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 端维护一个所有登陆用户的聊天会的记录文件,以便备查。
#include
#include
#include
#include
#include
#include
#include
#include
#include
#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].socket = client_socket;
buf->cmd = 1001;
strcpy(buf->signature,resultp[i+2]);
write(client_socket, buf, sizeof(struct User));
printf("密码账号匹配成功\n");
break;
}
}
if(k == 5)
{
printf("在线人数已达上限\n");
buf->cmd = -3;
write(client_socket, buf, sizeof(struct User));
}
return;
}
printf("账户和密码匹配不成功\n");
buf->cmd = -4;
write(client_socket, buf, sizeof(struct User));
break;
}
}
if(i = (nrow+1)*ncolumn)
{
printf("没有此用户\n");
buf->cmd = -4;
write(client_socket, buf, sizeof(struct User));
}
sqlite3_free_table(resultp);
sqlite3_close(database);
}
void View_profile(int client_socket, struct User *buf) //查看在线好友
{
printf("%s 要查看在线好友\n", buf->name);
buf->cmd = 1002;
int i;
for(i = 0; i < 5; i++)
{
if(user[i].socket != 0)
{
strcpy(buf->msg,user[i].name);
write(client_socket, buf, sizeof(struct User));
}
}
}
void Modify_the_signature(int client_socket, struct User *buf) //修改个签
{
sqlite3 * database = server_sqlite();
printf("%s 要修改个性签名\n", buf->name);
char *errmsg = NULL;
char sql[1024] ;
sprintf(sql, "update usermes set signature = '%s'where name = '%s'", buf->signature, buf->name);
int ret = sqlite3_exec(database, sql, NULL, NULL, &errmsg);
if (ret != SQLITE_OK)
{
buf->cmd = -1;
printf ("数据库操作失败:%s\n", errmsg);
sqlite3_close(database);
return;
}
buf->cmd = 1003;
write(client_socket, buf, sizeof(buf));
printf("个性签名修改成功\n");
sqlite3_close(database);
}
void Private_chat(int client_socket, struct User *buf) //私聊
{
sqlite3 * database = server_sqlite();
printf("%s 要进行私聊\n", buf->name);
int i;
char *errmsg = NULL;
char **resultp = NULL;
int nrow, ncolumn;
char *sql = "select name from usermes";
int ret = sqlite3_get_table(database, sql, &resultp, &nrow, &ncolumn, &errmsg);
if (ret != SQLITE_OK)
{
printf ("数据库操作失败:%s\n", errmsg);
return;
}
for (i = 1; i < (nrow+1)*ncolumn; i++)
{
if(strncmp(resultp[i], buf->toname, strlen(buf->toname)) == 0)
{
break;
}
}
if(i == (nrow+1)*ncolumn)
{
buf->cmd = -3; //服务器不存在该用户
write(client_socket, buf, sizeof(struct User));
sqlite3_close(database);
return;
}
for(i = 0; i < 5; i++)
{
if(strncmp(user[i].name, buf->toname, strlen(buf->toname)) == 0)
{
buf->cmd = 1004;
write(user[i].socket, buf, sizeof(struct User));
break;
}
}
if(i == 5)
{
buf->cmd = -2; //要发送消息的人不在线
write(client_socket, buf, sizeof(struct User));
}
sqlite3_close(database);
}
void Group_chat(int client_socket, struct User *buf) //群聊
{
printf("%s 要进行群聊\n", buf->name);
buf->cmd = 1005;
int i;
for(i = 0; i < 5; i++)
{
if(user[i].socket != 0)
{
write(user[i].socket, buf, sizeof(struct User));
}
}
}
void Logout(int client_socket, struct User *buf) //注销函数
{
printf("%s 要注销此用户\n", buf->name);
sqlite3 * database = server_sqlite();
int i;
char *errmsg = NULL;
char **resultp = NULL;
int nrow, ncolumn;
char sql[1024];
sprintf(sql,"delete from usermes where name = '%s'", buf->name);
int ret = sqlite3_exec(database, sql, NULL, NULL, &errmsg);
if (ret != SQLITE_OK)
{
printf ("数据库操作失败:%s\n", errmsg);
return;
}
buf->cmd = 1006;
printf("此用户已经成功注销\n");
write(client_socket, buf, sizeof(struct User));
sqlite3_close(database);
}
void Send_file(int client_socket, struct User *buf) //文件传输函数
{
sqlite3 * database = server_sqlite();
printf("%s 要向 %s 发送文件\n", buf->name, buf->toname);
int i;
for(i = 0;i < 5; i++)
{
if(strncmp(user[i].name, buf->toname, strlen(buf->toname)) == 0)
{
strcpy(buf->file_name, buf->name);
buf->cmd = 1007;
write(user[i].socket, buf, sizeof(struct User));
printf("文件发送中。。。\n");
break;
}
}
}
void exit_chat(int client_socket, struct User *buf) //退出聊天室
{
printf("%s 要退出聊天室\n", buf->name);
int i;
for(i = 0; i < 5; i++)
{
if(strcmp(user[i].name, buf->name) == 0)
{
memset(&user[i], 0, sizeof(struct User));
printf("聊天室退出成功\n");
buf->cmd = 1008;
write(client_socket, buf, sizeof(struct User));
break;
}
}
}
void exchang_password(int client_socket, struct User *buf) //修改密码
{
printf("%s 要修改密码\n", buf->name);
sqlite3 * database;
int i;
char str[1024];
database = server_sqlite(); //打开数据库
char *errmsg = NULL;
sprintf (str, "update usermes set password = '%s'where name = '%s'", buf->password, buf->name);
int ret = sqlite3_exec(database, str, NULL, NULL, &errmsg);
if (ret != SQLITE_OK)
{
printf ("数据库操作失败:%s\n", errmsg);
buf->cmd = -4;
write(client_socket, buf, sizeof(struct User));
}
else
{
printf("密码修改成功\n");
buf->cmd = 1009;
write(client_socket, buf, sizeof(struct User));
}
sqlite3_close(database); // 关闭数据库
}
void *hanld_client(void *v)
{
int client_socket = (int)v;
struct User buf;
while(1)
{
// 从客户端读一个结构体数据
int ret = read(client_socket, &buf, sizeof(buf));
if (ret == -1)
{
printf("错误代码\n");
perror ("read");
break;
}
if(ret == 0)
{
printf("客户端退出\n");
break;
}
switch (buf.cmd)
{
case 1 : // 客户端进行注册
printf("客户进行注册\n");
reg(client_socket, &buf);
break;
case 2 : // 客户端进行登录
log_in(client_socket, &buf);
break;
case 3 : // 客户端退出
printf("客户端退出\n");
break;
case 4: //查看在线好友
View_profile(client_socket, &buf);
memset(&buf, 0, sizeof(buf));
break;
case 5: //修改个性签名
Modify_the_signature(client_socket, &buf);
break;
case 6: //私聊
Private_chat(client_socket, &buf);
break;
case 7: //群聊
Group_chat(client_socket, &buf);
break;
case 8: //注销此用户
Logout(client_socket, &buf);
break;
case 9: //发送文件
Send_file(client_socket, &buf);
break;
case 10: //退出聊天室
exit_chat(client_socket, &buf);
break ;
case 11: //修改密码
exchang_password(client_socket, &buf);
break;
default:
printf("输入有误,请重新输入\n");
break;
}
}
}
int main()
{
int listen_socket = init_socket(); //初始化监听套接字
while (1)
{
// 获取与客户端连接的套接字
int client_socket = MyAccept(listen_socket);
// 创建一个线程去处理客户端的请求,主线程依然负责监听
pthread_t id;
pthread_create(&id, NULL, hanld_client, (void *)client_socket);
pthread_detach(id); // 线程分离
}
close (listen_socket);
return 0;
}
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#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[2];
char file_name[20];
char file_msg[1024];
};
int init_socket()
{
int client_socket = socket (AF_INET, SOCK_STREAM, 0);
if(client_socket == -1)
{
printf("\t\t错误代码\n");
perror("socket");
return -1;
}
struct sockaddr_in addr;
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET; // 设置地址族
addr.sin_port = htons(PORT); // 设置本地端口
inet_aton("127.0.0.1",&(addr.sin_addr));
// 连接服务器,如果成功,返回0,如果失败,返回-1
// 成功的情况下,可以通过socketfd与服务器进行通信
int ret = connect(client_socket, (struct sockaddr *)&addr, sizeof(addr));
if (ret == -1)
{
perror ("connect");
return -1;
}
return client_socket;
}
void Home_interface() //主界面
{
system("clear");
printf("\n\n\n\n");
printf("\t\t请输入对应功能的号码\n");
printf("\t\t\t1、注册\n");
printf("\t\t\t2、登录\n");
printf("\t\t\t3、退出\n");
}
sqlite3 * chat_sqlite() //聊天数据库,保存聊天记录
{
sqlite3 * database;
// 打开数据库
int ret = sqlite3_open("chathistory.db", &database);
if (ret != SQLITE_OK)
{
printf ("错误代码\n");
perror("sqlite3_open");
return NULL;
}
char *errmsg = NULL;
char *sql = "create table if not exists chathistory(fromname TEXT, toname TEXT, msg TEXT)";
ret = sqlite3_exec(database, sql, NULL, NULL, &errmsg);
if (ret != SQLITE_OK)
{
printf ("数据库操作失败:%s\n", errmsg);
return NULL;
}
return database;
}
void func_interface(struct User *user) //功能界面
{
system("clear");
printf("☺:%s\n", user->signature);
printf("\n\n");
printf("\t\t请输入对应功能的号码\n");
printf("\t\t\t1、查看在线好友\n");
printf("\t\t\t2、修改个性签名\n");
printf("\t\t\t3、私聊\n");
printf("\t\t\t4、群聊\n");
printf("\t\t\t5、注销此用户\n");
printf("\t\t\t6、发送文件\n");
printf("\t\t\t7、查看聊天记录\n");
printf("\t\t\t8、退出聊天室\n");
printf("\t\t\t9、修改密码\n");
}
void reg(int client_socket) //注册函数
{
struct User user;
user.cmd = 1;
//printf("\n\n\n\n");
printf("\t\t请输入用户名:");
scanf("%s", user.name);
printf("\t\t请输入密码:");
scanf("%s", user.password);
write(client_socket, &user, sizeof(user));
read(client_socket, &user, sizeof(user));
if(user.cmd == -2)
{
printf("\t\t已有用户,请重新注册\n");
}
if(user.cmd == 1000)
{
printf("\t\t注册成功\n");
}
else
{
printf("\t\t注册失败,请重新注册\n");
sleep(1);
}
}
void View_profile(int client_socket,struct User *user) //查看在线好友
{
user->cmd = 4;
write(client_socket, user, sizeof(struct User));
printf("\t\t请按回车键返回主界面\n");
getchar();
getchar();
}
void Modify_the_signature(int client_socket,struct User *user) //修改个签
{
user->cmd = 5;
printf("\t\t请输入你想输入的个性签名:\n");
scanf("%s", user->signature);
write(client_socket, user, sizeof(struct User));
}
void Private_chat(int client_socket,struct User *user) //私聊
{
sqlite3 * database = chat_sqlite();
user->cmd = 6;
printf("\t\t请输入你想聊天的对象:");
scanf("%s", user->toname);
strcpy(user->fromname, user->name);
printf("\t\t请输入聊天内容:");
scanf("%s", user->msg);
write(client_socket, user, sizeof(struct User));
char *errmsg = NULL;
char str[1024];
sprintf (str, "insert into chathistory values('%s', '%s', '%s')",user->fromname, user->toname, user->msg);
int ret = sqlite3_exec(database, str, NULL, NULL, &errmsg);
if (ret != SQLITE_OK)
{
printf ("数据库操作失败:%s\n", errmsg);
perror("sqlite3_exec");
}
sqlite3_close(database);
}
void Group_chat(int client_socket,struct User *user)
{
sqlite3 * database = chat_sqlite();
user->cmd = 7;
printf("请输入聊天内容:");
scanf("%s", user->msg);
write(client_socket, user, sizeof(struct User));
char *errmsg = NULL;
char str[1024];
sprintf (str, "insert into chathistory values('%s', 'everyone', '%s')",user->name, user->msg);
int ret = sqlite3_exec(database, str, NULL, NULL, &errmsg);
if (ret != SQLITE_OK)
{
printf ("数据库操作失败:%s\n", errmsg);
perror("sqlite3_exec");
}
sqlite3_close(database);
}
void * readMsg(void *v) //读写分离
{
sqlite3 * database = chat_sqlite();
int client_socket = (int)v;
struct User buf;
char str[1024];
int ret;
char *errmsg = NULL;
char file_name[20];
while (1)
{
int ret = read (client_socket, &buf, sizeof(buf));
if(ret == -1)
{
perror("read");
break;
}
switch (buf.cmd)
{
case 1002: //查看好友
system("clear");
printf("\n\n\n\n");
printf("\t\t在线的人有:%s\n", buf.msg);
break;
case 1003: //修改个签
printf("个性签名修改成功\n");
sleep(1);
break;
case -1:
printf("个性签名修改失败\n");
sleep(1);
break;
case 1004: // 私聊
printf("\n");
printf ("%s 给你发一条消息:%s\n", buf.fromname, buf.msg);
sprintf (str, "insert into chathistory values('%s', '%s', '%s')",buf.fromname, buf.toname, buf.msg);
ret = sqlite3_exec(database, str, NULL, NULL, &errmsg);
break;
case -2:
printf("该对象不在线\n");
break;
case -3:
printf("不存在该对象\n");
break;
case 1005: // 群聊
printf("\n");
printf ("收到一条群消息: %s\n", buf.msg);
sprintf (str, "insert into chathistory values('%s', 'everyone', '%s')",buf.name, buf.msg);
ret = sqlite3_exec(database, str, NULL, NULL, &errmsg);
break;
case 1006: //注销
printf("注销成功\n");
sleep(1);
sqlite3_close(database);
return ;
case 1008:
printf("成功退出聊天室\n");
sleep(1);
sqlite3_close(database);
return;
case 1009:
printf("密码修改成功\n");
sleep(1);
break;
case -4:
printf("密码修改失败\n");
sleep(1);
break;
case 1007:
printf("\n");
printf("%s 要给你传输文件\n", buf.name);
ret = open("text", O_WRONLY | O_CREAT, 0777);
if(ret == -1)
{
perror ("open ret");
break;
}
write(ret, buf.file_msg, strlen(buf.file_msg));
close(ret);
printf("成功接收到文件!\n");
break;
}
}
}
int Logout(int client_socket,struct User *user)
{
char buf[] = "y";
printf("是否注销此用户,请输入y/n \n");
scanf("%s", user->logout);
if(strcmp(user->logout, buf) == 0)
{
user->cmd = 8;
write(client_socket, user, sizeof(struct User));
printf("正在注销。。。\n");
sleep(1);
return 1;
}
else
{
return 0;
}
}
void Send_file(int client_socket,struct User *user) //发送文件
{
user->cmd = 9;
printf("请输入要发送文件的对象:");
scanf("%s", user->toname);
printf("请选择要发送的文件:");
scanf("%s", user->file_name);
int fd = open(user->file_name, O_RDWR);
if(fd == -1)
{
printf("文件打开失败\n");
perror("open");
return;
}
printf("文件正在发送\n");
int ret;
while(ret = read(fd, user->file_msg, 1024))
{
if(ret == -1)
{
printf("文件读取失败\n");
perror("read");
return;
}
write(client_socket, user, sizeof(struct User));
usleep(5);
}
printf("文件发送成功\n");
close(fd);
}
void chat_history() //查看聊天记录函数
{
sqlite3 * database = chat_sqlite();
char *errmsg = NULL;
char **resultp = NULL;
int nrow, ncolumn;
char *sql = "select * from chathistory";
int ret = sqlite3_get_table(database, sql, &resultp, &nrow, &ncolumn, &errmsg);
if (ret != SQLITE_OK)
{
printf ("数据库操作失败:%s\n", errmsg);
return;
}
int i;
for(i = 0; i < (nrow+1)*ncolumn; i++)
{
if (i % ncolumn == 0)
printf ("\n");
printf("%-16s", resultp[i]);
}
printf("\n");
printf("请按回车键返回主界面\n");
getchar();
getchar();
sqlite3_close(database);
}
void exit_chat(int client_socket,struct User *user)
{
user->cmd = 10;
write(client_socket, user, sizeof(struct User));
printf("正在退出....\n");
sleep(1);
}
void exchang_password(int client_socket,struct User *user)
{
user->cmd = 11;
printf("请输入要修改的密码:");
scanf("%s", user->password);
write(client_socket, user, sizeof(struct User));
printf("正在修改密码\n");
sleep(1);
}
void log_in(int client_socket)
{
struct User user;
user.cmd = 2;
while(1)
{
printf("\n\n\n\n");
printf("\t\t请输入用户名:");
scanf("%s", user.name);
printf("\t\t请输入密码:");
scanf("%s", user.password);
write(client_socket, &user, sizeof(user));
read(client_socket, &user, sizeof(user));
if(user.cmd == -3)
{
printf("\t\t客户已达上限,请稍后登录\n");
return;
}
if(user.cmd == -4)
{
printf("\t\t密码或用户名不对,请重新输入\n");
return;
}
if(user.cmd == 1001)
{
printf("\t\t正在登录。。。。。。\n");
//sleep(2);
printf("\t\t登录成功\n");
//sleep(1);
pthread_t id;
pthread_create(&id, NULL, readMsg, (void *)client_socket);
pthread_detach(id); // 读写分离
int ret;
int i;
while(1)
{
func_interface(&user); // 功能界面函数
printf("\t\t请输入对应功能的号码:");
scanf("%d",&i);
switch(i)
{
case 1:
View_profile(client_socket, &user); //查看在线好友
break;
case 2:
Modify_the_signature(client_socket, &user); //修改个性签名
break;
case 3:
Private_chat(client_socket, &user); //私聊
break;
case 4:
Group_chat(client_socket, &user); //群聊
break;
case 5:
ret = Logout(client_socket, &user); //注销此用户
if(ret == 1)
return;
break;
case 6:
Send_file(client_socket, &user); //发送文件
break;
case 7:
chat_history(); //查看聊天记录
break;
case 8: //退出聊天室
exit_chat(client_socket, &user);
return;
case 9: //修改密码
exchang_password(client_socket, &user);
break ;
default:
printf("\t\t输入有误,请重新输入\n");
break;
}
}
}
}
}
int main()
{
int client_socket = init_socket (); //客户端初始化套接字
while(1)
{
struct User user;
Home_interface();
printf("\t\t请输入对于功能的号码:");
scanf("%d", &user.cmd);
printf("\n");
switch(user.cmd)
{
case 1:
reg(client_socket); //注册函数
break;
case 2:
log_in(client_socket); //登录函数
break;
case 3:
write(client_socket, &user, sizeof(user));
exit(0);
break;
default:
printf("\t\t输入有误,请重新输入\n");
break;
}
}
return 0;
}