基于Linux的TCP多路复用IO结构网络在线聊天系统

项目要求

一个在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端维护一个所有登陆用户的聊天会的记录文件,以便备查

项目总体设计思路与方案

首先,就是服务器与客户端之间的协议要如何选择,以及整个项目的一个大概框架。这里采用多路转接型架构,使用I/O多路机制。简单来说就是用Select函数去做服务器端。
然后要清楚这个项目要分成多少个子模块,这里我把它暂时先分成最简单的几个模块即能满足基本要求。每个模块都是一个选项,我在结构体里写了一个标志位,给各个子模块标号,比如注册是1,登录是2,那么每次结构体传到服务器时,服务器判断这个标志位来检测你需要服务器去处理什么功能,再发送结果给客户端做相应处理。
子模块设计
(一) 注册:
登录之前如果没有账号则需要注册账号。这里需要建立数据库来存放注册的账号,第一个界面就是登录注册的界面,如果登录成功就进入聊天的界面,没有账号则进入注册界面。数据库的建立要在服务器上进行,所以服务器一开始就要建立一个数据库,然后客户端去访问数据库。

(二) 登录
登录同注册其实很像,也要到数据库里遍历一下看是否重复ID,再判断账号密码是否一致才可允许登录。登录有些不一样的就是需要判断是否已经在线,并且登录以后要获得其用户的信息,要解决这个问题就需要一个链表,这个链表存储着所有登录用户的信息,以便服务器回馈给客户端一些信息,每次需要一些其他用户的信息时直接从链表获取就可以了,当然登录的账号信息还要保存在数据库里。
(三) 查看在线用户
这里就要用到登录里说的那个链表了,因为链表时存储已登录的用户信息,从这里可以获得其他在线用户的信息。
(四) 私聊
私聊也是通过链表的关系找到你想要聊天的对象,可以通过昵称或者账号去寻找,需要注意的点就是如何结束对话。
(五) 群聊
群聊的意义就是你发与一句话,参加群聊的人都能看到,就是说要给在线的并且参加群聊的用户发送消息,这里可以给结构体一个群聊标志位,这样似乎容易遍历时分辨出来群聊用户。
(六) 查看聊天记录
这里要在客户端的目录下建立一个文本文件用来存储聊天记录,关键在于如何去存储属于谁和谁的聊天记录,需要好好构思一下。
(七) 附加功能
附加了踢人与禁言的功能,在传数据包的结构体中添加了VIP的信号位,当VIP位置1时,该用户拥有VIP权限,即踢人和禁言功能。踢人我设计的是直接踢下线,而禁言就是私聊群聊都不会收到消息,通过禁言标志位实现, 踢人直接下线就行了。

这是学完C做的一个比较全面的项目了,肯定还有很多不足的地方,我会在日后改进这个项目并传上博客,估计会换个模式,比如TCP/IP,还有我觉得把登录的包和聊天的包放在一起效率太低了,可以分成两个种类的包,一个登录包,一个聊天包,这样实施起来会有效率些。第一次做比较大的项目,很多地方都很稚嫩,希望各位看客多多包涵~ 代码如下:

头文件

#ifndef _CHATHEAD_
#define _CHATHEAD_

#define PORT 8888
#define success 10000
#define failure 10001

struct login
{
    char id[20];
    char password[20];
    char nickname[20];
    char personalsign[50];
    int  like;
    int  vip;
    int  ban;
    char onlineusr[10][20];
    char toname[20];
    char buf[100];
    int admin;
    int rdnum;
    int tofd;           //目标文件符
    int option;         //选项标志位
    int flag;           //状态标志位
    struct login *next;
};

typedef struct login Node;
typedef struct login *List;

int ListInit (List *L);

#endif

服务器API

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "ChatHead.h"
#include <sqlite3.h>

int ListInit (List *L)
{
    (*L) = (List)malloc(sizeof(Node));
    if(NULL == (*L))
    {
        return failure;
    }

    (*L)->next = NULL;

    return success;
}


服务器

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "ChatHead.h"
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/select.h>
#include <sqlite3.h>

#define PORT 8888
#define registerask  1001 
#define loginask     1002  
#define idinfo       1003
#define viewonline   1004
#define singlechat   1005
#define chatall      1006
#define filesend     1007
#define likeadd      1008
#define altersign    1009
#define bansay       1010
#define kickusr      1011
#define buyvip       1012
#define loginout     1013

int recordfd;

