Linux下C语言项目—聊天室的搭建1.0
来源:互联网 发布:js去掉属性值 编辑:程序博客网 时间:2024/05/16 14:34
之所以为1.0,是因为虽然能运行,但有些地方的边际条件并没有得到补充,很容易产生错误,先上代码吧。
一个在 Linux 下可以使用的聊天软件,要求至少实现如下功能:
1. 采用 Client/Server 架构
2. Client A 登陆聊天服务器前,需要注册自己的 ID 和密码
3. 注册成功后,Client A 就可以通过自己的 ID 和密码登陆聊天服务器
4. 多个 Client X 可以同时登陆聊天服务器之后,与其他用户进行通讯聊天
5. Client A 成功登陆后可以查看当前聊天室内其他在线用户 Client x
6. Client A 可以选择发消息给某个特定的 Client X,即”悄悄话”功能
7. Client A 可以选择发消息全部的在线用户,即”群发消息”功能
8. Client A 在退出时需要保存聊天记录
9. Server 端维护一个所有登陆用户的聊天会的记录文件,以便备查
服务器端
#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>#include <sys/stat.h>#include <fcntl.h>#define FALSE 0#define TRUE 1#define PORT 9999#define SIZE 1024// 协议struct Msg{ char msg[1024]; // 消息内容 char name[20]; // 用户账号 char password[20];// 用户密码 char flag[2]; // 状态(1在线0下线) char toname[20]; char fromname[20]; int cmd; // 消息类型};typedef struct User{ char name[20]; // 用户名 int socket; // 和用户通信的socket}UserData;typedef struct _node{ UserData data; struct _node* next;}Node;Node* h;//创建链表Node * Create_List(){ Node *list = (Node*)malloc(sizeof(Node)/sizeof(char)); if (list == NULL) return NULL; list->next = NULL; // 空表 return list;}//尾插int Insert_Last(Node *h, UserData data){ if (h == NULL) return FALSE; Node *node = (Node*)malloc(sizeof(Node)/sizeof(char)); if (node == NULL) { return FALSE; } node->data = data; node->next = NULL; Node* tmp = h; while (tmp->next) { tmp = tmp->next; } tmp->next = node; return TRUE;}// 初始化套接字,返回监听套接字int init_socket(){ //1、创建socket 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; } // 3、监听本地套接字 ret = listen(listen_socket, 5); if (ret == -1) { perror ("listen"); return -1; } printf ("等待客户端连接.......\n"); return listen_socket;}// 处理客户端连接,返回与连接上的客户端通信的套接字int MyAccept(int listen_socket){ // 4、接收连接 // 监听套接字不能用来与客户端进行通信,它的职责是监听客户端的连接 // accpet 处理客户端的连接,如果成功接收,会返回一个新的套接字,用来与客户端进行通信 // accept的第三个参数 是一个传入传出参数 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;}//创建数据库来保存用户信息void create_sqlite3(){ sqlite3* database; //打开数据库 int ret = sqlite3_open("usermsg.db", &database); if (ret != SQLITE_OK) { printf("打开数据库失败\n"); return; } char *errmsg = NULL; //创建用户信息表 char *sql = "create table if not exists usermsg(name TEXT,password TEXT,msg TEXT,primary key(name))"; ret = sqlite3_exec(database, sql, NULL, NULL, &errmsg); if (ret != SQLITE_OK) { printf("数据库操作失败\n"); return; }}//用户注册void reg(int client_socket, struct Msg *msg){ printf ("%s 进行注册\n", msg->name); sqlite3* database; //打开数据库 int ret = sqlite3_open("usermsg.db", &database); if (ret != SQLITE_OK) { printf("打开数据库失败\n"); return; } // 将用户进行保存 char buf[100];//保存用户账号密码 char *errmsg; sprintf (buf, "insert into usermsg values('%s','%s')", msg->name, msg->password); ret = sqlite3_exec(database, buf, NULL, NULL, &errmsg); if (ret != SQLITE_OK) { msg->cmd = -1; //数据库中已有该账号 } else { msg->cmd = 1001;//注册成功 } write (client_socket, msg, sizeof(struct Msg)); sqlite3_close(database);}//登录账号void login(int client_socket, struct Msg *msg){ UserData data; sqlite3* database; //打开数据库 int ret = sqlite3_open("usermsg.db", &database); if (ret != SQLITE_OK) { printf("打开数据库失败\n"); return; } //检测数据库中是否有该账号 char buf[100]; char *errmsg; char **resultp = NULL; int nrow, ncolumn; sprintf (buf, "select password from usermsg where name = '%s'", msg->name); ret = sqlite3_get_table(database, buf, &resultp, &nrow, &ncolumn, &errmsg); if (ret != SQLITE_OK) { printf ("数据库操作失败:%s\n", errmsg); return; } printf ("正在检测数据中\n"); if(strcmp(resultp[1],msg->password) == 0) { printf("该账号可以登录\n"); msg->cmd = 1002; strcpy(data.name,msg->name); data.socket = client_socket; Insert_Last (h,data); } else { printf ("该账号不存在\n"); msg->cmd = -1; } write (client_socket, msg, sizeof(struct Msg));}//打印在线人员void display(int client_socket, struct Msg* msg){ Node* tmp = h->next; while(tmp) { strcpy(msg->name,tmp->data.name); write (client_socket, msg, sizeof(struct Msg)); tmp = tmp->next; }}//群聊void group_chat(int client_socket, struct Msg* msg){ printf ("打开数据库成功\n"); Node* tmp = h->next; printf("%s 发了一次群消息\n", msg->name); while(tmp) { write (tmp->data.socket, msg, sizeof(struct Msg)); tmp = tmp->next; }}//悄悄话void private_chat (int client_socket, struct Msg* msg){ printf ("%s 要给 %s 发一条消息\n", msg->fromname, msg->toname); Node* tmp = h->next; while(tmp) { if (strcmp(tmp->data.name, msg->toname)==0) { printf("%s\n",msg->msg); write (tmp->data.socket, msg, sizeof(struct Msg)); return; } tmp = tmp->next; } if (tmp == NULL) { msg->cmd = -3; write (client_socket, msg, sizeof(struct Msg)); }}//用户下线void quit_user(int client_socket, struct Msg* msg){ printf ("接收到客户端下线请求\n"); Node* tmp = h; while(tmp->next) { if (strcmp(tmp->next->data.name, msg->name)==0) break; tmp = tmp->next; } if (tmp->next == NULL) { msg->cmd = -4; write (client_socket, msg, sizeof(struct Msg)); } Node* p = tmp->next; tmp->next = p->next; free(p); write (client_socket, msg, sizeof(struct Msg));}//查看聊天记录void check_msg(int client_socket, struct Msg* msg){ printf ("正在赋予客户端检查聊天记录的权限\n"); //在客户端自己创建一个数据库 write (client_socket, msg, sizeof(struct Msg));}//修改密码void change_password(int client_socket, struct Msg* msg){ printf ("%s申请修改密码\n",msg->name); sqlite3 * database; // 打开数据库 int ret = sqlite3_open("usermsg.db", &database); if (ret != SQLITE_OK) { printf ("打开数据库失败\n"); return; } printf ("打开数据库成功\n"); char buf[100]; char *errmsg = NULL; sprintf (buf, "update usermsg set password = '%s' where name = '%s'", msg->password, msg->name); ret = sqlite3_exec(database, buf, NULL, NULL, &errmsg); if (ret != SQLITE_OK) { printf ("数据库操作失败:%s\n", errmsg); return; } write (client_socket, msg, sizeof(struct Msg));}//发送文件void send_file(int client_socket, struct Msg* msg){ printf("%s收到文件传输请求\n",msg->toname); int fd2 = open("2.txt", O_WRONLY|O_CREAT, 0777); if (fd2 == -1) { perror ("open fd2"); return; } char buf[SIZE] = {0}; strcpy(buf, msg->msg); write (fd2, buf, strlen(buf)); 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) { printf ("客户端退出\n"); break; } switch (msg.cmd) { case 1 : // 客户端进行注册 reg(client_socket, &msg); break; case 2 : // 客户端进行登录 login(client_socket, &msg); break; case 3 : // 打印在线人员 display(client_socket, &msg); break; case 4 : // 群聊 group_chat(client_socket, &msg); break; case 5 : // 悄悄话 private_chat(client_socket, &msg); break; case 6 : // 下线 quit_user(client_socket, &msg); break; case 7 : // 查看聊天记录 check_msg(client_socket, &msg); break; case 8 : //修改密码 change_password(client_socket, &msg); break; case 9 : //文件传输 send_file(client_socket, &msg); break; } } close (client_socket);}int main(){ // 初始化套接字 int listen_socket = init_socket(); h = Create_List(); 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 <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>#include <time.h>#include <sys/stat.h>#include <fcntl.h>#define PORT 9999#define SIZE 1024struct Msg{ char msg[1024]; // 消息内容 char name[20]; // 用户账号 char password[20]; // 用户密码 char flag[2]; // 状态(1在线0下线) char toname[20]; char fromname[20]; int cmd; // 消息类型};char Myname[20]; // 界面void interface(){ struct tm *ptr; time_t lt; lt =time(NULL); ptr = gmtime(<); printf(asctime(ptr)); 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");}//创建数据库void create_sqlite3(){ sqlite3* database; //打开数据库 int ret = sqlite3_open("chatmsg.db", &database); if (ret != SQLITE_OK) { printf("打开数据库失败\n"); return; } char *errmsg = NULL; //创建用户信息表 char *sql = "create table if not exists chatmsg(time TEXT, fromname TEXT, toname TEXT, massage TEXT,primary key(time))"; ret = sqlite3_exec(database, sql, NULL, NULL, &errmsg); if (ret != SQLITE_OK) { printf("数据库操作失败: %s\n", errmsg); return; }}//插入聊天记录char insert_msg(struct Msg msg,int judge){ struct tm *ptr; time_t lt; lt = time(NULL); ptr = gmtime(<); sqlite3 * database; // 打开数据库 int ret = sqlite3_open("chatmsg.db", &database); if (ret != SQLITE_OK) { printf ("打开数据库失败\n"); return -1; } int i = 0; char time[30]; strcpy(time,asctime(ptr)); while (time[i] != '\n') { i++; } time[i] = '\0'; i = 0; char fromname[20]; strcpy(fromname, msg.name); while (fromname[i] != '\n') { i++; } fromname[i] = '\0'; i = 0; char toname[20]; strcpy(toname, msg.toname); while (toname[i] != '\n') { i++; } toname[i] = '\0'; i = 0; char massage[100]; strcpy(massage, msg.msg); while (massage[i] != '\n') { i++; } massage[i] = '\0'; char buf[100]; if (judge == 1) { strcpy(toname, "all"); sprintf (buf, "insert into chatmsg values('%s', '%s', '%s', '%s')", time, fromname, toname, massage); } else if (judge == 0) { sprintf (buf, "insert into chatmsg values('%s', '%s', '%s', '%s')", time, fromname, toname, massage); } char *errmsg = NULL; ret = sqlite3_exec(database, buf, NULL, NULL, &errmsg); if (ret != SQLITE_OK) { printf ("数据库操作失败:%s\n", errmsg); return -1; } sqlite3_close(database);}//检查数据库聊天记录int check_msg(){ sqlite3 * database; // 打开数据库 int ret = sqlite3_open("chatmsg.db", &database); if (ret != SQLITE_OK) { printf ("打开数据库失败\n"); return -1; } // 3、char ***resultp: char *resultp[] // 4、nrow: 多少行数据 // 5、ncolumn: 多少列数据 char *errmsg = NULL; char **resultp = NULL; int nrow, ncolumn; char *sql = "select * from chatmsg"; ret = sqlite3_get_table(database, sql, &resultp, &nrow, &ncolumn, &errmsg); if (ret != SQLITE_OK) { printf ("数据库操作失败:%s\n", errmsg); return -1; } int i; for (i = 0; i < (nrow+1)*ncolumn; i++) { if (i % ncolumn == 0) printf ("\n"); printf ("%-25s", resultp[i]); } printf ("\n"); // 关闭数据库 sqlite3_close(database); return 0;}void * readMsg(void *v){ int socketfd = (int)v; struct Msg msg; while (1) { read (socketfd, &msg, sizeof(msg)); switch (msg.cmd) { case 3: // 打印在线人员 printf("%s\n",msg.name); break; case 4: // 群聊 printf ("收到一条消息:%s\n", msg.msg); break; case 5: // 悄悄话 printf ("%s 给你发一条消息:%s\n", msg.fromname, msg.msg); break; case -3 : printf ("该好友不存在\n"); break; case 6 : //用户下线 printf ("服务器正在处理中\n"); sleep(1); printf ("下线成功\n"); printf ("请按ENTER退出\n"); return; case -4 : printf ("不存在该账号在线\n"); printf ("请按ENTER退出\n"); break; case 7 : //查看聊天记录 printf ("您的聊天记录: \n"); check_msg(); printf ("请按ENTER退出\n"); break; case 8 : //修改密码 sleep(1); printf ("服务器正在处理中\n"); printf ("处理成功\n"); printf ("请按ENTER退出\n"); break; case 9 : //文件传输 printf ("文件极速传输中,请稍后\n"); sleep(1); printf ("文件传输成功\n"); printf ("请按ENTER退出\n"); break; } }}//用户注册void reg(int socketfd){ struct Msg msg; msg.cmd = 1; char name[20]; char password[20]; printf("请输入您的用户名:\n"); scanf("%s",name); while(getchar() != '\n'); strcpy(msg.name, name); printf("请输入您的密码: \n"); scanf("%s",password); while(getchar() != '\n'); strcpy(msg.password, password); //输入账号密码 write (socketfd, &msg, sizeof(msg)); read (socketfd, &msg, sizeof(msg)); if (msg.cmd == 1001) { printf ("注册成功\n"); sleep(1); } else if (msg.cmd == -1) { printf ("该账号已被注册,请重新操作\n"); printf ("正在返回界面\n"); sleep(1); }}//用户登录void login(int socketfd){ struct Msg msg; msg.cmd = 2; char name[20]; char password[20]; printf("请输入您的用户名:\n"); scanf ("%s",Myname); while(getchar() != '\n'); strcpy(msg.name, Myname); printf("请输入您的密码: \n"); scanf("%s",password); while(getchar() != '\n'); strcpy(msg.password, password); //输入账号密码 write (socketfd, &msg, sizeof(msg)); read (socketfd, &msg, sizeof(msg)); if (msg.cmd == -1) { printf ("登录失败\n"); } else if (msg.cmd == 1002) { printf ("登陆成功\n"); } sleep(1); pthread_t id; pthread_create(&id, NULL, readMsg, (void *)socketfd); pthread_detach(id); // 线程分离}// 打印在线人员void display(int socketfd){ struct Msg msg; msg.cmd = 3; write (socketfd, &msg, sizeof(msg));}//群聊void group_chat(int socketfd){ struct Msg msg; msg.cmd = 4; printf ("请输入要发送的内容: \n"); fgets(msg.msg, 1024, stdin); strcpy(msg.name,Myname); insert_msg(msg,1); write (socketfd, &msg, sizeof(msg));}//悄悄话void private_chat (int socketfd){ struct Msg msg; msg.cmd = 5; printf ("请输入要发送的对象名称: \n"); fgets(msg.toname, 20, stdin); char *tmp = msg.toname; while(tmp) { if(*tmp == '\n') { *tmp = '\0'; break; } tmp++; } printf ("请输入要发送的内容: \n"); fgets(msg.msg, 1024, stdin); strcpy (msg.fromname, Myname); strcpy (msg.name, Myname); insert_msg(msg,0); write (socketfd, &msg, sizeof(msg));}//用户下线void quit_user (int socketfd){ struct Msg msg; msg.cmd = 6; strcpy(msg.name,Myname); write (socketfd, &msg, sizeof(msg));}//查看聊天记录void check(int socketfd){ struct Msg msg; msg.cmd = 7; write (socketfd, &msg, sizeof(msg));}//修改密码void change_password(int socketfd){ struct Msg msg; msg.cmd = 8; printf ("请输入您要修改的密码:\n"); char new_password[20]; scanf ("%s",new_password); strcpy(msg.name, Myname); strcpy(msg.password,new_password); write (socketfd, &msg, sizeof(msg));}//发送文件void send_file(int socketfd){ struct Msg msg; msg.cmd = 9; printf("请输入要发送的对象名称\n"); fgets(msg.toname, 20, stdin); char *tmp = msg.toname; while(tmp) { if(*tmp == '\n') { *tmp = '\0'; break; } tmp++; } int fd1 = open("1.txt", O_RDONLY); if (fd1 == -1) { perror ("open fd1"); return; } int ret = 0; char buf[SIZE] = {0}; ret = read(fd1, buf, 1024); buf[ret] = '\0'; strcpy(msg.msg,buf); write (socketfd, &msg, sizeof(msg));}// 客户端向服务器发送数据void ask_server(int socketfd){ char ch[2]; while (1) { interface(); scanf("%c",ch); while(getchar()!= '\n'); switch(ch[0]) { case '1': // 注册 reg(socketfd); break; case '2': // 登录 login(socketfd); break; case '3': //打印在线人员 display(socketfd); break; case '4': //群聊 group_chat(socketfd); break; case '5': //悄悄话 private_chat(socketfd); break; case '6': //用户下线 quit_user(socketfd); break; case '7': //查看聊天记录 check(socketfd); break; case '8': //修改密码 change_password(socketfd); break; case '9': //文件传输 send_file(socketfd); break; } system("clear"); }}int main(){ create_sqlite3(); // 创建与服务器通信的套接字 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;}
之后会不断修改添加功能。
阅读全文
0 0
- Linux下C语言项目—聊天室的搭建1.0
- 【C语言】linux下简单的聊天室程序(TCP+多线程)
- 利用多线程实现linux下C语言的聊天室程序:
- Linux c语言 聊天室
- LINUX 下的网络聊天室项目
- Linux下C实现的聊天室
- Linux下C实现的聊天室
- Linux下C实现的聊天室
- 网络编程项目:linux下基于C/S架构的聊天室
- linux下基于TCP协议的多线程聊天室的搭建
- Linux下C语言编程环境的搭建
- 聊天室项目(二)界面的搭建
- 我是这样学习Linux下C语言编程的-Linux下C语言编程环境的搭建
- 我是这样学习Linux下C语言编程的-Linux下C语言编程环境的搭建
- linux系统下用socket通讯编写网络聊天室(C语言)
- C语言聊天室
- Linux C实现简单的网络聊天室
- 在linux下做的多用户聊天室
- java类中构造方法执行的顺序
- 个人对于app开发的感悟
- 【洛古豪华游轮】题解报告
- 编程范式4 笔记 C语言泛型
- python实现工作区间问题
- Linux下C语言项目—聊天室的搭建1.0
- HashMap深入理解
- opensns 5.0手机版消息列表赞消息链接不到所赞微博的修复
- UVA
- C++中堆栈的区别
- dubbo + jenkins持续集成 shell 脚本样例
- 浅谈PWM控制电机
- 虚拟机和物理机之间互ping【局域网内ping不通,防火墙规则更改(win7为例)】
- 常见的用户密码加密及破解方法