有一陣子沒寫博客了,這個項目寫到這,我覺得有必要記錄一下了.中間有很多無用的備註,思路和格式也寫的不是很好,沒辦法,水平有限.
在關於傳輸文件方面,還有一些不可預知的錯誤,暫時我還沒有發現原因,只能減緩傳輸速度來降低這種錯誤出現的概率.超級用戶還沒有寫,不過功能也不算少了.
優先頭文件
/***************************************************************
* 文件名 : 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 "數據庫插入數據失敗,";
}
}
最後自己寫了個Makefileserver: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
目前就是這樣,以後肯定會更新.