int main()
{
    int sockfd, ret, fd[100] ={0}, i = 0, j, length, maxfd;
    int m = 0, n = 1;                               //用来检索数据库里的id password
    struct sockaddr_in server_addr;
    struct sockaddr_in client_addr;
    fd_set readfds, tmpfds;
    struct login RecvBuf;
    sqlite3 *ppdb;
    int flag = 0;
    char sql[150] = {0};
    char **pResult;
    int recordcount = 0;
    int rowcount = 1;
    List list;
    List plist;
    List tmplist;
    List newlogin;
    List freelist;
    bzero(&newlogin, sizeof(newlogin));
    int u = 0;

    ret = ListInit(&list);
    if(failure == ret)
    {
        printf("ListInit Failure!\n");
    }
    else
    {
        printf("ListInit Success!\n");
    }

    ret = sqlite3_open("Regiseter.db", &ppdb);//数据库
    if(ret != SQLITE_OK)
    {
        perror("sqlite3_open");
        exit(1);
    }

    sprintf(sql, "create table if not exists login(id text, password text, nickname text, personalsign text, like integer, vip integer, ban integer);");
    ret = sqlite3_exec(ppdb, sql, NULL, NULL, NULL);
    if(ret != SQLITE_OK)
    {
        perror("sqlite3_exec");
        exit(1);
    }
    bzero(&sql, sizeof(sql));

    sockfd = socket(PF_INET, SOCK_STREAM, 0);           //通道
    if(sockfd < 0)
    {
        perror("socket");
        exit(1);
    }

    bzero(&server_addr, sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(PORT);
    server_addr.sin_addr.s_addr = inet_addr("192.168.1.116");

    int opt = 1;
    setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));

    ret = bind(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr));
    if(ret < 0)
    {
        perror("bind");
        exit(1);
    }

    ret = listen(sockfd, 50);
    if(ret < 0)
    {
        perror("listen");
        exit(1);
    }

    FD_ZERO(&readfds);
    FD_ZERO(&tmpfds);
    FD_SET(sockfd, &readfds);
    maxfd = sockfd;

    while(1)
    {
        bzero(&pResult, sizeof(pResult));
        bzero(&sql, sizeof(sql));
        m = 7;
        n = 8;
        u = 0;
        flag = 0;
        rowcount = 0;
        plist = list->next;     //链表指针每次一开始都指向链表的第一个元素
        tmplist = list;
        freelist = tmplist;
        tmpfds = readfds;

        ret = select(maxfd + 1, &tmpfds, NULL, NULL, NULL);
        if(-1 == ret)
        {
            perror("select");
            exit(1);
        }

        if(FD_ISSET(sockfd,&tmpfds))
        {
            length = sizeof(client_addr);
            fd[i] = accept(sockfd, (struct sockaddr *)&client_addr, &length);
            if(fd[i] < 0)
            {
                perror("accept");
                exit(1);
            }

            printf("Accept %d Port: %d\n", fd[i], client_addr.sin_port);

            FD_SET(fd[i], &readfds);
            maxfd = fd[i];
            i++;
        }
        else
        {
            for(j = 0; j < i; j++)
            {
                if(FD_ISSET(fd[j], &tmpfds))
                {   
                    bzero(&RecvBuf, sizeof(RecvBuf));

                    ret = recv(fd[j], &RecvBuf, sizeof(RecvBuf), 0);
                    if(ret < 0)
                    {
                        perror("recv");
                        exit(1);
                    }

                    switch (RecvBuf.option)
                    {
                        case 1001:
                                while(plist != NULL)                //如果登录链表里已存在此ID则重复登录
                                {
                                    if(!strcmp(plist->id, RecvBuf.id))
                                    {
                                        flag = 2;
                                        break;
                                    }
                                    plist = plist->next;
                                    tmplist = tmplist->next;
                                }

                                if(flag == 2)
                                {
                                    ret = send(fd[j], &flag, sizeof(flag), 0);
                                    if(ret < 0)
                                    {
                                        perror("sendrelogin");
                                        exit(1);
                                    }
                                    break;
                                }

                                sprintf(sql, "select * from login;");   
                                ret = sqlite3_get_table(ppdb, sql, &pResult, &recordcount, NULL, NULL);
                                if(ret != SQLITE_OK)
                                {
                                    perror("sqlite3_get_table1");
                                    exit(1);
                                }

                                if(recordcount == 0)
                                {
                                    flag = 0;
                                    ret = send(fd[j], &flag, sizeof(int), 0);
                                    if(ret < 0)
                                    {
                                        perror("sendflag3");                    
                                        exit(1);
                                    }
                                    break;
                                }

                                while(rowcount < recordcount)
                                {
                                    if((!strcmp(pResult[m], RecvBuf.id)) && (!strcmp(pResult[n], RecvBuf.password)))    //若数据库有此ID则登录成功
                                    {
                                        flag = 1;                                       //flag = 1表明可以登录
                                        strcpy(RecvBuf.nickname, pResult[m + 2]);
                                        RecvBuf.vip = *pResult[m + 5];
                                        RecvBuf.ban = *pResult[m + 6];
                                        break;
                                    }
                                    m += 7;
                                    n += 7;
                                    rowcount++;
                                }

                                ret = send(fd[j], &flag, sizeof(int), 0);
                                if(ret < 0)
                                {
                                    perror("sendflag1");
                                    exit(1);
                                }

                                if(flag == 1)
                                {
                                    newlogin = (List)malloc(sizeof(Node));
                                    strcpy(newlogin->id, RecvBuf.id);
                                    strcpy(newlogin->password, RecvBuf.password);
                                    strcpy(newlogin->nickname, RecvBuf.nickname);
                                    newlogin->ban = RecvBuf.ban;
                                    newlogin->vip = RecvBuf.vip;
                                    newlogin->tofd = fd[j];
                                    tmplist->next = newlogin;
                                    newlogin->next = plist;                 //插入链表

                            /*      recordfd = open("chatrecord.txt", O_WRONLY);        //记录用户登录时间
                                    if(-1 == recordfd)
                                    {
                                        perror("openrecordfd");
                                        exit(1);
                                    }*/

                                }

                                break;

                        case 1002 : 
                                sprintf(sql, "select * from login;");   
                                ret = sqlite3_get_table(ppdb, sql, &pResult, &recordcount, NULL, NULL);
                                if(ret != SQLITE_OK)
                                {
                                    perror("sqlite3_get_table2");
                                    exit(1);
                                }


                                if(recordcount == 0)                        //如果没有记录直接可以注册
                                {
                                    flag = 1;
                                    bzero(&sql, sizeof(sql));
                                    sprintf(sql, "insert into login(id, password, nickname, personalsign, like, vip, ban) values('%s', '%s', '%s', '%s', %d, %d, %d);", RecvBuf.id, RecvBuf.password, RecvBuf.nickname, RecvBuf.personalsign, RecvBuf.like, RecvBuf.vip, RecvBuf.ban);

                                    ret = sqlite3_exec(ppdb, sql, NULL, NULL, NULL);        //插入注册记录到数据库中
                                    if(ret != SQLITE_OK)
                                    {
                                        perror("sqlite4_insert");
                                        exit(1);
                                    }
                                    ret = send(fd[j], &flag, sizeof(int), 0);
                                    if(ret < 0)
                                    {
                                        perror("sendflag2");                    
                                        exit(1);
                                    }
                                    break;
                                }

                                while(rowcount < recordcount)
                                {
                                    if(!strcmp(pResult[m], RecvBuf.id)) //若数据库有此ID则不可以注册
                                    {
                                        flag = 2;                                       //flag = 2表明不可以注册
                                        break;
                                    }
                                    m += 7;
                                    n += 7;
                                    rowcount++;
                                }
                                if(flag == 2)
                                {
                                    ret = send(fd[j], &flag, sizeof(int), 0);
                                    if(ret < 0)
                                    {
                                        perror("sendflag1");
                                        exit(1);
                                    }
                                    break;
                                }

                                flag = 1;                                       //flag = 1表示可以注册
                                bzero(&sql, sizeof(sql));
                                sprintf(sql, "insert into login(id, password, nickname, personalsign, like, vip, ban) values('%s', '%s', '%s', '%s', %d, %d, %d);", RecvBuf.id, RecvBuf.password, RecvBuf.nickname, RecvBuf.personalsign, RecvBuf.like, RecvBuf.vip, RecvBuf.ban);
                                ret = sqlite3_exec(ppdb, sql, NULL, NULL, NULL);        //插入注册记录到数据库中
                                if(ret != SQLITE_OK)
                                {
                                    perror("sqlite3_insert");
                                    exit(1);
                                }

                                ret = send(fd[j], &flag, sizeof(int), 0);
                                if(ret < 0)
                                {
                                    perror("sendflag2");                    
                                    exit(1);
                                }

                                break;

                        case 1003:
                                sprintf(sql, "select * from login;");   
                                ret = sqlite3_get_table(ppdb, sql, &pResult, &recordcount, NULL, NULL);
                                if(ret != SQLITE_OK)
                                {
                                    perror("sqlite3_get_table1003");
                                    exit(1);
                                }

                                while(rowcount < recordcount)
                                {
                                    if(!strcmp(pResult[m], RecvBuf.id))
                                    {
                                        strcpy(RecvBuf.nickname, pResult[m + 2]);
                                        strcpy(RecvBuf.personalsign, pResult[m + 3]);
                                        RecvBuf.like = (*pResult[m + 4] - 48);              
                                    }
                                    m += 7;
                                    rowcount++;
                                }

                                RecvBuf.flag = 3;

                                ret = send(fd[j], &RecvBuf, sizeof(RecvBuf), 0);
                                if(ret < 0)
                                {
                                    perror("sendinfo1003");
                                    exit(1);
                                }

                                break;

                        case 1004:                          //在线用户
                                tmplist = plist;
                                while(tmplist)
                                {
                                    if(!strcmp(RecvBuf.id, tmplist->id))
                                    {
                                        strcpy(RecvBuf.nickname, tmplist->nickname);
                                        break;
                                    }
                                    tmplist = tmplist->next;
                                }

                                plist = list->next;

                                while(plist != NULL)
                                {
                                    strcpy(RecvBuf.onlineusr[u], plist->nickname);
                                    plist = plist->next;
                                    u++;
                                }

                                RecvBuf.flag = 0;

                                ret = send(fd[j], &RecvBuf, sizeof(RecvBuf), 0);
                                if(ret < 0)
                                {
                                    perror("sendonline");
                                    exit(1);
                                }

                                break;

                        case 1005:                  //私聊
                                tmplist = plist;
                                while(tmplist)
                                {
                                    if(!strcmp(RecvBuf.id, tmplist->id))
                                    {
                                        strcpy(RecvBuf.nickname, tmplist->nickname);
                                        break;
                                    }
                                    tmplist = tmplist->next;
                                }

                                plist = list->next;

                                while(plist != NULL)
                                {
                                    if(!strcmp(RecvBuf.toname, plist->nickname))
                                    {
                                        if(tmplist->ban == 48)
                                        {
                                            RecvBuf.flag = 1;
                                            ret = send(plist->tofd, &RecvBuf, sizeof(RecvBuf), 0);
                                            if(ret < 0)
                                            {
                                                perror("sendbuf1005");
                                                exit(1);
                                            }
                                        }
                                        else
                                        {
                                            RecvBuf.flag = 13;
                                            ret = send(fd[j], &RecvBuf, sizeof(RecvBuf), 0);
                                            if(ret < 0)
                                            {
                                                perror("send13");
                                                exit(1);
                                            }
                                        }

                                        break;
                                    }

                                    plist = plist->next;
                                }

                                if(RecvBuf.flag == 0)
                                {
                                    RecvBuf.flag = 11;

                                    ret = send(fd[j], &RecvBuf, sizeof(RecvBuf), 0);        //考虑对方不在线
                                    if(ret < 0)
                                    {
                                        perror("sendflag1005");
                                        exit(1);
                                    }
                                }

                                break;

                        case 1006:                          //群聊
                                tmplist = plist;
                                while(tmplist)
                                {
                                    if(!strcmp(RecvBuf.id, tmplist->id))
                                    {
                                        strcpy(RecvBuf.nickname, tmplist->nickname);
                                        break;
                                    }
                                    tmplist = tmplist->next;
                                }

                                plist = list->next;

                                if(tmplist->ban == 48)
                                {
                                    while(plist)
                                    {
                                        RecvBuf.flag = 1;
                                        ret = send(plist->tofd, &RecvBuf, sizeof(RecvBuf), 0);
                                        if(ret < 0)
                                        {
                                            perror("sendallserver");
                                            exit(1);
                                        }
                                        plist = plist->next;
                                    }
                                }
                                else
                                {
                                    RecvBuf.flag = 13;
                                    ret = send(fd[j], &RecvBuf, sizeof(RecvBuf), 0);
                                    if(ret < 0)
                                    {
                                        perror("send13");
                                        exit(1);
                                    }
                                }

                                break;

                        case 1007:                          //文件传输
                                while(plist)
                                {
                                    if(!strcmp(RecvBuf.toname, plist->nickname))
                                    {
                                        RecvBuf.flag = 2;
                                        ret = send(plist->tofd, &RecvBuf, sizeof(RecvBuf), 0);
                                        if(ret < 0)
                                        {
                                            perror("sendfile1");
                                            exit(1);
                                        }
                                    }
                                    plist = plist->next;
                                }
                                break;

                        case 1008:                          //点赞
                                bzero(&sql, sizeof(sql));
                                sprintf(sql, "select * from login;");   

                                ret = sqlite3_get_table(ppdb, sql, &pResult, &recordcount, NULL, NULL);
                                if(ret != SQLITE_OK)
                                {
                                    perror("sqlite3_get_table1");
                                    exit(1);
                                }

                                while(rowcount < recordcount)
                                {
                                    if(!strcmp(pResult[m + 2], RecvBuf.toname))
                                    {
                                        RecvBuf.flag = 4;
                                        break;
                                    }
                                    rowcount++;
                                    m += 7;
                                }

                                if(RecvBuf.flag == 4)
                                {
                                    RecvBuf.like = *pResult[m + 4]; 
                                    RecvBuf.like += 1;
                                    bzero(&sql, sizeof(sql));
                                    sprintf(sql, "update login set like = %d where nickname = '%s';", RecvBuf.like, RecvBuf.toname);
                                    ret = sqlite3_exec(ppdb, sql, NULL, NULL, NULL);
                                    if(ret != SQLITE_OK)
                                    {
                                        perror("updatelike");
                                        exit(1);
                                    }

                                    ret = send(fd[j], &RecvBuf, sizeof(RecvBuf), 0);
                                    if(ret < 0)
                                    {
                                        perror("sendlike1");
                                        exit(1);
                                    }
                                }
                                else
                                {
                                    RecvBuf.flag = 12;
                                    ret = send(fd[j], &RecvBuf, sizeof(RecvBuf), 0);
                                    if(ret < 0)
                                    {
                                        perror("sendlike12");
                                        exit(1);
                                    }
                                }

                                break;

                        case 1009:                          //修改个性签名
                                bzero(&sql, sizeof(sql));
                                sprintf(sql, "select * from login;");   

                                ret = sqlite3_get_table(ppdb, sql, &pResult, &recordcount, NULL, NULL);
                                if(ret != SQLITE_OK)
                                {
                                    perror("sqlite3_get_table1");
                                    exit(1);
                                }

                                while(rowcount < recordcount)
                                {
                                    if(!strcmp(pResult[m], RecvBuf.id))
                                    {
                                        RecvBuf.flag = 5;
                                        break;
                                    }
                                    rowcount++;
                                    m += 7;
                                }

                                if(RecvBuf.flag == 5)
                                {
                                    bzero(&sql, sizeof(sql));
                                    sprintf(sql, "update login set personalsign = '%s' where id = '%s';", RecvBuf.personalsign, RecvBuf.id);
                                    ret = sqlite3_exec(ppdb, sql, NULL, NULL, NULL);
                                    if(ret != SQLITE_OK)
                                    {
                                        perror("updatesign");
                                        exit(1);
                                    }

                                    ret = send(fd[j], &RecvBuf, sizeof(RecvBuf), 0);
                                    if(ret < 0)
                                    {
                                        perror("sendsign1");
                                        exit(1);
                                    }
                                }
                                else
                                {
                                    RecvBuf.flag = 12;
                                    ret = send(fd[j], &RecvBuf, sizeof(RecvBuf), 0);
                                    if(ret < 0)
                                    {
                                        perror("sendsign12");
                                        exit(1);
                                    }
                                }

                                break;

                        case 1010:                      //禁言
                                bzero(&sql, sizeof(sql));
                                sprintf(sql, "select * from login;");   

                                ret = sqlite3_get_table(ppdb, sql, &pResult, &recordcount, NULL, NULL);
                                if(ret != SQLITE_OK)
                                {
                                    perror("sqlite3_get_table1");
                                    exit(1);
                                }

                                while(rowcount < recordcount)
                                {
                                    if(!strcmp(pResult[m], RecvBuf.id))
                                    {
                                        if(49 == *pResult[m + 5])
                                        {
                                            RecvBuf.flag = 6;
                                            ret = send(fd[j], &RecvBuf, sizeof(RecvBuf), 0);
                                            if(ret < 0)
                                            {
                                                perror("send6");
                                                exit(1);
                                            }
                                            break;
                                        }
                                        else
                                        {
                                            RecvBuf.flag = 61;
                                            ret = send(fd[j], &RecvBuf, sizeof(RecvBuf), 0);
                                            if(ret < 0)
                                            {
                                                perror("send61");
                                                exit(1);
                                            }
                                            break;
                                        }
                                    }
                                    rowcount++;
                                    m += 7;
                                }

                                if(RecvBuf.flag == 6)
                                {
                                    bzero(&sql, sizeof(sql));
                                    sprintf(sql, "update login set ban = 1 where id = '%s';",  RecvBuf.toname);
                                    ret = sqlite3_exec(ppdb, sql, NULL, NULL, NULL);
                                    if(ret != SQLITE_OK)
                                    {
                                        perror("updateban");
                                        exit(1);
                                    }
                                    while(plist)
                                    {
                                        if(!strcmp(RecvBuf.toname, plist->nickname))
                                        {
                                            plist->ban = 49;
                                            break;
                                        }
                                        plist = plist->next;
                                    }
                                }

                                break;

                        case 1011:                      //踢人
                                bzero(&sql, sizeof(sql));
                                sprintf(sql, "select * from login;");   

                                ret = sqlite3_get_table(ppdb, sql, &pResult, &recordcount, NULL, NULL);
                                if(ret != SQLITE_OK)
                                {
                                    perror("sqlite3_get_table1");
                                    exit(1);
                                }

                                while(rowcount < recordcount)
                                {
                                    if(!strcmp(pResult[m], RecvBuf.id))
                                    {
                                        if(49 == *pResult[m + 5])
                                        {
                                            RecvBuf.flag = 7;
                                            ret = send(fd[j], &RecvBuf, sizeof(RecvBuf), 0);
                                            if(ret < 0)
                                            {
                                                perror("send6");
                                                exit(1);
                                            }

                                            break;
                                        }
                                        else
                                        {
                                            RecvBuf.flag = 71;
                                            ret = send(fd[j], &RecvBuf, sizeof(RecvBuf), 0);
                                            if(ret < 0)
                                            {
                                                perror("send71");
                                                exit(1);
                                            }
                                            break;
                                        }
                                    }
                                    rowcount++;
                                    m += 7;
                                }

                                tmplist = plist;
                                while(tmplist)
                                {
                                    if(!strcmp(RecvBuf.id, tmplist->id))
                                    {
                                        strcpy(RecvBuf.nickname, tmplist->nickname);
                                        break;
                                    }
                                    tmplist = tmplist->next;
                                }

                                plist = list->next;
                                tmplist = list;

                                if(RecvBuf.flag == 7)
                                {
                                    while(plist)
                                    {
                                        if(!strcmp(RecvBuf.toname, plist->nickname))
                                        {
                                            RecvBuf.flag = 73;
                                            ret = send(plist->tofd, &RecvBuf, sizeof(RecvBuf), 0);
                                            if(ret < 0)
                                            {
                                                perror("send73");
                                                exit(1);
                                            }

                                            freelist = plist;
                                            plist = plist->next;
                                            tmplist->next = plist;
                                            free(freelist);
                                            freelist = NULL;

                                            break;
                                        }
                                        plist = plist->next;
                                        tmplist = tmplist->next;
                                    }
                                }

                                break;              
                        case 1012:                      //购买VIP
                                bzero(&sql, sizeof(sql));
                                sprintf(sql, "select * from login;");   

                                ret = sqlite3_get_table(ppdb, sql, &pResult, &recordcount, NULL, NULL);
                                if(ret != SQLITE_OK)
                                {
                                    perror("sqlite3_get_table1");
                                    exit(1);
                                }

                                while(rowcount < recordcount)
                                {
                                    if(!strcmp(pResult[m], RecvBuf.id))
                                    {
                                        RecvBuf.flag = 8;
                                        ret = send(fd[j], &RecvBuf, sizeof(RecvBuf), 0);
                                        if(ret < 0)
                                        {
                                            perror("send8");
                                            exit(1);
                                        }
                                        break;
                                    }
                                    rowcount++;
                                    m += 7;
                                }

                                if(RecvBuf.flag == 8)
                                {
                                    bzero(&sql, sizeof(sql));
                                    sprintf(sql, "update login set vip = 1 where id = '%s';",  RecvBuf.id);
                                    ret = sqlite3_exec(ppdb, sql, NULL, NULL, NULL);
                                    if(ret != SQLITE_OK)
                                    {
                                        perror("updatevip");
                                        exit(1);
                                    }
                                }

                                break;              
                        case 1013:                  //退出
                                tmplist = plist;
                                while(tmplist)
                                {
                                    if(!strcmp(RecvBuf.id, tmplist->id))
                                    {
                                        strcpy(RecvBuf.nickname, tmplist->nickname);
                                        break;
                                    }
                                    tmplist = tmplist->next;
                                }

                                tmplist = list;
                                plist = list->next;

                                while(plist)
                                {
                                    if(!strcmp(RecvBuf.nickname, plist->nickname))
                                    {
                                        freelist = plist;
                                        plist = plist->next;
                                        tmplist->next = plist;
                                        free(freelist);
                                        freelist = NULL;

                                        break;
                                    }
                                    plist = plist->next;
                                    tmplist = tmplist->next;
                                }

                                break;

                        default :
                                break;
                    }
                }
            }   
        }
    }

    return 0;
}

