聊天室项目

来源:互联网 发布: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;}
原创粉丝点击