有一阵子没写博客了,这个项目写到这,我觉得有必要记录一下了.中间有很多无用的备注,思路和格式也写的不是很好,没办法,水平有限.
在关于传输文件方面,还有一些不可预知的错误,暂时我还没有发现原因,只能减缓传输速度来降低这种错误出现的概率.超级用户还没有写,不过功能也不算少了.
优先头文件
/***************************************************************
* 文件名 : network.h
* 创建时间 : 2017年8月20日15:28:04
* 目录 : D:\苏嵌安装环境\red hat share\leo\项目\聊天室
* 制作者 : 刘铠源
* 目的 : 编写Linux下聊天室中关于网络参数传递的头文件
***************************************************************/
#ifndef __NETWORK_H__
#define __NETWORK_H__
#include <sqlite3.h>
#include <stdio.h>
#include <time.h>
#include <string.h>
#define SIZE 1024
typedef struct msg //表示聊天时的信息
{
char msg[SIZE]; //消息内容
char localname[20]; //消息目的名字
char fromname[20]; //消息来源名字
char password[20]; //用户密码 被借用与文件传输文件名
char signname[40]; //个性签名
int cmd; //工作模式
int num; //用于保存文件复制的字节数,这个我想了好久,没有别的可以取代
}Msg;
//做到一半,我发现只要有来源,目的名字,消息内容和工作模式就可以解决一切问题
//前提是不做文件传输
//***************************服务器数据库**************************************
//创建数据库用于保存账号密码
sqlite3 * Create_Sqlite(void);
//初测时保存用户账号和密码
int Save_User(Msg *msg, sqlite3 *datebase);
//登录时检查用户账号和密码
int Entry_User(Msg *msg, sqlite3 *datebase);
//修改数据库 (个性签名)
int revise_sign_sqlite(Msg * msg);
//修改密码(数据库)
int revise_password_sqlite(Msg * msg);
//***************************客户端操作****************************************
//创建数据库用于保存聊天记录
sqlite3 * Create_user_sqlite(Msg * msg);
//保存聊天记录到数据库中
void save_Chat(Msg *msg);
//查看聊天记录(调用数据库)
void see_chat(Msg * msg);
#endif
/***************************************************************
* 文件名 : server.c/server.h
* 创建时间 : 2017年8月20日13:30:38
* 目录 : D:\苏嵌安装环境\red hat share\leo\项目\聊天室
* 制作者 : 刘铠源
* 目的 : 编写Linux下聊天室中关于服务器的总头文件
***************************************************************/
#ifndef __SERVER_H__
#define __SERVER_H__
//socket bind accept函数使用
#include <sys/types.h>
#include <sys/socket.h> //listen 使用
//memset 等函数使用
#include <string.h>
//htonl htons 函数使用
#include <arpa/inet.h>
//线程管理
#include <pthread.h>
//标准IO
#include <stdio.h>
//自制错误显示
#include "error.h"
//关于保存数据和数据库的头文件
#include "network.h"
#define PORT 9999 //表示服务器端口
#define NOW_MAX 20 //表示最大在线用户个数
typedef struct now_client //用于保存在线的客户端
{
char name[20];
int socket;
}NowClient;
//创建监听套接字
int Socket_init (void);
//链接客户端
int Socket_Accept (int listen_socket);
//创建服务器
int Make_Server (void);
//线程操作客户端
void * hanld_client (void* v);
//客户但进行注册
int regis(int client_socket, Msg *msg);
//登录账号
int entry(int client_socket, Msg *msg);
//用户界面
void User_server(int client_socket, Msg *msg);
//群聊
void server_chatall(int client_socket, Msg * msg);
//私聊
void server_chatone(int client_socket, Msg * msg);
//退出当前登录
void server_entryout(int client_socket, Msg * msg);
//显示当前在下人数
void see_nowuser(int client_socket, Msg * msg);
//修改个性签名
void server_revise_sign(int client_socket, Msg * msg);
//修改密码
void server_revise_password(int client_socket, Msg * msg);
//收到开始传输文件命令
void server_transfer_file(int client_socket, Msg * msg);
//接受文件
void server_transfer_file_y(Msg * msg);
//拒绝文件
void server_transfer_file_n(Msg * msg);
//一切条件都已成立,直接开始传输
void server_start_transfer_file(Msg * msg);
#endif
/***************************************************************
* 文件名 : client.c/client.h
* 创建时间 : 2017年8月20日19:24:59
* 目录 : D:\苏嵌安装环境\red hat share\leo\项目\聊天室
* 制作者 : 刘铠源
* 目的 : 编写Linux下聊天室中关于客户端的总头文件
***************************************************************/
#ifndef __CLIENT_H__
#define __CLIENT_H__
#define PORT 9999
//socket 函数使用
#include <sys/types.h>
#include <sys/socket.h>
//memset 等函数使用
#include <string.h>
//htonl htons 函数使用
#include <arpa/inet.h>
//标准IO
#include <stdio.h>
//系统调用
#include <sys/stat.h>
#include <fcntl.h>
//exit 函数使用
#include <unistd.h>
#include <stdlib.h>
//关于保存数据和数据库的头文件
#include "network.h"
#include <strings.h>
//连接服务器
int Connect_Server(void);
//访问服务器 主界面
int Ask_server(int client_socket);
//客户端主界面
void main_menu(void);
//用户登录界面
void user_menu(void);
//注册账号
void regis(int client_socket);
//登录账号
int entry(int client_socket);
//用户界面
void User_used(int client_socket);
//读写分离
void * readMsg (void *v);
//群聊
void chat_all(int client_socket);
//私聊
void chat_one(int client_socket);
//退出登录
void entry_out(int client_socket);
//查看聊天记录
void look_chat(void);
//显示当前在线人数
void see_now_time(int client_socket);
//修改个性签名
void revise_sign(int client_socket);
//修改密码
void revise_password(int client_socket);
//传输文件
void transfer_file(int client_socket);
//表示愿意接受文件
void transfer_file_y(int client_socket);
//表示不愿意接受文件
void transfer_file_n(int client_socket);
//传出文件来源开始传输文件
void start_transfer_file(int client_socket);
//接受文件
void save_transfer_file(Msg * buf);
#endif
/***************************************************************
* 文件名 : error.c/error.h
* 创建时间 : 2017年8月20日13:44:51
* 目录 : D:\苏嵌安装环境\red hat share\leo\项目\聊天室
* 制作者 : 刘铠源
* 目的 : 编写Linux下聊天室中报错系统的头文件
***************************************************************/
#ifndef __ERROR_H__
#define __ERROR_H__
#include<stdio.h>
#define SOCKET_INIT -1 //初始化套接字失败
#define SOCKET_ACCEPT -2 //链接客户端
#define CREATE_SQLITE -3 //打开数据库失败
#define SAVE_SQLITE -4 //数据库插入数据失败
int errno ;
//向屏幕输出错误原因
void myerror (char *str);
//向字符串传送错误原因
char * myStrError (void);
#endif
然后是各函数
/***************************************************************
* 文件名 : server.c/server.h
* 创建时间 : 2017年8月20日13:30:38
* 目录 : D:\苏嵌安装环境\red hat share\leo\项目\聊天室
* 制作者 : 刘铠源
* 目的 : 编写Linux下聊天室中关于服务器的函数
***************************************************************/
#include "server.h"
NowClient user[NOW_MAX]; //表示当前在线人的资料
//****************************服务器制作*******************************
//创建监听套接字
int Socket_init (void)
{
//创建
int listen_socket = socket(AF_INET, SOCK_STREAM, 0);
if(listen_socket == -1)
{
printf("监听套接字创建失败.\n");
perror("socket");
return -1;
}
//绑定
struct sockaddr_in addr;
memset(&addr, 0, sizeof(addr)); //清空内存
addr.sin_family = AF_INET;
addr.sin_port = htons(PORT); //端口和ip要改成大端模式
addr.sin_addr.s_addr = htonl(INADDR_ANY); //INADDR_ALL表示任意ip
int ret = bind(listen_socket, (struct sockaddr*)&(addr), sizeof(addr));
if(ret == -1)
{
printf("绑定套接字失败.\n");
perror("bind");
return -1;
}
//监听
ret = listen(listen_socket, 5); //用户最多20个
if(ret == -1)
{
printf("监听套接字失败.\n");
perror("listen");
return -1;
}
printf("等待用户连接.....\n");
return listen_socket;
}
//链接客户端
int Socket_Accept (int listen_socket)
{
struct sockaddr_in client_addr;
int len = sizeof(client_addr);
int client_socket = accept(listen_socket,
(struct sockaddr*)&(client_addr), &len);
if(client_socket == -1)
{
printf("链接客户端失败\n");
perror("accept");
return -1;
}
printf("成功链接了一个客户端 : %s\n"), inet_ntoa(client_addr.sin_addr);
return client_socket;
}
//创建服务器
int Make_Server (void)
{
//初始化套接字
int listen_socket = Socket_init();
if(listen_socket == -1)
{
errno = SOCKET_INIT;
myerror("Socket_init");
return -1;
}
while(1)
{
//链接客户端
int client_socket = Socket_Accept(listen_socket);
if(listen_socket == -1)
{
errno = SOCKET_ACCEPT;
myerror("Socket_Accept");
return -1;
}
//创建进程处理客户端
pthread_t client_id;
int ret = pthread_create(&client_id, NULL, hanld_client, (void*)client_socket);
if(ret != 0)
{
perror("pthread_create");
return -1;
}
pthread_detach(client_id); // 线程分离
}
close (listen_socket);
return 0;
}
//****************************主界面操作*******************************
//线程操作客户端
void * hanld_client (void* v)
{
int client_socket = (int)v;
Msg msg;
while(1)
{
//从客户端读取数据
int ret = read(client_socket, &msg, sizeof(msg));
if (ret == -1)
{
perror ("read");
break;
}
// 代表客户端退出
if (ret == 0)
{
printf ("客户端退出\n");
break;
}
switch (msg.cmd)
{
case 1 : // 客户但进行注册
regis(client_socket, &msg);
break;
case 2 : //客户端登录
ret = entry(client_socket, &msg);
write(client_socket , &msg ,sizeof(Msg));
if (ret == 1)
{
//在线人数加1
int i;
for (i=0; i<NOW_MAX; i++)
{
if(user[i].socket == 0)
{
strcpy(user[i].name, msg.fromname);
user[i].socket = client_socket;
//printf("%d %s\n",user[i].socket,user[i].name);
printf("客户端在线人数加一\n");
break;
}
}
//用户界面
User_server(client_socket, &msg);
}
break;
}
}
}
// 客户端进行注册
int regis(int client_socket, Msg *msg)
{
printf(" %s 进行注册.\n",msg->fromname);
//用户账号和密码保存在数据库中
sqlite3 *datebase = Create_Sqlite();
if(datebase == NULL)
{
errno = CREATE_SQLITE;
myerror("Create_Sqlite");
}
int flag = Save_User(msg, datebase);
if (flag == -1)
{
errno = SAVE_SQLITE;
myerror("Save_User");
msg->cmd = -1;
}
/*
else if (flag == -2) //表示用户名已经存在
{
printf("账号创建失败,用户民已存在.\n");
msg->cmd = -1;
}
*/
else
{
msg->cmd += 1000;
}
sqlite3_close(datebase);
write(client_socket , msg ,sizeof(Msg));
}
//登录账号
int entry(int client_socket, Msg *msg)
{
printf(" %s 进行登录.\n",msg->fromname);
//用户登录
sqlite3 *datebase = Create_Sqlite();
if(datebase == NULL)
{
errno = CREATE_SQLITE;
myerror("Create_Sqlite");
return -1;
}
int flag = Entry_User(msg, datebase);
//printf("2222\n"); 段错误没有输出
if (flag == -1)
{
errno = SAVE_SQLITE;
myerror("Entry_User");
return -1;
}
if (flag == -2)
{
printf("登录失败,用户名不存在\n");
msg->cmd = -1;
return -1;
}
if (flag == -3)
{
printf("登录失败,密码错误\n");
msg->cmd = -2;
return -1;
}
else
{
printf("登录成功\n");
msg->cmd += 1000;
return 1;
}
}
//***************************用户界面操作******************************
//用户界面
void User_server(int client_socket, Msg *msg)
{
int j = 1; //表示循环退出条件
while(j)
{
//从客户端读取数据
int ret = read(client_socket, msg, sizeof(Msg));
if (ret == -1)
{
perror ("read");
break;
}
// 代表客户端退出
if (ret == 0)
{
//printf ("客户端退出\n");
break;
}
switch (msg->cmd)
{
case 3 : //群聊
server_chatall(client_socket, msg);
break;
case 4 : //私聊
server_chatone(client_socket, msg);
break;
case 5 : //退出登录
server_entryout(client_socket, msg);
j = 0;
break;
case 6 : //显示当前在线人数
see_nowuser(client_socket, msg);
break;
case 7 : //修改个性签名
server_revise_sign(client_socket, msg);
break;
case 8 : //修改密码
server_revise_password(client_socket, msg);
break;
/*************错误思路
case 9 : //传输文件
server_transfer_file(client_socket, msg);
break;
case 10 : //表示一切处理成功,直接传输文件
server_transfer_filenow(client_socket, msg);
*/
case 9 : //请求传输文件
server_transfer_file(client_socket, msg);
break;
case 10: //接受传输文件
server_transfer_file_y(msg);
break;
case -10 : //拒绝传输文件
server_transfer_file_n(msg);
break;
case 11: //一切条件都已成立,直接开始传输
server_start_transfer_file(msg);
}
}
//用户下线
int i ;
for(i=0; i<NOW_MAX; i++)
{
if(user[i].socket == client_socket)
{
user[i].socket = 0;
printf("客户端在线人数减一\n");
break;
}
}
}
//群聊
void server_chatall(int client_socket, Msg * msg)
{
printf (" %s 进行群发.\n",msg->fromname);
int i;
for(i=0; i<NOW_MAX; i++)
{
if (user[i].socket != 0)
{
write(user[i].socket, msg , sizeof(Msg));
}
}
}
//私聊
void server_chatone(int client_socket, Msg * msg)
{
printf ("私聊 %s发送信息给%s\n",msg->fromname,msg->localname);
int i;
for(i=0; i<NOW_MAX; i++)
{
if(user[i].socket != 0 && strncmp(user[i].name, msg->localname, strlen(msg->localname)) == 0)
{
//printf("%s\n",msg->localname);
//printf("%d %s\n",user[i].socket,user[i].name);
write(user[i].socket, msg , sizeof(Msg));
printf("私聊成功\n");
break;
}
}
//printf("%d\n",i);
if (i == NOW_MAX)
{
msg->cmd = -3; //表示私聊失败
write(client_socket, msg , sizeof(Msg));
printf("私聊失败\n");
}
}
//退出当前登录
void server_entryout(int client_socket, Msg * msg)
{
write(client_socket, msg , sizeof(Msg));
printf("%s 退出登录\n",msg->fromname);
}
//显示当前在下人数
void see_nowuser(int client_socket, Msg * msg)
{
printf("%s 查看当前在线人员\n",msg->fromname);
int i;
int len;
char buf[1024] = {0};
for(i=0; i<NOW_MAX; i++)
{
if(user[i].socket != 0)
{
strcat(buf,user[i].name);
len = strlen(buf);
buf[len] = ' ';
}
}
strcpy(msg->msg,buf);
write(client_socket, msg, sizeof(Msg));
printf("查看成功\n");
}
//修改个性签名
void server_revise_sign(int client_socket, Msg * msg)
{
printf("%s 修改个性签名\n",msg->fromname);
int ret = revise_sign_sqlite(msg); //修改数据库
if (ret == -1)
{
msg->cmd = -7;
printf("%s 修改个性签名失败\n",msg->fromname);
write(client_socket, msg, sizeof(Msg));
}
printf("%s 修改个性签名成功\n",msg->fromname);
write(client_socket, msg, sizeof(Msg));
}
//修改密码
void server_revise_password(int client_socket, Msg * msg)
{
printf("%s 修改密码\n",msg->fromname);
int ret = revise_password_sqlite(msg);
if (ret == -1)
{
msg->cmd = -8;
printf("%s 修改密码失败\n",msg->fromname);
write(client_socket, msg, sizeof(Msg));
}
printf("%s 修改密码成功\n",msg->fromname);
write(client_socket, msg, sizeof(Msg));
}
/*错误思路
//传输文件
void server_transfer_file(int client_socket, Msg * msg)
{
printf ("%s 请求发送%s文件给 %s\n",msg->fromname,msg->signname,msg->localname);
int i;
for(i=0; i<NOW_MAX; i++)
{
if(user[i].socket != 0 && strcmp(user[i].name, msg->localname) == 0)
{
write(user[i].socket, msg , sizeof(Msg));
printf("发送给%s信息进行判断是否接受\n",msg->localname);
break;
}
}
if (i == NOW_MAX)
{
msg->cmd == -9; //表示传输文件失败
write(client_socket, msg , sizeof(Msg));
printf("发送文件失败,好友不在线或不存在\n");
return ;
}
read(user[i].socket, msg, sizeof(Msg));
if (msg->cmd != 2000)
{
msg->cmd = -10; //传输文件失败
write(client_socket, msg , sizeof(Msg));
printf("发送文件失败,好友拒绝\n");
return ;
}
msg->cmd = 10; //表示接受传输
printf("开始传输文件\n");
write(client_socket, msg, sizeof(Msg));
}
//表示一切处理成功,直接传输文件
void server_transfer_filenow(int client_socket, Msg * msg)
{
printf ("接受到数据,进行传输\n");
msg->cmd = 11;
int i;
for(i=0; i<NOW_MAX; i++)
{
if(user[i].socket != 0 && strncmp(user[i].name, msg->localname, strlen(msg->localname)) == 0)
{
write(user[i].socket, msg , sizeof(Msg));
printf("传输成功\n");
break;
}
}
}
*/
//传输文件
void server_transfer_file(int client_socket, Msg * msg)
{
printf ("%s 请求发送%s文件给 %s\n",msg->fromname,msg->signname,msg->localname);
int i;
for(i=0; i<NOW_MAX; i++)
{
if(user[i].socket != 0 && strcmp(user[i].name, msg->localname) == 0)
{
write(user[i].socket, msg , sizeof(Msg));
printf("发送给%s信息进行判断是否接受\n",msg->localname);
break;
}
}
if (i == NOW_MAX)
{
msg->cmd == -9; //表示传输文件失败
write(client_socket, msg , sizeof(Msg));
printf("发送文件失败,好友不在线或不存在\n");
}
}
//接受文件
void server_transfer_file_y(Msg * msg)
{
printf("%s 接受文件传输.\n",msg->fromname);
int i;
for(i=0; i<NOW_MAX; i++)
{
if(user[i].socket != 0 && strcmp(user[i].name, msg->localname) == 0)
{
write(user[i].socket, msg , sizeof(Msg));
break;
}
}
}
//拒绝文件
void server_transfer_file_n(Msg * msg)
{
printf("%s 不愿意接受文件传输.\n",msg->fromname);
int i;
for(i=0; i<NOW_MAX; i++)
{
if(user[i].socket != 0 && strcmp(user[i].name, msg->localname) == 0)
{
write(user[i].socket, msg , sizeof(Msg));
break;
}
}
}
//一切条件都已成立,直接开始传输
void server_start_transfer_file(Msg * msg)
{
//printf("文件传输中\n");
int i;
for(i=0; i<NOW_MAX; i++)
{
if(user[i].socket != 0 && strcmp(user[i].name, msg->localname) == 0)
{
write(user[i].socket, msg , sizeof(Msg)); //写文件数据
break;
}
}
if(msg->num != 1024)
{
printf("文件传输完成\n");
}
}
int main()
{
Make_Server ();
return 0;
}
/***************************************************************
* 文件名 : hanld_sqlite3/network.h
* 创建时间 : 2017年8月20日15:27:56
* 目录 : D:\苏嵌安装环境\red hat share\leo\项目\聊天室
* 制作者 : 刘铠源
* 目的 : 编写Linux下聊天室中关于一些对数据库操作的函数
***************************************************************/
#include "network.h"
//***************************服务器数据库**************************************
//创建数据库用于保存账号密码
sqlite3 * Create_Sqlite(void)
{
sqlite3 * datebase;
int ret = sqlite3_open("chat.db", &datebase);
if(ret != SQLITE_OK)
{
printf("数据库打开失败\n");
return NULL;
}
printf("数据库打开成功\n");
return datebase;
}
/*
create table user(
NAME TEXT,
Password TEXT,
);
*/
//注册时保存用户账号和密码
int Save_User(Msg *msg, sqlite3 *datebase)
{
//打开表
char *errmsg = NULL;
char buf[100] = "create table if not exists user(NAME TEXT,Password TEXT,Sign_name TEXT,primary key(NAME))";
int ret = sqlite3_exec(datebase, buf, NULL, NULL, &errmsg);
if(ret != SQLITE_OK)
{
printf("打开表失败\n");
printf("sqlite3_exec: %s\n",errmsg);
return -1;
}
printf("打开表成功\n");
//判断表中是否已存在相同账号名
char **resultp = NULL;
int nrow,ncolumn;
char *sq1 = "select NAME from user";
ret = sqlite3_get_table(datebase, sq1, &resultp, &nrow, &ncolumn, &errmsg);
if(ret != SQLITE_OK)
{
printf("数据库操作失败\n");
printf("sqlite3_get_table: %s\n",errmsg);
return -1;
}
/*
int i;
for(i=1; i<(nrow+1)*ncolumn ;i++)
{
if(strcmp(resultp[i], msg->fromname) == 0)
{
break;
}
}
printf("判断时候存在同名账号....\n");
sqlite3_free_table(resultp); //释放空间
if ((nrow+1)*ncolumn != 0)
{
if(i == (nrow+1)*ncolumn)
return -2;
}
*/ //用主键比较同名
//插入数据
sprintf(buf,"insert into user values('%s', '%s', '铠源铠源你最帅')",msg->fromname, msg->password);
ret = sqlite3_exec(datebase, buf, NULL, NULL,&errmsg);
if(ret != SQLITE_OK)
{
printf("插入数据失败\n");
printf("sqlite3_exec: %s\n",errmsg);
return -1;
}
return 0;
}
//登录时检查用户账号和密码
int Entry_User(Msg *msg, sqlite3 *datebase)
{
//打开表
char *errmsg = NULL;
char buf[100] = "create table if not exists user(NAME TEXT,Password TEXT,Sign_name TEXT,primary key(NAME))";
int ret = sqlite3_exec(datebase, buf, NULL, NULL, &errmsg);
if(ret != SQLITE_OK)
{
printf("打开表失败\n");
printf("sqlite3_exec: %s\n",errmsg);
return -1;
}
printf("打开表成功\n");
//判断账号密码
char **resultp = NULL;
int nrow,ncolumn;
char *sq1 = "select * from user";
ret = sqlite3_get_table(datebase, sq1, &resultp, &nrow, &ncolumn, &errmsg);
if(ret != SQLITE_OK)
{
printf("数据库操作失败\n");
printf("sqlite3_get_table: %s\n",errmsg);
return -1;
}
int i;
int j = 0 ; //表示密码对不对的标记
int flag = 0; //表示账号存不存在
for(i=3; i<(nrow+1)*ncolumn ;i+=3)
{
if(strcmp(resultp[i], msg->fromname) == 0)
{
if (strcmp(resultp[i+1], msg->password) != 0)
{
j = 1;
}
flag = 1;
break;
}
}
//strcpy(msg->signname,resultp[i+2]); //保存个性签名
ret = 0;
if (flag != 1)
{
ret = -2; //表示名字不存在
}
else if (j == 1)
{
ret = -3; //表示密码不对
}
else
strcpy(msg->signname,resultp[i+2]); //保存个性签名
sqlite3_free_table(resultp); //释放空间
return ret; //表示登录成功
}
//修改数据库 (个性签名)
int revise_sign_sqlite(Msg * msg)
{
sqlite3 * datebase = Create_Sqlite();
char *errmsg = NULL;
char buf[200];
sprintf(buf,"update user set Sign_name = '%s' where Name = '%s'",msg->signname,msg->fromname);
int ret = sqlite3_exec(datebase, buf, NULL, NULL,&errmsg);
if(ret != SQLITE_OK)
{
printf("修改数据失败\n");
printf("sqlite3_exec: %s\n",errmsg);
sqlite3_close(datebase); //关闭表
return -1;
}
printf("修改数据成功\n");
sqlite3_close(datebase);
return 0;
}
//修改密码(数据库)
int revise_password_sqlite(Msg * msg)
{
sqlite3 * datebase = Create_Sqlite();
char *errmsg = NULL;
char buf[200];
sprintf(buf,"update user set Password = '%s' where Name = '%s'",msg->password,msg->fromname);
int ret = sqlite3_exec(datebase, buf, NULL, NULL,&errmsg);
if(ret != SQLITE_OK)
{
printf("修改数据失败\n");
printf("sqlite3_exec: %s\n",errmsg);
sqlite3_close(datebase); //关闭表
return -1;
}
printf("修改数据成功\n");
sqlite3_close(datebase);
return 0;
}
//***************************客户端数据库***************************************
//创建数据库用于保存聊天记录
sqlite3 * Create_user_sqlite(Msg * msg)
{
sqlite3 * datebase;
char name[23];
sprintf(name ,"%s.db",msg->fromname);
int ret = sqlite3_open(name, &datebase);
return datebase;
}
//保存聊天记录到数据库中
void save_Chat(Msg *msg)
{
sqlite3 * datebase = Create_user_sqlite(msg);
//打开表
char *errmsg = NULL;
char buf[200] = "create table if not exists chat(时间 TEXT,发送者 TEXT,接收者 TEXT,内容 TEXT)";
int ret = sqlite3_exec(datebase, buf, NULL, NULL, &errmsg);
if(ret != SQLITE_OK)
{
printf("打开表失败\n");
return ;
}
time_t t;
t = time(&t);
char time[100];
strcpy(time,ctime(&t));
int len = strlen(time);
time[len-1] = '\0'; //去掉回车
sprintf(buf,"insert into chat values('%s','%s','%s','%s')",time,msg->fromname,msg->localname,msg->msg);
ret = sqlite3_exec(datebase, buf, NULL, NULL,&errmsg);
if(ret != SQLITE_OK)
{
printf("插入数据失败\n");
return ;
}
sqlite3_close(datebase);
}
//查看聊天记录(调用数据库)
void see_chat(Msg * msg)
{
sqlite3 * datebase = Create_user_sqlite(msg);
//打开表
char *errmsg = NULL;
char buf[200] = "create table if not exists chat(时间 TEXT,发送者 TEXT,接收者 TEXT,内容 TEXT)";
int ret = sqlite3_exec(datebase, buf, NULL, NULL, &errmsg);
if(ret != SQLITE_OK)
{
printf("打开表失败\n");
return ;
}
//查看聊天记录
char **resultp = NULL;
int nrow,ncolumn;
char *sq1 = "select * from chat";
ret = sqlite3_get_table(datebase, sq1, &resultp, &nrow, &ncolumn, &errmsg);
if(ret != SQLITE_OK)
{
printf("数据库操作失败\n");
printf("sqlite3_get_table: %s\n",errmsg);
return ;
}
int i;
for(i=0; i<(nrow+1)*ncolumn ;i++)
{
if(i%4 == 0)
{
printf("\n");
printf("%-25s",resultp[i]);
}
else
printf("%15s",resultp[i]);
}
printf("\n");
sqlite3_free_table(resultp); //释放空间
sqlite3_close(datebase);
sleep(5);
}
/***************************************************************
* 文件名 : client.c/client.h
* 创建时间 : 2017年8月20日19:24:59
* 目录 : D:\苏嵌安装环境\red hat share\leo\项目\聊天室
* 制作者 : 刘铠源
* 目的 : 编写Linux下聊天室中关于客户端的函数
***************************************************************/
#include "client.h"
char myname[20]; //用于保存本地名字
char signname[40]; //用于保存个性签名
char mylocalname[20]; //用于保存传输文件来源的名字
char fsignname[40]; //用于保存文件传输名字
//********************************网络连接***************************
//连接服务器
int Connect_Server(void)
{
//创建套接字
int client_socket = socket(AF_INET, SOCK_STREAM, 0);
if(client_socket == -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));
int ret = connect(client_socket,(struct sockaddr *)&addr, sizeof(addr));
if(ret == -1)
{
perror("connect");
return -1;
}
printf("成功连接到服务器 : %s\n",inet_ntoa(addr.sin_addr));
return client_socket;
}
//访问服务器 主界面
int Ask_server(int client_socket)
{
char ch;
int ret;
while(1)
{
main_menu();
printf("请输入您要做操作\n");
scanf("%c",&ch);
while(getchar() != '\n');
switch(ch)
{
case '1': //注册
regis(client_socket);
break;
case '2': //登录
ret = entry(client_socket);
if (ret == 1)
{
User_used(client_socket); //调用函数表示用户界面
}
break;
case '3': //退出
exit(0);
break;
}
}
}
//********************************界面*******************************
//客户端主界面
void main_menu(void)
{
system("clear");
printf("\n\n\n\n\n\n\n\n");
printf("\t\t1,注册\n");
printf("\t\t2,登录\n");
printf("\t\t3,退出\n");
}
//用户登录界面
void user_menu(void)
{
system("clear");
printf("%s: %s\n",myname,signname);
printf("\n\n\n\n\n\n\n");
printf("\t\t1,群聊\n");
printf("\t\t2,私聊\n");
printf("\t\t3,退出登录\n");
printf("\t\t4,查看聊天记录\n");
printf("\t\t5,显示当前在线人员\n");
printf("\t\t6,修改个性签名\n");
printf("\t\t7,修改密码\n");
printf("\t\t8,传输文件\n");
}
//********************************主界面功能*************************
//注册账号
void regis(int client_socket)
{
Msg msg;
msg.cmd = 1;
printf("注册,请输入账号名: ");
scanf("%s",msg.fromname);
while(getchar() != '\n');
//strcpy(msg.fromname,myname);
printf("注册,请输入密码: ");
scanf("%s",msg.password);
while(getchar() != '\n');
write(client_socket, &msg, sizeof(msg));
read(client_socket, &msg, sizeof(msg));
if(msg.cmd == 1001)
{
printf("注册成功\n");
}
else if (msg.cmd == -1)
{
printf("用户名以存在,注册失败\n");
}
else
{
printf("系统繁忙,注册失败\n");
}
sleep(2);
}
//登录账号
int entry(int client_socket)
{
Msg msg;
msg.cmd = 2;
printf("登录,请输入账号名: ");
scanf("%s",msg.fromname);
while(getchar() != '\n');
//strcpy(msg.fromname,myname);
printf("登录,请输入密码: ");
scanf("%s",msg.password);
while(getchar() != '\n');
write(client_socket, &msg, sizeof(msg));
read(client_socket, &msg, sizeof(msg));
if(msg.cmd == -1) //表示用户不存在
{
printf("登录失败,用户不存在.\n");
sleep(2);
return -1;
}
if(msg.cmd == -2)
{
printf("登录失败,密码错误.\n");
sleep(2);
return -2;
}
if(msg.cmd == 1002)
{
printf("登录成功,登录中...\n");
strcpy(myname,msg.fromname); //保存在线名字
strcpy(signname, msg.signname); //保存个性前面
sleep(2);
return 1;
}
}
//********************************用户界面功能***********************
//用户界面
void User_used(int client_socket)
{
//要进行读写分离
pthread_t read_id;
pthread_create(&read_id, NULL, readMsg, (void *)client_socket);
pthread_detach(read_id); //等待线程分离
char ch;
int i = 1;
while(i)
{
user_menu();
printf("请输入您要做操作\n");
scanf("%c",&ch);
while(getchar() != '\n');
switch(ch)
{
case '1': //群聊
chat_all(client_socket);
break;
case '2': //私聊
chat_one(client_socket);
break;
case '3': //退出登录
entry_out(client_socket);
i = 0;
break;
case '4': //查看聊天记录
look_chat();
break;
case '5': //显示当前在线人数
see_now_time(client_socket);
break;
case '6': //修改个性签名
revise_sign(client_socket);
break;
case '7': //修改密码
revise_password(client_socket);
break;
case '8': //传输文件
transfer_file(client_socket);
break;
case 'y': //表示愿意接受文件
transfer_file_y(client_socket);
break;
case 'n': //表示不愿意接受文件
transfer_file_n(client_socket);
break;
}
}
}
//读写分离
void * readMsg (void *v)
{
int client_socket = (int)v;
Msg buf;
int i = 0;
while(1)
{
//Msg buf;
bzero(&buf,sizeof(buf));
int ret = read(client_socket, &buf, sizeof(Msg));
if(ret == -1)
{
perror("read");
break;
}
switch(buf.cmd)
{
case 3: //群聊
printf("收到了一条消息: %s\n",buf.msg);
save_Chat(&buf); //保存聊天记录
break;
case 4: //私聊
printf("%s 给你发了一条消息: %s\n",buf.fromname,buf.msg);
save_Chat(&buf); //保存聊天记录
break;
case -3: //私聊失败
printf("私聊失败,用户不存在或下线\n");
break;
case 5 : //退出登录
printf("%s 退出登录\n",buf.fromname);
sleep(1);
pthread_exit(NULL); //线程退出
break;
case 6 : //显示当前在线人数
printf("当前在线人员:\n");
printf("%s\n",buf.msg);
break;
case 7 : //修改个性签名成功
strcpy(signname,buf.signname);
printf("修改个性签名成功\n");
break;
case -7 : //修改个性签名失败
printf("修改个性签名失败\n");
break;
case 8 : //修改密码成功
printf("修改密码成功\n");
break;
case -8 : //修改密码失败
printf("修改密码失败\n");
break;
/*************错误思路
//传输文件++++++++++++++++++++++++++++++++++++
case 9 : //调用函数确认是否接受文件
receive1_file(client_socket, &buf);
break;
case -9 : //发送文件失败,找不到该好友
printf("发送文件失败,好友不在线或不存在\n");
break;
case -10 : //发送文件失败,对方拒绝
printf("发送文件失败,好友拒绝接受文件\n");
break;
case 10 : //表示确认通过,直接传输文件
receive2_file_now(client_socket, &buf);
break;
case 11 : //表示直接接受
save3_file_now(&buf);
break;
*/
case 9 : //调用函数确认是否接受文件
system("clear");
printf("请问你是否接受来自 %s 的文件 %s(y/n)\n",buf.fromname,buf.signname);
strcpy(mylocalname,buf.fromname); //保存传输文件来源名字
sleep(1);
break;
case -9 : //表示传输文件失败,没有找到该人
printf("发送文件失败,好友不在线或不存在\n");
break;
case 10 : //表示愿意接受文件,开始传输
start_transfer_file(client_socket);
break;
case -10 : //表示不愿意接受文件
printf("发送文件失败,后又拒绝接受文件\n");
break;
case 11 : //接受文件
save_transfer_file(&buf);
i++;
break;
//printf("%d\n",i);
default:break;
}
}
}
//群聊
void chat_all(int client_socket)
{
Msg msg;
msg.cmd = 3;
strcpy(msg.fromname,myname);
strcpy(msg.localname,"All");
printf("亲输入你要群发送的信息\n");
scanf("%s",msg.msg);
while(getchar() != '\n');
write(client_socket, &msg, sizeof(Msg));
//save_Chat(msg); //保存聊天记录
sleep (2);
}
//私聊
void chat_one(int client_socket)
{
Msg msg;
msg.cmd = 4;
printf("请输入你要聊天的对象:\n");
scanf ("%s",msg.localname);
while(getchar() != '\n');
printf("请输入要发送的内容: \n");
scanf("%s",msg.msg);
while(getchar() != '\n');
strcpy (msg.fromname,myname);
write(client_socket, &msg, sizeof(Msg));
save_Chat(&msg); //保存聊天记录
sleep(2);
}
//退出登录
void entry_out(int client_socket)
{
Msg msg;
msg.cmd = 5;
strcpy (msg.fromname,myname);
write(client_socket, &msg, sizeof(Msg));
}
//查看聊天记录
void look_chat(void)
{
Msg msg;
strcpy(msg.fromname,myname);
see_chat(&msg);
}
//显示当前在线人数
void see_now_time(int client_socket)
{
Msg msg;
msg.cmd = 6;
strcpy (msg.fromname,myname);
write(client_socket, &msg, sizeof(Msg));
sleep(2);
}
//修改个性签名
void revise_sign(int client_socket)
{
Msg msg;
msg.cmd = 7;
printf("请输入新的个性签名:");
scanf("%s",msg.signname);
while(getchar() != '\n');
strcpy(msg.fromname,myname); //需要保存名字
write(client_socket, &msg, sizeof(Msg));
sleep(2);
}
//修改密码
void revise_password(int client_socket)
{
Msg msg;
msg.cmd = 8;
printf("亲输入新的密码: ");
scanf("%s",msg.password);
while(getchar() != '\n');
strcpy(msg.fromname,myname); //需要保存名字
write(client_socket, &msg, sizeof(Msg));
sleep(2);
}
//+++++++++++++++++++++++++++传输文件我另起一节,这个真的不好做+++++++++++++++++
//传输文件
void transfer_file(int client_socket)
{
Msg msg;
msg.cmd = 9;
printf("请输入你要传输文件的对象:");
scanf ("%s",msg.localname);
while(getchar() != '\n');
printf("请输入你要传输的本地文件名:");
scanf ("%s",msg.signname);
while(getchar() != '\n');
strcpy(msg.fromname,myname); //需要保存名字
write(client_socket, &msg, sizeof(Msg));
printf("等待验收中...\n");
strcpy(fsignname,msg.signname);
strcpy(mylocalname,msg.localname);
sleep(2);
}
/*因为读写分离,只能一端读,一端写,思路错误
//确认是否接受文件(读线程函数)
void receive1_file(int client_socket, Msg * buf)
{
//system("clear");
printf("请问你是否接受来自 %s 的文件 %s(y/n)\n",buf->localname,buf->signname);
char c;
scanf("%s",&c);
//while(getchar() != '\n');
if (c == 'y')
{
buf->cmd = 2000;
}
write(client_socket, buf, sizeof(Msg));
}
//表示确认通过,直接传输文件
void receive2_file_now(int client_socket, Msg * buf)
{
//printf("%s\n",buf->signname);
int fd = open(buf->signname,O_RDONLY); //没有能判断打开失败
if(fd == -1)
{
perror("open");
printf("传输失败\n");
return ;
}
//buf->cmd = 11;
int ret = 0;
while(ret = read(fd,buf->msg,SIZE))
{
if(ret == -1)
{
perror("read");
break;
}
if (ret == 0)
{
break;
}
buf->num = ret;
write(client_socket, buf, sizeof(Msg));
}
printf("文件复制完成传输\n");
close(fd);
}
//表示直接接受
void save3_file_now(Msg * buf)
{
int fd = open(buf->signname,O_RDWR|O_CREAT|O_APPEND,0777);
write(fd,buf->msg,buf->num);
if(buf->num != SIZE)
{
printf ("文件接受完成\n");
}
close (fd);
}
*/
//表示愿意接受文件
void transfer_file_y(int client_socket)
{
Msg msg;
msg.cmd = 10;
strcpy(msg.fromname,myname);
strcpy(msg.localname,mylocalname);
strcpy(mylocalname,"\0"); //用完后至零
write(client_socket, &msg, sizeof(Msg));
}
//表示不愿意接受文件
void transfer_file_n(int client_socket)
{
Msg msg;
msg.cmd = -10;
strcpy(msg.fromname,myname);
strcpy(msg.localname,mylocalname);
strcpy(mylocalname,"\0"); //用完后至零
write(client_socket, &msg, sizeof(Msg));
}
//传出文件来源开始传输文件
void start_transfer_file(int client_socket)
{
Msg msg;
msg.cmd = 11;
strcpy(msg.fromname,myname);
strcpy(msg.signname,fsignname);
strcpy(msg.localname,mylocalname);
int fd = open(msg.signname,O_RDONLY);
if(fd == -1)
{
perror("open");
printf("传输失败\n");
return ;
}
int ret = 0;
int i = 0;
while(ret = read(fd,msg.msg,SIZE))
{
if(ret == -1)
{
perror("read");
break;
}
if (ret == 0)
{
break;
}
msg.num = ret;
write(client_socket, &msg, sizeof(Msg));
usleep(10000); //这个睡眠时间减缓传输速度,降低不可预祝错误出现
}
printf("文件复制完成传输\n");
strcpy(fsignname,"\0");
strcpy(mylocalname,"\0");
close(fd);
}
//接受文件
void save_transfer_file(Msg * buf)
{
int fd = open(buf->signname,O_WRONLY|O_CREAT|O_APPEND,0777);
write(fd,buf->msg,buf->num);
if(buf->num != SIZE)
{
printf ("文件接受完成\n");
}
close (fd);
}
int main()
{
int client_socket = Connect_Server();
Ask_server(client_socket);
close(client_socket);
return 0;
}
/***************************************************************
* 文件名 : error.c/error.h
* 创建时间 : 2017年8月20日13:44:51
* 目录 : D:\苏嵌安装环境\red hat share\leo\项目\聊天室
* 制作者 : 刘铠源
* 目的 : 编写Linux下聊天室中报错系统的函数
***************************************************************/
#include"error.h"
//往屏幕输出错误原因
void myerror (char *str)
{
char *msg = myStrError ();
printf("%s: %s\n",str,msg);
}
//往字符串输入错误原因
char * myStrError (void)
{
switch (errno)
{
case SOCKET_INIT :
return "初始化套接字失败.";
case SOCKET_ACCEPT :
return "链接客户端失败.";
case CREATE_SQLITE:
return "打开数据库失败.";
case SAVE_SQLITE:
return "数据库插入数据失败,";
}
}
最后自己写了个Makefile
server:sqlite3.o server.o error.o
gcc sqlite3.o server.o error.o -o server -lsqlite3 -lpthread
client:client.o error.o sqlite3.o
gcc client.o error.o sqlite3.o -o client -lsqlite3 -lpthread
sqlite3.o:sqlite3.c
gcc -c sqlite3.c
server.o:server.c
gcc -c server.c
client.o:client.c
gcc -c client.c
error.o:error.c
gcc -c error.c
.PHONY:clean
clean:
rm -f *.o
rm -f server
rm -f client
clear:
rm *.db
目前就是这样,以后肯定会更新.