客户端

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "ChatHead.h"
#include <sys/types.h>
#include <sys/socket.h>
#include <pthread.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <time.h>
#include <signal.h>

#define PORT 8888
#define registerask  1001 
#define loginask     1002  

pthread_t tid;
pthread_t tid1;
int fd[3] = {0};

struct login SendChat;

void *returnchat(void *arg)
{
    int ret, oldtype;
    char buf[100] = {0};
    char recordchat[4][100];

    signal(SIGINT, SIG_IGN);
    pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &oldtype);

    while(1)
    {   
        strcpy(SendChat.buf, buf);
        scanf("%s", SendChat.buf);

        ret = send(*(int *)arg, &SendChat, sizeof(SendChat), 0);
        if(ret < 0)
        {
            perror("sendchatreturn");
            exit(1);
        }
        bzero(&recordchat, sizeof(recordchat));

        time_t timep;
        time(&timep);

        strcpy(recordchat[0], "To:");           //用结构体完善    
        strcpy(recordchat[1], SendChat.toname);
        strcpy(recordchat[2], SendChat.buf);
        strcpy(recordchat[3], asctime(gmtime(&timep)));

        fd[2] = open("recordchat.txt", O_WRONLY);
        if(-1 == fd[2])
        {
            perror("chatopen");
            exit(1);
        }

        ret = write(fd[2], &recordchat, sizeof(recordchat));
        if(-1 == ret)
        {
            perror("writechat");
            exit(1);
        }

        close(fd[2]);

        if(!strcmp(SendChat.buf, "bye"))
        {
            break;
        }
    }

    pthread_exit(NULL);
}


