写了几天1.0终于写好了,不容易啊
客户端
#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <arpa/inet.h>
#include <sqlite3.h>
#include <stdlib.h>
#define SIZE 1024
#define PORT 9999
int flag1 = 0;
char myname [20];
char password[20];
char msg1[1024]; // 保存聊天信息
struct Msg
{
char msg[SIZE]; //消息内容
char name[20]; //用户名
char fromname[20]; //发送消息的用户名
char toname[20]; //要发送消息的用户名
char password[20]; //用户密码
int socket;
int cmd; //执行的命令
int flag;
};
struct Msg msg; // 全局变量两个线程共享
//主页面
void interface()
{
printf ("1、注册\n");
printf ("2、登陆\n");
printf ("3、退出\n");
}
//普通用户的界面
void noruser()
{
printf ("1、查看当前在线用户\n");
printf ("2、进入群聊\n");
printf ("3、进入私聊\n");
printf ("4、查看聊天记录\n");
printf ("5、更改用户密码\n");
printf ("6、注销当前用户\n");
printf ("7、设置个性签名\n");
printf ("8、文件传输\n");
printf ("9、退出聊天室 返回登陆界面\n");
}
//查看当前在线人数
int online (int socketfd)
{
struct Msg msg;
msg.flag = 1;
msg.cmd = 10;
write (socketfd, &msg, sizeof (msg));
}
//群聊
void chat2(int socketfd, char *name)
{
struct Msg msg;
msg.flag = 2;
msg.cmd = 10;
strcpy (msg.toname, "all");
printf ("请输入要发送的内容: \n");
fgets(msg.msg, 1024, stdin);
strcpy (msg.fromname, name);
write (socketfd, &msg, sizeof(msg));
}
//私聊
void chat (int socketfd,char *name)
{
struct Msg msg;
msg.flag = 3;
//msg.cmd = 10;
printf ("请输入要发送的对象的名称:\n");
scanf ("%s", msg.toname);
while(getchar() != '\n');
printf ("请输入要发送的内容:\n");
scanf ("%s", msg.msg);
while(getchar() != '\n');
strcpy (msg.fromname, name);
write (socketfd, &msg, sizeof (msg));
//printf("qwwerterter\n");
}
//更改密码
void change_pw (int socketfd,char *name)
{
struct Msg msg;
msg.flag = 5;
msg.cmd = 10;
printf ("请输入你要更改密码的用户:\n");
scanf ("%s", name);
while(getchar() != '\n');
printf ("请输入更改的密码:\n");
scanf ("%s", msg.msg);
while(getchar() != '\n');
strcpy (msg.fromname, name);
write (socketfd, &msg, sizeof (msg));
}
// 用来保存收到的聊天信息
int keep_msg(char * msg1,struct Msg msg)
{
sqlite3 * database;
// 打开数据库
int ret = sqlite3_open("histroy.db", &database);
if (ret != SQLITE_OK)
{
printf ("\t打开数据库失败\n");
return;
}
char *errmsg = NULL;
char *sql = "create table if not exists histroy(fromname TEXT, tonamed TEXT, msg TEXT)";
ret = sqlite3_exec(database, sql, NULL, NULL, &errmsg);
if (ret != SQLITE_OK)
{
printf ("数据库操作失败:%s\n", errmsg);
return -1;
}
// 往histroy表中添加信息
char buf[100];
sprintf (buf, "insert into histroy values('%s','%s','%s')",msg.fromname,msg.toname,msg1);
ret = sqlite3_exec(database, buf, NULL, NULL, &errmsg);
if (ret != SQLITE_OK)
{
printf ("\t数据库操作失败:%s\n", errmsg);
return -1;
}
sqlite3_close(database);
}
// 打印群聊历史记录
void chat1_hst()
{
system("clear");
printf ("\t*************************** 网络聊天室 *****************************\n\n\n");
printf ("\t群聊历史记录: \n");
sqlite3 * database;
// 打开数据库
int ret = sqlite3_open("histroy.db", &database);
if (ret != SQLITE_OK)
{
printf ("\t打开数据库失败\n");
return;
}
// 获取histroy表中的信息
char *errmsg = NULL;
char **resultp = NULL;
int nrow, ncolumn;
char *sql = "select * from histroy";
ret = sqlite3_get_table(database, sql, &resultp, &nrow, &ncolumn, &errmsg);
if (ret != SQLITE_OK)
{
printf ("数据库操作失败:%s\n", errmsg);
return;
}
int i;
for (i = 4; i < (nrow+1)*ncolumn; i += 3)
{
if(strcmp(resultp[i], "all") == 0)
{
printf ("%s\n", resultp[i+1]);
}
}
sqlite3_free_table(resultp);
// 关闭数据库
sqlite3_close(database);
}
// 打印私聊历史记录
void chat2_hst()
{
system("clear");
printf ("\t*************************** 网络聊天室 *****************************\n\n\n");
printf ("\t私聊历史记录:\n");
sqlite3 * database;
// 打开数据库
int ret = sqlite3_open("histroy.db", &database);
if (ret != SQLITE_OK)
{
printf ("\t打开数据库失败\n");
return;
}
// 获取histroy表中的信息
char *errmsg = NULL;
char **resultp = NULL;
int nrow, ncolumn;
char *sql = "select * from histroy";
ret = sqlite3_get_table(database, sql, &resultp, &nrow, &ncolumn, &errmsg);
if (ret != SQLITE_OK)
{
printf ("数据库操作失败:%s\n", errmsg);
return;
}
int i;
for (i = 4; i < (nrow+1)*ncolumn; i += 3)
{
if(strcmp(resultp[i], "all") != 0)
{
printf ("%s\n", resultp[i+1]);
}
}
sqlite3_free_table(resultp);
// 关闭数据库
sqlite3_close(database);
}
// 查看聊天记录
void dis_histroy(int socketfd)
{
printf ("\n\t a、查看群聊记录\n\t b、查看个人聊天记录\n\t");
printf ("\n\t***** 请选择: ");
switch (getchar())
{
case 'a':
chat1_hst();
break;
case 'b':
chat2_hst();
break;
}
printf ("\n\t回车键返回 ");
getchar();
}
//用户注销
void logout (int socketfd, char *name)
{
struct Msg msg;
msg.flag = 6;
msg.cmd = 10;
strcpy (msg.fromname, name);
write (socketfd, &msg, sizeof (msg));
flag1 = 1;
}
//普通用户界面工作函数
void work(int socketfd, char *name)
{
char ch[10];
system ("clear");
int i = 1;
while(i)
{
noruser();
fgets (ch, 2, stdin);
while (getchar() != '\n');
switch (ch[0])
{
case '1':
online (socketfd);
break;
case '2': //群聊
chat2(socketfd, name);
break;
case '3': //私聊
chat (socketfd, name);
break;
case '4':
dis_histroy(socketfd);
getchar();
break;
case '5': //更改用户密码
change_pw (socketfd, name);
break;
case '6':
logout (socketfd,name);
i = 0;
return;
case '9':
logout (socketfd, name);
exit (0);
break;
}
system ("clear");
}
}
//注册账号
void reg(int socketfd)
{
struct Msg msg;
msg.cmd = 1;
printf ("请输入用户名:\n");
//fgets (name, 20, stdin);
//strcpy (msg.name, name);
scanf("%s",msg.name);
while(getchar() != '\n');
printf ("请输入密码:\n");
//fgets(password, 20, stdin);
//strcpy (msg.password, password);
scanf ("%s",msg.password);
while(getchar() != '\n');
write (socketfd, &msg, sizeof(msg));
read (socketfd, &msg, sizeof(msg));
if (msg.cmd == 1001)
{
printf ("注册成功\n");
}
else if (msg.cmd == -1)
{
printf ("注册失败\n");
}
}
//线程分离函数
void * readMsg(void *v)
{
int socketfd = (int)v;
struct Msg msg;
while (1)
{
if(flag1 == 1)
{
flag1 = 0;
pthread_exit(NULL);
}
read (socketfd, &msg, sizeof(msg));
switch (msg.flag)
{
case 1:
printf ("在线用户: %s\n", msg.name);
sleep(2);
system ("clear");
break;
case 2:
printf("%s 给所有人发了一条消息:%s\n",msg.fromname, msg.msg);
sprintf (msg1,"%s 向 %s 发送一条信息:%s\n",msg.fromname, msg.toname, msg.msg);
keep_msg(msg1,msg);
break;
case 3: // 私聊
printf ("%s 给你发一条消息:%s\n", msg.fromname, msg.msg);
sprintf (msg1,"%s 向 %s 发送一条信息:%s\n",msg.fromname, msg.toname, msg.msg);
keep_msg(msg1,msg);
break;
case 5:
printf ("%s 更改了用户密码\n", msg.fromname);
break;
case 6:
printf ("%s 进行了注销\n", msg.fromname);
break;
case 9:
exit (0);
break;
}
}
}
//登陆账号
void Login(int socketfd)
{
struct Msg msg;
msg.cmd = 2;
printf ("请输入用户名:\n");
//fgets (name, 20, stdin);
//strcpy (msg.name, name);
scanf("%s",msg.name);
while(getchar() != '\n');
printf ("请输入密码:\n");
//fgets(password, 20, stdin);
//strcpy (msg.password, password);
scanf ("%s",msg.password);
while(getchar() != '\n');
write (socketfd, &msg, sizeof(msg));
read (socketfd, &msg, sizeof(msg));
if (msg.cmd == 1001)
{
printf ("登陆成功\n");
pthread_t id;
pthread_create(&id, NULL, readMsg, (void *)socketfd);
pthread_detach(id); // 线程分离
work(socketfd,msg.name);
}
else if (msg.cmd == -1)
{
printf ("登陆失败\n");
}
}
// 客户端向服务器发送数据
void ask_server(int socketfd)
{
char ch[3];
while (1)
{
interface();
printf ("请输入操作命令\n");
fgets (ch, 2, stdin);
while (getchar() != '\n');
switch (ch[0])
{
case '1': //注册
reg(socketfd);
break;
case '2': //登陆
Login(socketfd);
break;
case '3': //退出
exit (0);
break;
default:
printf ("您输入有误 请重新输入\n");
sleep(1);
break;
}
system ("clear");
}
}
int main()
{
// 创建与服务器通信的套接字
int socketfd = socket(AF_INET, SOCK_STREAM, 0);
if (socketfd == -1)
{
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(socketfd, (struct sockaddr *)&addr, sizeof(addr));
if (ret == -1)
{
perror ("connect");
return -1;
}
printf ("成功连上服务器\n");
ask_server(socketfd);
// 关闭套接字
close(socketfd);
return 0;
}
服务器
#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <arpa/inet.h>
#include <pthread.h>
#include <sqlite3.h>
#include <stdlib.h>
#define PORT 9999
#define SIZE 1024
struct Msg
{
char msg[SIZE]; //消息内容
char name[20]; //用户名
char fromname[20]; //发送消息的用户名
char toname[20]; //要发送消息的用户名
char password[20]; //用户密码
int Socket; //用户通信的socket
int cmd; //执行的命令
int flag;
};
//初始化套接字,返回监听套接字
int init_socket ()
{
//1、创建套接字
int listen_socket = socket (AF_INET, SOCK_STREAM, 0);
if (listen_socket == -1)
{
perror ("socket");
return -1;
}
//2、命名套接字,绑定本地的ip地址和端口
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)
{
perror("bind");
return -1;
}
//监听本地套接字
ret = listen (listen_socket, 5);
if (ret == -1)
{
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)
{
perror ("accept");
}
printf ("成功接收一个客户端: %s\n",inet_ntoa(client_addr.sin_addr));
return client_socket;
}
//进行注册
int reg(int client_socket, struct Msg *msg)
{
printf ("进行注册\n");
sqlite3 * database;
// 打开数据库
int ret = sqlite3_open("user.db", &database);
if (ret != SQLITE_OK)
{
printf ("打开数据库失败\n");
return -1;
}
char *errmsg = NULL;
char *sql = "create table if not exists user(name TEXT, password TEXT, socket INTEGER,primary key(name))";
ret = sqlite3_exec(database, sql, NULL, NULL, &errmsg);
if (ret != SQLITE_OK)
{
printf ("数据库操作失败:%s\n", errmsg);
return -1;
}
// insert into student values(id, name, sex, age);
char buf[100];
sprintf (buf, "insert into user values('%s', '%s', %d)", msg->name, msg->password,msg->Socket);
ret = sqlite3_exec(database, buf, NULL, NULL, &errmsg);
if (ret != SQLITE_OK)
{
msg->cmd = -1;
}
else
{
msg->cmd = 1001;
}
// 关闭数据库
sqlite3_close(database);
printf ("%d\n",msg->cmd);
write (client_socket, msg, sizeof(struct Msg));
}
// 1、exec传过来的参数
// 2、代表查询的列数
// 3、char** value 代表一条记录的值 char *value[] = {"1", "zhang1", "M", "12"};
// 4、char** name 代表每一列的字段 char *name[] = {"id", "name", "sex", "age"};
// 每查到一条记录,该函数被调用一次
//查询的回掉函数
int MSG(void* v, int num, char** value, char** name)
{
struct Msg *msg = (struct Msg *)v;
if (strcmp(value[0], msg->name) == 0 && strcmp(value[1], msg->password) == 0)
{
printf ("登陆成功\n");
msg->cmd = 1001;
}
return 0; // 函数内部一定要返回一个0
}
//登陆账号
int Login(int client_socket, struct Msg *msg)
{
sqlite3 * database;
// 打开数据库
int ret = sqlite3_open("user.db", &database);
if (ret != SQLITE_OK)
{
printf ("打开数据库失败\n");
return -1;
}
char *errmsg = NULL;
char *sql = "select * from user";
ret = sqlite3_exec(database, sql, MSG, msg, &errmsg);
if (ret != SQLITE_OK)
{
printf ("数据库操作失败:%s\n", errmsg);
return -1;
}
if (msg->cmd != 1001)
{
printf ("登陆失败\n");
msg->cmd = -1;
}
// 关闭数据库
sqlite3_close(database);
write (client_socket, msg, sizeof(struct Msg));
}
//查看当前在线用户
int noruser (int client_socket, struct Msg *msg)
{
sqlite3 * database;
// 打开数据库
int ret = sqlite3_open("user.db", &database);
if (ret != SQLITE_OK)
{
printf ("打开数据库失败\n");
return -1;
}
char *errmsg = NULL;
char **resultp = NULL;
int nrow, ncolumn;
char sql[SIZE];
sprintf (sql, "select * from user where socket > %d", -1); //查找大于-1的socket
ret = sqlite3_get_table(database, sql, &resultp, &nrow, &ncolumn, &errmsg);
if (ret != SQLITE_OK)
{
printf ("数据库操作失败:%s\n", errmsg);
return -1;
}
int i;
for (i = 3; i < (nrow+1)*ncolumn; i += 3)
{
//if (i % ncolumn == 0)
// printf ("-8%s",resultp[i]);
strcpy (msg->name, resultp[i]);
write (client_socket, msg, sizeof (struct Msg));
}
sqlite3_free_table(resultp);
// 关闭数据库
sqlite3_close(database);
}
//更改密码
int change_pw (int client_socket, struct Msg *msg)
{
sqlite3 * database;
// 打开数据库
int ret = sqlite3_open("user.db", &database);
if (ret != SQLITE_OK)
{
printf ("打开数据库失败\n");
return -1;
}
char *errmsg = NULL;
char sql[SIZE];
sprintf (sql, "update user set password = '%s' where name = '%s'", msg->msg, msg->fromname);
ret = sqlite3_exec(database, sql, NULL, NULL, &errmsg);
if (ret != SQLITE_OK)
{
printf ("数据库操作失败:%s\n", errmsg);
return -1;
}
// 关闭数据库
sqlite3_close(database);
write (client_socket,msg, sizeof (struct Msg));
}
//更新套接字
int update_socket (int client_socket, struct Msg *msg)
{
sqlite3 * database;
// 打开数据库
int ret = sqlite3_open("user.db", &database);
if (ret != SQLITE_OK)
{
printf ("打开数据库失败\n");
return -1;
}
char *errmsg = NULL;
char sql[SIZE];
sprintf (sql, "update user set socket = %d where name = '%s'", client_socket, msg->name);
ret = sqlite3_exec(database, sql, NULL, NULL, &errmsg);
if (ret != SQLITE_OK)
{
printf ("数据库操作失败:%s\n", errmsg);
return -1;
}
// 关闭数据库
sqlite3_close(database);
}
//套接字结束时置-1
int update_Socket (int client_socket, struct Msg *msg)
{
sqlite3 * database;
// 打开数据库
int ret = sqlite3_open("user.db", &database);
if (ret != SQLITE_OK)
{
printf ("打开数据库失败\n");
return -1;
}
char *errmsg = NULL;
char sql[SIZE];
sprintf (sql, "update user set socket = %d where name = '%s'", -1, msg->fromname);
ret = sqlite3_exec(database, sql, NULL, NULL, &errmsg);
if (ret != SQLITE_OK)
{
printf ("数据库操作失败:%s\n", errmsg);
return -1;
}
// 关闭数据库
sqlite3_close(database);
}
//群聊
int chat2 (int client_socket, struct Msg *msg)
{
printf ("%s 发一次群消息\n", msg->fromname);
sqlite3 * database;
// 打开数据库
int ret = sqlite3_open("user.db", &database);
if (ret != SQLITE_OK)
{
printf ("打开数据库失败\n");
return -1;
}
// 3、char ***resultp: char *resultp[] = {"id", "name", "sex", "age", "1", "zhang", "M", "12","2".....};
// 4、nrow: 多少行数据
// 5、ncolumn: 多少列数据
char *errmsg = NULL;
char **resultp = NULL;
int nrow, ncolumn;
char *sql = "select * from user";
ret = sqlite3_get_table(database, sql, &resultp, &nrow, &ncolumn, &errmsg);
if (ret != SQLITE_OK)
{
printf ("数据库操作失败:%s\n", errmsg);
return -1;
}
int i;
for (i = 5; i < (nrow+1)*ncolumn; i += 3)
{
if ((atoi(resultp[i]) != 0))
{
write (atoi(resultp[i]), msg, sizeof (struct Msg));
}
}
sqlite3_free_table(resultp);
// 关闭数据库
sqlite3_close(database);
}
//私聊
int chat (int client_socket, struct Msg *msg)
{
printf ("%s 要给 %s 发一条消息\n", msg->fromname, msg->toname);
sqlite3 * database;
// 打开数据库
int ret = sqlite3_open("user.db", &database);
if (ret != SQLITE_OK)
{
printf ("打开数据库失败\n");
return -1;
}
// 3、char ***resultp: char *resultp[] = {"id", "name", "sex", "age", "1", "zhang", "M", "12","2".....};
// 4、nrow: 多少行数据
// 5、ncolumn: 多少列数据
char *errmsg = NULL;
char **resultp = NULL;
int nrow, ncolumn;
char *sql = "select * from user";
ret = sqlite3_get_table(database, sql, &resultp, &nrow, &ncolumn, &errmsg);
if (ret != SQLITE_OK)
{
printf ("数据库操作失败:%s\n", errmsg);
return -1;
}
int i;
for (i = 5; i < (nrow+1)*ncolumn; i += 3)
{
if ((atoi(resultp[i]) != 0) && (strcmp(resultp[i-2],msg->toname) == 0))
{
write (atoi(resultp[i]), msg, sizeof (struct Msg));
}
}
sqlite3_free_table(resultp);
// 关闭数据库
sqlite3_close(database);
}
//进行注销
void logout (int client_socket, struct Msg *msg)
{
//strcpy (name, msg->name);
write(client_socket, msg, sizeof(struct Msg));
}
//把 负责处理客户端通信的函数改成线程的工作函数
void * hanld_client(void* v)
{
int client_socket = (int)v;
struct Msg msg;
while (1)
{
//从客户端读一个结构体
int ret = read (client_socket, &msg, sizeof (msg));
if (ret == -1)
{
perror ("read");
break;
}
//代表客户端退出
if (ret == 0)
{
update_Socket (client_socket, &msg);
sleep(1);
printf ("客户端退出\n");
break;
}
switch (msg.cmd)
{
case 1: //客户端进行注册
reg (client_socket, &msg);
break;
case 2: //客户端进行登陆
Login(client_socket, &msg);
update_socket(client_socket, &msg);
break;
case 3: //客户端退出
exit(0);
break;
}
switch (msg.flag)
{
case 1:
noruser (client_socket, &msg);
break;
case 2:
chat2 (client_socket, &msg);
break;
case 3:
chat(client_socket, &msg);
break;
case 5:
change_pw (client_socket, &msg);
break;
case 6:
logout (client_socket, &msg);
break;
case 9:
exit(0);
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;
}