聊天室项目
来源:互联网 发布:2017淘宝一天的交易额 编辑:程序博客网 时间:2024/05/16 15:47
1、简述:
采用了cs结构
服务器端四步走:socket,bind,listen,accept
客户端两步走:socket,connect
简单模型:客户端a消息发送到服务器;服务器找到客户端b的socket,在把消息发送给客户端b;
设计数据库操作存储的一些操作比较简单这里不提
2、看代码:
(1)、服务器端代码
#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>#define PORT 9999struct User{ char name[20]; // 用户名 int socket; // 和用户通信的socket};// 协议struct Msg{ char information[1024]; //发送消息 char msg[1024]; // (姓名)消息内容 int id; char sex[20]; int age; int password; char toname[20]; char fromname[20]; int cmd; // 消息类型 char online_name[20]; //在线姓名;};struct User user[2000] = {0};//打开数据库函数,客户端用户登录数据传递过来,服务器端打开数据库比较查询是否匹配此登录用户信息;int Database_enter(int client_socket,struct Msg *msg){ sqlite3 *database; int ret = sqlite3_open( "information.db", &database ); if(ret != SQLITE_OK) { perror("sqlite3_open"); return -1; } printf("数据库打开成功\n"); // 3、char ***resultp: char *resultp[] = {"id", "name", "sex", "age", "1", "zhang", "M", "12","2".....}; // 4、nrow: 多少行数据 // 5、ncolumn: 多少列数据 char ** resultp; char *errmsg = NULL; int nrow; int ncolumn; //进行什么操作; char *sql = "select * from information"; ret = sqlite3_get_table(database, sql, &resultp, &nrow, &ncolumn, &errmsg ); if(ret != SQLITE_OK) { printf("数据库操作失败 %s\n",errmsg); return -1; } //查询之后,把客户端传递过来的数据和这张表里的数据想比较,先比较唯一的name;再比较password; int i; for(i = 0 ;i < (nrow+1)*ncolumn;i++) { if (strncmp (resultp[i],msg->msg,strlen(msg->msg)) == 0) //if (strncmp (resultp[i],msg->msg,strlen(msg->msg)-1) == 0) { if(atoi(resultp[i+1]) == msg->password) //强制转换; { printf("服务器端已经通过登录验证\n"); msg->cmd = 1002; int k; for(k = 0 ;k < 2000; k++) { if(user[k].socket == 0) { strcpy(user[k].name, msg->msg); user[k].socket = client_socket; printf("名字:%s\n",user[k].name); break; } } break; } else { printf("服务器端发现你的密码不正确哦!\n"); msg->cmd = -1; break; } } } if(i == (nrow+1)*ncolumn) { msg->cmd = -2; printf("服务器端没找到该用户\n"); } sqlite3_free_table(resultp); //之前的char**resultp (数组)等于malloc了一个空间来储存数据库的数据;所以需要释放; sqlite3_close(database); printf("%d,%d\n",msg->cmd,sizeof(msg));}//私聊数据库函数int Database_chat2(int client_socket,struct Msg *msg){ sqlite3 *database; int ret = sqlite3_open( "information.db", &database ); if(ret != SQLITE_OK) { perror("sqlite3_open"); return -1; } printf("数据库打开成功\n"); // 3、char ***resultp: char *resultp[] = {"id", "name", "sex", "age", "1", "zhang", "M", "12","2".....}; // 4、nrow: 多少行数据 // 5、ncolumn: 多少列数据 char ** resultp = NULL; char *errmsg = NULL; int nrow; int ncolumn; //进行什么操作; char *sql = "select * from information"; ret = sqlite3_get_table(database, sql, &resultp, &nrow, &ncolumn, &errmsg ); if(ret != SQLITE_OK) { printf("数据库操作失败 %s\n",errmsg); return -1; } //查询之后,把客户端传递过来的数据和这张表里的数据想比较,先比较唯一的name;再比较password; int i; for(i = 0 ;i < (nrow+1)*ncolumn;i++) { if (strncmp (resultp[i],msg->toname,strlen(msg->toname)) == 0) // if (strncmp (resultp[i],msg->toname,strlen(msg->toname)-1) == 0) { printf("222222\n"); msg->cmd = 11; return 11; } } if(i == (nrow+1)*ncolumn) { msg->cmd = -5; return -5; } sqlite3_free_table(resultp); //之前的char**resultp (数组)等于malloc了一个空间来储存数据库的数据;所以需要释放; sqlite3_close(database);}//数据库写入注册数据函数int Database_insert( struct Msg *msg){ sqlite3 *database; //打开数据库,数据库指针指向你要打开的数据库,studtet.db是你创建的数据库文件 int ret = sqlite3_open( "information.db", &database ); if(ret != SQLITE_OK) { perror("sqlite3_open"); return -1; } printf("数据库打开成功\n"); //下边一大段是创建一个表 char *errmsg = NULL; //sql语句 char *sql = "create table if not exists information (name TEXT, PASSWORD INTEGER, primary key(name))"; //执行sql语句的函数; ret = sqlite3_exec(database ,sql, NULL ,NULL ,&errmsg); if(ret != SQLITE_OK) { printf("数据库创建操作失败 %s\n",errmsg); return -1; } //往表里边插入数据; //insert into student values(1, 'Zhang', 'M', 18); char buf[100]; sprintf(buf," insert into information values('%s',%d)",msg->msg ,msg->password ); //设置主键之后,如果插入的id已经存在那么就会返回错误,一班来说返回的错误都是插入重复这里有点取巧; ret = sqlite3_exec(database ,buf, NULL ,NULL ,&errmsg); if (ret != SQLITE_OK) { printf ("数据库插入操作失败:%s\n", errmsg); return -2; } printf("数据库已经成功插入了你的注册信息\n"); sqlite3_close(database); return 1;}// 初始化套接字,返回监听套接字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 reg(int client_socket, struct Msg *msg){ printf ("%s 正在进行注册\n", msg->msg); //下边的代码就是应该把注册的消息写进数据库; int ret = Database_insert( msg ); if(ret == -2) { printf("该用户id已经被注册 \n"); msg->cmd = -2; } else { msg->cmd = 1001; } //msg->cmd = 1001; /* { // 将用户套接字进行保存,保存在数组里 int i = 0; for (i = 0; i < 20; i++) { if (user[i].socket == 0) { strcpy (user[i].name , msg->msg); user[i].socket = client_socket; break; } } if (i == 2000) { msg->cmd = -1; } else { msg->cmd = 1001; } } */ write (client_socket, msg, sizeof(struct Msg));}//登录验证函数; void enter(int client_socket, struct Msg *msg) { Database_enter(client_socket,msg ); write(client_socket, msg ,sizeof(struct Msg)); printf("11111111\n"); }// 群发void chat(int client_socket, struct Msg *msg){ printf ("%s 发一次群消息\n", msg->msg); // 将用户进行保存 int i = 0; for (i = 0; i < 2000; i++) { if (user[i].socket != 0) { write (user[i].socket, msg, sizeof(struct Msg)); } }}// 私聊void chat2(int client_socket, struct Msg *msg){ printf ("%s 要 给 %s 发一条消息\n", msg->fromname, msg->toname); //判断数据库是够有此用户 int ret = Database_chat2(client_socket,msg); if(ret == -5) { printf("此用户不存在\n"); write(client_socket,msg,sizeof(struct Msg)); } //用户才在于数据库,查找是否在线? else if(ret == 11) { printf("数据库存在此用户,正在判断该用户是否在线\n"); //判断用户是否在线; int i = 0; for (i = 0; i < 2000; i++) { if (user[i].socket != 0 && strncmp(user[i].name, msg->toname,strlen(msg->toname))==0) // if (user[i].socket != 0 && strncmp(user[i].name, msg->toname,strlen(msg->toname)-1)==0) { printf("11111111\n"); msg->cmd = 10; write(client_socket,msg,sizeof(struct Msg)); printf("服务器判断出该用户在线,正在给该用户发送消息....\n"); msg->cmd = 3; write (user[i].socket, msg, sizeof(struct Msg)); printf("1111111111\n"); break; } } if(i == 2000) { printf("服务器显示该用户不在线\n"); msg->cmd = -6; write(client_socket,msg,sizeof(struct Msg)); } }}//显示在线好友列表;void display(int client_socket,struct Msg *msg){ //服务器端把user[]里边的在线遍历出来发送给客户端; int i; msg->cmd = 4; for(i = 0;i < 2000;i++) { if(user[i].socket != 0) { //遍历一次写给客户端一次; strncpy(msg->online_name ,user[i].name,strlen(user[i].name)); write(client_socket, msg ,sizeof(struct Msg)); printf("44444444\n"); } }}//注销用户信息int logout(int client_socket, struct Msg *msg){ sqlite3 * database; // 打开数据库 int ret = sqlite3_open("information.db", &database); if (ret != SQLITE_OK) { printf ("打开数据库失败\n"); return -1; } printf ("打开数据库成功\n"); char *errmsg = NULL; char buf[100]; sprintf (buf, "delete from information where name = '%s'", msg->msg); ret = sqlite3_exec(database, buf, NULL, NULL, &errmsg); if (ret != SQLITE_OK) { printf ("数据库操作失败:%s\n", errmsg); return -1; } printf("注销成功\n"); write(client_socket,msg, sizeof(struct Msg)); printf("msg.cmd %d\n",msg->cmd); sqlite3_close(database);}// 把 负责处理客户端通信的函数改成线程的工作函数void* hanld_client(void* v){ int client_socket = (int)v; struct Msg msg; while(1) { // 从客户端读一个结构体数据 int ret = read(client_socket, &msg, sizeof(msg)); //printf("2:%d,%d\n",msg.cmd,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 : // 客户端进行群聊天 chat(client_socket, &msg); break; case 3 : // 客户端进行私聊 chat2(client_socket, &msg); break; case 4: //客户进行登录; enter(client_socket,&msg); break; case 5: //显示在线好友列表; display(client_socket,&msg); break; case 6: //注销用户; logout(client_socket,&msg); break; } } close (client_socket);}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;}
(2)、客户端代码
这里写代码片#include <sys/types.h>#include <sys/socket.h>#include <stdio.h>#include <unistd.h>#include <string.h>#include <arpa/inet.h>#include <sqlite3.h> #define PORT 9999void enter( int );char myName[20];char Sex[20];struct Msg{ char information[1024]; //发送消息; char msg[1024]; // (姓名)消息内容 int id; char sex[20]; int age; int password; char toname[20]; //消息发给谁 char fromname[20]; //消息从谁的那边发出来 int cmd; // 消息类型 char online_name[20]; //在线姓名;};// 主界面;void interface(){ printf ("\t\t 1、注册\n"); printf ("\t\t 2、登录\n");}void interface3(){ //printf ("\t\t 1、注册\n"); printf ("\t\t 2、登录\n");} //登录后的界面;void interface2(){ printf ("\t\t 1、发送文件\n"); printf ("\t\t 2、群聊\n"); printf ("\t\t 3、私聊\n"); printf ("\t\t 4、注销当前用户\n"); printf ("\t\t 5、显示好友列表\n"); printf ("\t\t 6、查看本地聊天记录\n");}//读写分离的读线程的回调函数; void * readMsg(void *v) { int socketfd = (int)v; struct Msg msg; while (1) { read(socketfd, &msg, sizeof(msg)); //printf("test msg.cmd %d,%d\n",msg.cmd,sizeof(msg)); switch (msg.cmd) { case -5: //私聊用户不存在; printf("你想私聊的用户不存在\n"); break; case 10: //用户存在并且在线; printf("用户存在并且在线\n"); break; case -6: //用户不在线; printf("用户存在但是不在线\n"); break; case 2: // 群聊 printf ("收到一条消息: %s\n", msg.information); break; case 3: // 私聊 printf ("%s 给你发一条消息:%s\n", msg.fromname, msg.information); break; case 4: // 显示在线好友列表; printf("在线的好友有:%s\n",msg.online_name); printf("3333333\n"); break; case 6: //用户注销; printf("注销成功!\n"); return; break; } } } //注册函数int reg(int socketfd){ struct Msg msg; //msg.cmd = 1; while(1) { msg.cmd = 1; //memset(&msg,0,sizeof(msg));//清零 printf ("请输入用户名:\n"); scanf("%s",&(msg.msg)); printf ("请输入用户的密码 :\n"); scanf("%d",&(msg.password)); write (socketfd, &msg, sizeof(msg)); //来自服务器端的回复:到底有没有注册成功,成功了返回一个标号,要是失败了,那么为什么失败,返回另外一个标号,最后失败的需要全部重新注册 read (socketfd, &msg, sizeof(msg)); if (msg.cmd == 1001) { printf ("你已注册成功,欢迎加入聊天室\n"); /* int a; while (1) { printf("\n"); interface3(); printf("\n"); printf("\n"); sleep(1); printf("\t\t*_*输入功能:\n"); scanf("%d",&a); printf("\n"); while(getchar()!= '\n'); switch(a) { case 2: //登录; enter(socketfd); break; } system("clear"); } */ break; } //用户已经被注册过了,重新注册 if (msg.cmd == -2) { printf ("该用户名已经被注册,请重新进行注册\n"); memset(&msg,0,sizeof(msg));//清零 continue; } //聊天室的登录人数上限已经到达注册失败; else if(msg.cmd == -1) { printf ("聊天室的上限已经到达,请重新进行注册\n"); continue; } } sleep(1);} //群聊函数,void chat(int socketfd){ struct Msg msg; msg.cmd = 2; printf ("请输入要发送的内容: \n"); //fgets(msg.information, 1024, stdin); scanf("%s",msg.information); write (socketfd, &msg, sizeof(msg));} //私聊;void chat2(int socketfd){ struct Msg msg; msg.cmd = 3; printf ("请输入要发送的对象名称: \n"); //fgets(msg.toname, 20, stdin); scanf("%s",msg.toname); printf ("请输入要发送的内容: \n"); //fgets(msg.information, 1024, stdin); scanf("%s",msg.information); printf("输入自己的名字 : \n"); //fgets(msg.fromname, 20, stdin); scanf("%s",msg.fromname); write (socketfd, &msg, sizeof(msg)); //调用存储聊天记录的函数; int ret = insert_msg( &msg ); if(ret == 1) { printf("成功保存本地记录\n"); } } //注销用户;void logout(int socketfd){ char ch[2]; printf("是否注销此用户: y/n \n"); scanf("%c",ch); if(ch[0] == 'y') { struct Msg msg; msg.cmd = 6; printf("输入您的用户名:\n"); scanf("%s",msg.msg); write(socketfd, &msg,sizeof(struct Msg)); } else { return ; }}//显示在线好友列表;void display(int socketfd){ struct Msg msg; printf("我想知道在线的好友列表\n"); msg.cmd = 5; write(socketfd, &msg, sizeof(struct Msg)); } //登录函数;void enter(int socketfd){ struct Msg msg; msg.cmd = 4; printf("请输入用户名 :\n"); scanf("%s",&(msg.msg)); printf("请输入用户密码:\n"); scanf("%d",&(msg.password)); write (socketfd, &msg, sizeof(msg)); //消息写过去,等待回复,例如什么用户不存在,密码错误; if(msg.cmd == 5) { } read(socketfd ,&msg ,sizeof(msg)); //读取来自服务器的返回的消息,进行客户端屏幕打印; //printf("%d,%d\n",msg.cmd,sizeof(msg)); if(msg.cmd == 1002) { printf("恭喜你登录成功\n"); //登录完成之后开始读写分离; sleep(1); pthread_t id; pthread_create(&id, NULL, readMsg, (void *)socketfd); pthread_detach(id); // 线程分离 int a; while (1) { interface2(); printf("\n"); printf("\n"); sleep(1); printf("\t\t*_*输入功能:\n"); scanf("%d",&a); printf("\n"); while(getchar()!= '\n'); switch(a) { case 1: //发送文件 send_file(socketfd); break; case 2: // 群聊天 chat(socketfd); break; case 3: // 私聊 chat2(socketfd); break; case 4 : //注销用户; logout(socketfd); return; case 5: //显示在线好友列表; display(socketfd); sleep(1); break; case 6: //查看本地聊天记录; look_msg(socketfd); sleep(1); break; } system("clear"); } } else if(msg.cmd == -1) { printf("密码不正确\n"); usleep(5*100000); } else if(msg.cmd == -2) { printf("该用户不存在\n"); sleep(0.5); } }//聊天记录存到本地;int insert_msg(struct Msg* msg){ sqlite3 *database; int ret = sqlite3_open( "msg.db", &database ); if(ret != SQLITE_OK) { perror("sqlite3_open"); return -1; } printf("数据库打开成功\n"); //下边一大段是创建一个表 char *errmsg = NULL; char *sql = "create table if not exists msg (fromname TEXT, information TEXT,toname TEXT)"; //sql语句 ret = sqlite3_exec(database ,sql, NULL ,NULL ,&errmsg);//执行sql语句的函数; if(ret != SQLITE_OK) { printf("数据库创建操作失败 %s\n",errmsg); return -1; } //往表插入数据; //insert into student values(1, 'Zhang', 'M', 18); char buf[100]; printf("%s\n",msg->fromname); sprintf(buf," insert into msg values('%s','%s' ,'%s')",msg->fromname ,msg->information ,msg->toname ); //设置主键之后,如果插入的id已经存在那么就会返回错误,一班来说返回的错误都是插入重复这里有点取巧; ret = sqlite3_exec(database ,buf, NULL ,NULL ,&errmsg); if (ret != SQLITE_OK) { printf ("数据库插入操作失败:%s\n", errmsg); return -1; } printf("聊天记录成功保存到本地数据库*_*\n"); sqlite3_close(database); return 1;}//查看本地聊天记录;int look_msg(int socketfd){ //本地创建一张数据库表,存放本地的消息; //查看不就简单了,直接打开本地的数据库就好了; sqlite3 *database; int ret = sqlite3_open( "msg.db", &database ); if(ret != SQLITE_OK) { perror("sqlite3_open"); return -1; } printf("数据库打开成功\n"); char ** resultp; char *errmsg = NULL; int nrow; int ncolumn; //进行什么操作; char *sql = "select * from msg"; ret = sqlite3_get_table(database, sql, &resultp, &nrow, &ncolumn, &errmsg ); if(ret != SQLITE_OK) { printf("数据库操作失败 %s\n",errmsg); return -1; } int i; printf("nrow = %d,ncolumn = %d",nrow, ncolumn); for(i = 0; i < (nrow +1)* ncolumn;i++) { if(i % ncolumn == 0) { printf("\n"); } printf("%-18s" ,resultp[i]); } printf("\n"); sqlite3_free_table(resultp); //之前的char**resultp (数组)等于malloc了一个空间来储存数据库的数据;所以需要释放; sqlite3_close(database);}//传送文件;send_file(int socketfd){ }// 客户端向服务器发送数据处理函数;void ask_server(int socketfd){ int a ; while (1) { //char ch[2]; int a; while (1) { printf("\n"); interface(); printf("\n"); printf("\n"); sleep(1); printf("\t\t*_*输入功能:\n"); scanf("%d",&a); printf("\n"); while(getchar()!= '\n'); switch(a) { case 1: //注册; reg(socketfd); printf("注册注册注册\n"); break; case 2: //登录; enter(socketfd); 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;}
阅读全文
0 0
- 聊天室项目
- 神码组的聊天室项目。
- 聊天室项目分析
- django WEB聊天室项目
- 聊天室项目问题
- Oracle 数据库 聊天室项目
- 结课项目:聊天室
- 网络编程项目(聊天室项目)
- java聊天室源代码(练习项目)
- ParseChat聊天室应用项目源码
- Java聊天室项目的几个问题
- 聊天室项目相关内容知识问答
- 聊天室
- 聊天室
- 聊天室
- 聊天室
- 聊天室
- 聊天室
- 8.3 TensorFlow BP神经网络构建与超参数的选取
- BestTimeToBuyAndSell
- 一、朴素贝叶斯分类
- 射线算法的js实现
- hdu1250(大 数)Hat's Fibonacci
- 聊天室项目
- 手机自动签到简易应用
- mysql 列column常用命令
- 蓝桥杯 K好数(dp)
- Linux命令之uniq
- 使用GTest发生错误"检测到“RuntimeLibrary”的不匹配项: 值“MTd_StaticDebug”不匹配值“MDd_DynamicDebug"
- Btrace入门到熟练小工完全指南
- HDU
- 跟着《Learn Python The Hard Way》学Python(二)