void *Recv(void *arg)
{
    int ret, oldtype;
    struct login RecvChat;
    char recordchat[4][100];
    int g = 0;
    int choice;

    signal(SIGINT, SIG_IGN);
    pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &oldtype);

    while(1)
    {
        bzero(&RecvChat, sizeof(RecvChat));
        ret = recv(*(int *)arg, &RecvChat, sizeof(RecvChat), 0);
        if(ret < 0)
        {
            perror("recv");
            exit(1);
        }

        switch (RecvChat.flag)
        {
            case 0:
                    while(*RecvChat.onlineusr[g] != 0)
                    {
                        printf("%s\n", RecvChat.onlineusr[g]);
                        g++;
                    }

                    break;

            case 1:                                 //收到的消息
                    printf("\nFrom %s:\n", RecvChat.nickname);
                    printf("%s\n", RecvChat.buf);
                    printf("\n是否回复?\n1.是 2.否\n");
                    scanf("%d", &choice);
                    if(choice == 1)
                    {
                        bzero(&SendChat, sizeof(SendChat));
                        strcpy(SendChat.id, RecvChat.id);
                        strcpy(SendChat.toname, RecvChat.nickname);
                        SendChat.tofd = RecvChat.tofd;
                        SendChat.option = RecvChat.option;
                        printf("\t\t\t\t\t聊天结束输入bye!\n");
                        ret = pthread_create(&tid1, NULL, returnchat, &arg);
                        if(ret != 0)
                        {
                            perror("pthread_create1");
                            exit(1);
                        }

                    }
                    else
                    {
                        usleep(50);
                    }
                    printf("\t\t\t\t\t\t\t\t\t\t\t\n");
                    break;

            case 11:                                //不在线
                    printf("\n该用户不在线!\n");
                    break;

            case 12:
                    printf("\n该用户不存在!\n");
                    break;
            case 13:
                    printf("\n您已被禁言!\n");
                    break;

            case 2:                                 //文件接收
                    fd[1] = open("hellochat.txt", O_CREAT | O_EXCL | O_WRONLY, 755);
                    if(-1 == fd[1])
                    {
                        perror("opennew");
                        exit(1);
                    }

                    ret = write(fd[1], RecvChat.buf, RecvChat.rdnum);
                    if(-1 == ret)
                    {
                        perror("write");
                        exit(1);
                    }

                    break;
            case 3:
                    printf("\t\t\t\t\t\t\t\t\t\t\t*********************************\n");
                    printf("\t\t\t\t\t\t\t\t\t\t\t*赞:%d *%s\n", RecvChat.like, RecvChat.personalsign);
                    printf("\t\t\t\t\t\t\t\t\t\t\t*********************************\n");
                    printf("\t\t\t\t\t\t\t\t\t\t\t*%s \n", RecvChat.nickname);
                    printf("\t\t\t\t\t\t\t\t\t\t\t*                                \n");

                    break;
            case 4:
                    printf("\n\t点赞成功!\n");                  
                    break;  

            case 5:
                    printf("\n\t修改修改个性签名成功!\n");
                    break;
            case 6:
                    printf("\n\t禁言成功!\n");
                    break;
            case 61:
                    printf("\n\t禁言失败,您不是VIP,没有权限!\n");
                    break;
            case 7:
                    printf("\n\t踢人成功!\n");
                    break;
            case 71:
                    printf("\n\t踢人失败,您不是VIP,没有权限!\n");
                    break;
            case 73:
                    printf("\n\t你已被踢出聊天室!\n");
                    exit(1);
                    break;
            case 8:
                    printf("\n\tVIP购买成功!(若已是VIP则自动延期)\n");
                    break;
            default:
                    sleep(1);
                    break;
        }
    }

}


int main()
{
    int sockfd, ret, choice;
    struct sockaddr_in server_addr;
    struct login Login;
    int flag;
    char recordchat[4][100];

    signal(SIGINT, SIG_IGN);

    sockfd = socket(PF_INET, SOCK_STREAM, 0);
    if(sockfd == -1)
    {
        perror("socket");
        exit(1);
    }

    bzero(&server_addr, sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons (PORT);
    server_addr.sin_addr.s_addr = inet_addr("192.168.1.116");

    ret = connect(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr));
    if(ret < 0)
    {
        perror("connect");
        exit(1);
    }

    while(1)
    {   
        system("clear");
        printf("\t\t\t\t\t   _________________________________________________________   \n");
        printf("\t\t\t\t\t|*|*********************************************************|*|\n");
        printf("\t\t\t\t\t|*|                   Welcome to ChatHome                   |*|\n");
        printf("\t\t\t\t\t|*|                                                         |*|\n");
        printf("\t\t\t\t\t|*|                                                         |*|\n");
        printf("\t\t\t\t\t|*|                                                         |*|\n");
        printf("\t\t\t\t\t|*|                                                         |*|\n");
        printf("\t\t\t\t\t|*|                                                         |*|\n");
        printf("\t\t\t\t\t|*|                        1.登录                           |*|\n");
        printf("\t\t\t\t\t|*|                        2.注册                           |*|\n");
        printf("\t\t\t\t\t|*|                                                         |*|\n");
        printf("\t\t\t\t\t|*|*********************************************************|*|\n");
        flag = 0;

        printf("Please input choice:\n");
        scanf("%d", &choice);

        if(1 == choice)
        {
            bzero(&Login, sizeof(Login));
            printf("\t\t\t\t\t   *********************************************************   \n");
            printf("\t\t\t\t\t   请输入帐号密码:                                 \n");
            printf("\t\t\t\t\t     帐号:");
            scanf("%s", Login.id);
            printf("\n");
            printf("\t\t\t\t\t     密码:");
            scanf("%s", Login.password);

            Login.option = 1001;    //登录

            ret = send(sockfd, &Login, sizeof(Login), 0);           //发送登录请求
            if(ret < 0)
            {
                perror("sendlogin");
            }
            else
            {
                printf("登录中...\n");
                printf("loading...\n");
            }

            sleep(1);

            ret = recv(sockfd, &flag, sizeof(flag), 0);                     //接收登录许可
            if(ret < 0)
            {
                perror("recvflag1");
                exit(1);
            }
            if(1 == flag)
            {
                printf("登录成功!正在跳转...\n");
                system("clear");
                break;
            }
            else if(flag == 2)
            {
                printf("该帐户已登录!\n");
                sleep(1);
            }
            else
            {
                printf("登录失败!\n");
                sleep(1);
            }

        }
        else if(2 == choice)
        {
            bzero(&Login, sizeof(Login));
            printf("\t\t\t\t\t   *********************************************************\n");
            printf("\t\t\t\t\t   请输入帐号密码:                                 \n");
            printf("\t\t\t\t\t     帐号:");
            scanf("%s", Login.id);
            printf("\n");
            printf("\t\t\t\t\t     密码:");
            scanf("%s", Login.password);
            printf("\n");
            printf("\t\t\t\t\t     昵称:");
            scanf("%s", Login.nickname);

            Login.option = 1002;        //注册
            ret = send(sockfd, &Login, sizeof(Login), 0);
            if(ret < 0)
            {
                perror("sendregister");
            }
            else
            {
                printf("注册中...\n");
                printf("loading...\n");
            }

            sleep(1);
            ret = recv(sockfd, &flag, sizeof(flag), 0);             //接收注册许可
            if(ret < 0)
            {
                perror("recvflag2");
                exit(1);
            }
            if(1 == flag)
            {
                printf("注册成功!正在跳转...\n");
                sleep(2);
            }
            else if(flag == 2)
            {
                printf("该id已存在\n");
                printf("注册失败!\n");
                sleep(1);
            }
        }
        else
        {
            sleep(1);
        }

    }

    ret = pthread_create(&tid, NULL, Recv, (void *)&sockfd);
    if(ret != 0)
    {
        perror("pthread_create");
        exit(1);
    }


    while(1)
    {
        Login.option = 1003;    //获取数据库用户信息
        ret = send(sockfd, &Login, sizeof(Login), 0);
        if(ret < 0)
        {
            perror("send1003");
            exit(1);
        }
        usleep(900);

        printf("\t\t\t\t\t\t\t\t\t\t\t*********************************\n");
        printf("\t\t\t\t\t\t\t\t\t\t\t*1.查看在线好友                  \n");
        printf("\t\t\t\t\t\t\t\t\t\t\t*2.私聊                          \n");
        printf("\t\t\t\t\t\t\t\t\t\t\t*3.群聊                          \n");
        printf("\t\t\t\t\t\t\t\t\t\t\t*4.文件传输                      \n");
        printf("\t\t\t\t\t\t\t\t\t\t\t*5.点赞                          \n");
        printf("\t\t\t\t\t\t\t\t\t\t\t*6.修改个性签名                  \n");
        printf("\t\t\t\t\t\t\t\t\t\t\t*7.禁言(VIP)                     \n");
        printf("\t\t\t\t\t\t\t\t\t\t\t*8.踢人(VIP)                     \n");
        printf("\t\t\t\t\t\t\t\t\t\t\t*9.购买VIP                       \n");
        printf("\t\t\t\t\t\t\t\t\t\t\t*10.刷新                         \n");
        printf("\t\t\t\t\t\t\t\t\t\t\t*11.退出                         \n");
        printf("\t\t\t\t\t\t\t\t\t\t\t*                                \n");
        printf("\t\t\t\t\t\t\t\t\t\t\t*********************************\n");
        printf("\t\t\t\t\t\t\t\t\t\t\t请输入您的选项:  ");

        scanf("%d", &choice);

        switch (choice)
        {
            case 1:
                    Login.option = 1004;                //查看在线好友

                    ret = send(sockfd, &Login, sizeof(Login), 0);
                    if(ret < 0)
                    {
                        perror("send1004");
                        exit(1);
                    }

                    printf("\t\t\t\t在线用户:\n");
                    sleep(1);

                    break;
            case 2:                                     //私聊
                    Login.option = 1005;
                    bzero(&Login.buf, sizeof(Login.buf));
                    printf("\tWho do you want to talk with?\n");
                    scanf("%s", Login.toname);
                    printf("\t是否使用常用短语?\n\t1.是 2.否\n");
                    scanf("%d", &choice);
                    if(choice == 1)
                    {
                        printf("\n\tInput Info:\t\t\t\t常用短语:\n");
                        printf("\t           \t\t\t\t1.你好!\n");
                        printf("\t           \t\t\t\t2.早上好!\n");
                        printf("\t           \t\t\t\t3.中午好!\n");
                        printf("\t           \t\t\t\t4.晚上好!\n");
                        printf("\t           \t\t\t\t5.现在有事不方便联系!\n");
                        printf("\t           \t\t\t\t6.哦\n");
                        printf("\t           \t\t\t\t7.呵呵\n");
                        printf("\t           \t\t\t\t8.知道了!\n");
                        scanf("%s", Login.buf);
                        switch(Login.buf[0])
                        {
                            case 49:
                                    strcpy(Login.buf, "你好!");
                                    break;
                            case 50:
                                    strcpy(Login.buf, "早上好!");
                                    break;
                            case 51:
                                    strcpy(Login.buf, "中午好!");
                                    break;
                            case 52:
                                    strcpy(Login.buf, "晚上好!");
                                    break;
                            case 53:
                                    strcpy(Login.buf, "现在有事不方便联系!");
                                    break;
                            case 54:
                                    strcpy(Login.buf, "哦");
                                    break;
                            case 55:
                                    strcpy(Login.buf, "呵呵");
                                    break;
                            case 56:
                                    strcpy(Login.buf, "知道了!");
                                    break;
                            default:
                                    break;
                        }
                    }
                    else
                    {
                        printf("Input\n");
                        scanf("%s", Login.buf);
                    }

                    ret = send(sockfd, &Login, sizeof(Login), 0);
                    if(ret < 0)
                    {
                        perror("sendsigle");
                        exit(1);
                    }

                    bzero(&recordchat, sizeof(recordchat));

                    time_t timep;
                    time(&timep);

                    strcpy(recordchat[0], "To:");           //用结构体完善    
                    strcpy(recordchat[1], Login.toname);
                    strcpy(recordchat[2], Login.buf);
                    strcpy(recordchat[3], asctime(gmtime(&timep)));

                    fd[2] = open("recordchat.txt", O_WRONLY);
                    if(-1 == fd[2])
                    {
                        perror("chatopen");
                        exit(1);
                    }

                    ret = write(fd[2], &recordchat, sizeof(recordchat));
                    if(-1 == ret)
                    {
                        perror("writechat");
                        exit(1);
                    }

                    close(fd[2]);
                    sleep(1);

                    printf("\t是否继续聊天?\n1.是 2.否\n");
                    scanf("%d", &choice);
                    if(choice == 1)
                    {
                        printf("\t\t\t\t\t聊天结束输入bye!\n");
                        while(1)
                        {
                            scanf("%s", Login.buf);
                            ret = send(sockfd, &Login, sizeof(Login), 0);
                            if(ret < 0)
                            {   
                                perror("sendchat");
                                exit(1);
                            }
                            bzero(&recordchat, sizeof(recordchat));

                            time_t timep;
                            time(&timep);

                            strcpy(recordchat[0], "To:");           //用结构体完善    
                            strcpy(recordchat[1], Login.toname);
                            strcpy(recordchat[2], Login.buf);
                            strcpy(recordchat[3], asctime(gmtime(&timep)));

                            fd[2] = open("recordchat.txt", O_WRONLY);
                            if(-1 == fd[2])
                            {
                                perror("chatopen");
                                exit(1);
                            }

                            ret = write(fd[2], &recordchat, sizeof(recordchat));
                            if(-1 == ret)
                            {
                                perror("writechat");
                                exit(1);
                            }

                            close(fd[2]);

                            if(!strcmp(Login.buf, "bye"))
                            {
                                break;
                            }
                        }
                        pthread_join(tid1, NULL);
                    }
                    sleep(1);

                    break;
            case 3:                                     //群聊
                    Login.option = 1006;
                    bzero(&Login.buf, sizeof(Login.buf));
                    printf("Input:\n");
                    scanf("%s", Login.buf); 

                    ret = send(sockfd, &Login, sizeof(Login), 0);
                    if(ret < 0)
                    {
                        perror("sendall");
                        exit(1);
                    }

                    strcpy(recordchat[0], "To:");
                    strcpy(recordchat[1], "All");
                    strcpy(recordchat[2], Login.buf);
                    strcpy(recordchat[3], asctime(gmtime(&timep)));

                    fd[2] = open("recordchat.txt", O_WRONLY);
                    if(-1 == fd[2])
                    {
                        perror("chatopen");
                        exit(1);
                    }

                    ret = write(fd[2], &recordchat, sizeof(recordchat));
                    if(-1 == ret)
                    {
                        perror("writechat");
                        exit(1);
                    }

                    close(fd[2]);
                    sleep(1);

                    break;
            case 4:
                    Login.option = 1007;
                    bzero(&Login.buf, sizeof(Login.buf));
                    printf("本地文件:\n");
                    system("ls");
                //  printf("请输入发送文件:\n");   //指定发送文件hello.txt

                    printf("请输入接收对象:\n");
                    scanf("%s", Login.toname);

                    fd[0] = open("hello.txt", O_RDONLY);
                    if(-1 == fd[0])
                    {
                        perror("open1");
                        exit(1);
                    }

                    while((Login.rdnum = read(fd[0], Login.buf, sizeof(Login.buf))) != 0)
                    {
                        ret = send(sockfd, &Login, sizeof(Login), 0);
                        if(ret < 0)
                        {
                            perror("sendfile");
                            exit(1);
                        }

                        bzero(&Login.buf, sizeof(Login.buf));
                    }

                    printf("传输中...\n");
                    sleep(1);

                    break;

            case 5:                                     //点赞
                    Login.option = 1008;

                    printf("你想给谁点赞?\n");
                    scanf("%s", Login.toname);

                    ret = send(sockfd, &Login, sizeof(Login), 0);
                    if(ret < 0)
                    {
                        perror("sendlike");
                        exit(1);
                    }
                    sleep(1);

                    break;

            case 6:                                     //修改个性签名
                    Login.option = 1009;

                    printf("输入新的签名:\n");
                    scanf("%s", Login.personalsign);

                    ret = send(sockfd, &Login, sizeof(Login), 0);
                    if(ret < 0)
                    {
                        perror("sendsign");
                        exit(1);
                    }
                    usleep(900);

                    break;

            case 7:                                     //禁言
                    Login.option = 1010;

                    printf("你想禁言谁?\n");
                    scanf("%s", Login.toname);

                    ret = send(sockfd, &Login, sizeof(Login), 0);
                    if(ret < 0)
                    {
                        perror("sendban");
                        exit(1);
                    }

                    usleep(900);
                    break;
            case 8:
                    Login.option = 1011;                //踢人
                    printf("你想踢走谁?\n");
                    scanf("%s", Login.toname);

                    ret = send(sockfd, &Login, sizeof(Login), 0);
                    if(ret < 0)
                    {
                        perror("sendkick");
                        exit(1);
                    }
                    sleep(1);

                    break;
            case 9:
                    Login.option = 1012;
                    printf("是否购买VIP?\n1.是 2.否\n");
                    scanf("%d", &choice);

                    printf("购买中...\n");
                    sleep(1);
                    if(choice == 1)
                    {
                        ret = send(sockfd, &Login, sizeof(Login), 0);
                        if(ret < 0)
                        {
                            perror("send1012");
                            exit(1);
                        }
                        usleep(900);
                        break;
                    }
                    else
                    {
                        usleep(900);
                        break;
                    }
            case 10:                            //刷新
                    printf("\t刷新成功!\n");
                    system("clear");
                    sleep(1);

                    break;
            case 11:                                        //退出
                    Login.option = 1013;

                    ret = send(sockfd, &Login, sizeof(Login), 0);
                    if(ret < 0)
                    {
                        perror("sendloginout");
                        exit(1);
                    }

                    pthread_cancel(tid);

                    flag = 9;

                    break;
            default:
                    break;
        }

        if(9 == flag)
        {
            break;
        }
    }

    close(sockfd);

    return 0;
}
  • 2
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值