聊天室服务器(select实现监听)

关于聊天室的服务器

服务器用来接收客户端的请求与消息,并发送消息,服务器端不需要进行操作。

服务器通过文件描述符来检测不同的客户端发送的消息
大部分的服务器采用的是多线程的方式进行接收与发送,我采用了select来监听所有文件描述符

在开启客户端时需要输入本机IPv4地址

头文件

#ifndef _SERVER_H_
#define _SERVER_H_

#define color_none      "\033[0m"
#define color_red       "\033[1;5;31m"
#define color_purple    "\033[1;5;35m"
#define color_green     "\033[1;5;32m"
#define color_yellow    "\033[1;5;33m"

#define PORT    8888

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

pthread_t tid;

struct info
{
    int option;
    int sign;
    int num;
    int chat;
    char username[20];
    char passward[20];
    char name[20];
    char question[20];
    char key[20];
    char buf[10000];
    char n[10][10];
    int online_num;
    char recvname[20];
    char filename[20];
    char member[10];  //判断是否是会员
    int speak;  //是否被禁言
    int offline;
    char agree[10];
};

struct link
{
    char id[20];
    char name[20];
    int fd;
    int port;
    char member[10];  //判断是否是会员
    int speak;  //是否被禁言
    int offline;
    struct link *next;
};

typedef struct link Link;
typedef Link *link_list;

void signalhandler(void);
int list_init(link_list *list);
int list_compare(link_list list, struct info buf);
int list_insert(link_list *list, struct info buf, int fd, int port);
int list_delete(link_list *list, int fd);
int find_name(link_list list, int fd, struct info *sendbuf);
int find_recvname(link_list list, int fd, struct info *buf);
int find_fd(link_list list, struct info recvbuf);
int on_line(link_list list, struct info *sendbuf, int fd);
int sendall(link_list list, struct info buf, int fd);
int member_set(link_list *list, struct info buf, int fd);
int find_speak(link_list list, struct info recvbuf);
int speak_set(link_list *list, struct info buf);
int speak_set1(link_list *list, int fd);
int offline_set(link_list *list, int fd);

#endif

主程序

#include "chatroom.h"

int compare(void* para, int columnCount, char **columnValue, char **columnName)
{
    int i;
    for(i = 0; i < columnCount; i++)
    {
        if(strcmp(*columnValue, para) == 0)
        {
            return 1;
        }
    }
    return 0;
}

int print(void* para, int columnCount, char **columnValue, char **columnName)
{
    strcpy(para, *columnValue);
    return 0;
}


int main(int argc, char *argv[])
{
    sqlite3 *ppdb;
    struct info recvbuf;
    struct info sendbuf;
    int sockfd, ret, cfd, fd[100] = {0}, length, i = 0, j, m = 0;
    pthread_t tid;
    fd_set readfds;
    fd_set tmpfds;
    struct sockaddr_in server_addr;
    struct sockaddr_in client_addr;
    char sql[100] = {0};
    int tmpfd;
    char name[10] = {0};


    link_list list;
    ret = list_init(&list);
    if(ret == 0)
    {
        printf(color_red"\t\t\t\t\t1初始化失败!!!\n"color_none);
    }

    //signalhandler();    //按下ctrl+c或ctrl+\时不会退出

    ret = sqlite3_open("ID_PASSWARD.db", &ppdb);
    if(ret != SQLITE_OK)
    {
        perror("sqlite3_open");
        exit(1);
    }

    sprintf(sql,"create table if not exists ID_PASSWARD(username text, passward text, name text, question text, key text, member text);");
    ret = sqlite3_exec(ppdb, sql, NULL, NULL, NULL);
    if(ret != SQLITE_OK)
    {
        perror("create");
        exit(1);
    }
    system("clear");
    printf("开启服务器!\n");
    sockfd = socket(PF_INET, SOCK_STREAM, 0);
    if(-1 == sockfd)
    {
        perror("socket");
        exit(1);
    }
    int opt = 1; 
    setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));

    memset(&server_addr, 0, sizeof(server_addr));
    server_addr.sin_family = PF_INET;
    server_addr.sin_port = htons(PORT);
    server_addr.sin_addr.s_addr = inet_addr(argv[1]);
    ret = bind(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr));   //上述信息绑定到socket
    if (-1 == ret)                                                                                                                
    {
        perror("bind");
        exit(1);
    }

    printf("等待连接...\n");
    ret = listen(sockfd, 10);             //监听  最多允许10个连接
    if(-1 == ret)
    {
        perror("listen");
        exit(1);
    }

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

    int maxfd = sockfd;
    while(1)
    {
        tmpfds = readfds;
        sleep(1);
        ret = select(maxfd + 1, &tmpfds, NULL, NULL, NULL);    
        if(-1 == ret)
        {
            perror("select");
            exit(1);
        }
        if(FD_ISSET(sockfd, &tmpfds))
        {
            length = sizeof(client_addr);
            cfd = accept(sockfd, (struct sockaddr*)&client_addr, &length);

            if(-1 == ret)
            {
                perror("accept");
                exit(1);
            }

            printf("\n\n客户端已连接!端口号 :%d \n", client_addr.sin_port);

            for(i = 0; i < 100; i++)
            {
                if(fd[i] <= 0)
                {
                    fd[i] = cfd;
                    break;
                }
            }
            FD_SET(cfd, &readfds);
            if(cfd > maxfd)
            {
                maxfd = cfd;
            }
        }

        else
        {
            for(j = 0; j <= maxfd; j++)
            {
                if(FD_ISSET(fd[j], &tmpfds))
                {

                    memset(&recvbuf, 0, sizeof(recvbuf));
                    memset(&sendbuf, 0, sizeof(sendbuf));


                    ret = recv(fd[j], &recvbuf, sizeof(recvbuf), 0);
                    if(-1 == ret)
                    {
                        perror("recv");
                        exit(1);
                    }
                    switch(recvbuf.num)
                    {
                        case (1):      //登录
                        {
                            sprintf(sql, "select username from ID_PASSWARD;");
                            ret = sqlite3_exec(ppdb, sql, compare, recvbuf.username, NULL);
                            if(ret == SQLITE_OK)                             //判断账号是否错误
                            {
                                sendbuf.sign = 1;
                                send(fd[j], &sendbuf, sizeof(sendbuf), 0);
                            }
                            else                    //如果账号正确,判断密码是否正确
                            {

                                sprintf(sql, "select passward from ID_PASSWARD where username = '%s';", recvbuf.username);
                                ret = sqlite3_exec(ppdb, sql, compare, recvbuf.passward, NULL);
                                if(ret == SQLITE_OK)
                                {
                                    sendbuf.sign = 2;
                                    send(fd[j], &sendbuf, sizeof(sendbuf), 0);

                                }
                                else                //判断该账号是否已登录
                                {
                                    ret = list_compare(list, recvbuf);

                                    if(0 == ret)          //如果未登录,返回信号进入聊天界面
                                    {
                                        sprintf(sql, "select name from ID_PASSWARD where username = '%s';", recvbuf.username); 
                                        ret = sqlite3_exec(ppdb, sql, print, &recvbuf.name, NULL);
                                        sprintf(sql, "select member from ID_PASSWARD where username = '%s';", recvbuf.username); 
                                        ret = sqlite3_exec(ppdb, sql, print, &recvbuf.member, NULL);

                                        printf("\t用户名 %s 登陆成功\n", recvbuf.username);
                                        ret = list_insert(&list, recvbuf, fd[j], client_addr.sin_port); //将登陆信息插入链表

                                        if(1 == ret)
                                        {
                                            sendbuf.sign = 3;
                                            send(fd[j], &sendbuf, sizeof(sendbuf), 0);

                                        }

                                    }

                                    else if(-1 == ret)        //如果已登录,返回已登录信号
                                    {
                                        sendbuf.sign = 4;
                                        send(fd[j], &sendbuf, sizeof(sendbuf), 0);

                                    }
                                }
                            }
                            break;
                        }
                        case (2):  ///注册
                        {
                            sprintf(sql, "select username from ID_PASSWARD;");
                            ret = sqlite3_exec(ppdb, sql, compare, recvbuf.username, NULL);         //判断账号是否已存在
                            if(ret != SQLITE_OK)
                            {
                                sendbuf.sign = 2;
                                send(fd[j], &sendbuf, sizeof(sendbuf), 0);
                                break;
                            }

                            sprintf(sql, "insert into ID_PASSWARD(username, passward, name, question, key, member) values ('%s', '%s', '%s', '%s', '%s', '%s');", recvbuf.username, recvbuf.passward, recvbuf.name, recvbuf.question, recvbuf.key, recvbuf.member);
                            ret = sqlite3_exec(ppdb, sql, NULL, NULL, NULL);          //账号信息录入表
                            if(ret != SQLITE_OK)
                            {
                                perror("insert");
                                exit(1);
                            }

                            sendbuf.sign = 1;                                     
                            send(fd[j], &sendbuf, sizeof(sendbuf), 0);                          //返回注册成功信号
                            break;
                        }

                        case (3):  //找回密码
                        {
                            switch(recvbuf.sign)
                            {
                                case (1):           //接收账号
                                {                           
                                    sprintf(sql, "select username from ID_PASSWARD;");
                                    ret = sqlite3_exec(ppdb, sql, compare, recvbuf.username, NULL);
                                    if(ret != SQLITE_OK)
                                    {
                                        sendbuf.sign = 2;
                                        sprintf(sql, "select question from ID_PASSWARD where username = '%s';", recvbuf.username); 
                                        ret = sqlite3_exec(ppdb, sql, print, sendbuf.question, NULL);
                                        if(ret == SQLITE_OK)
                                        {
                                            send(fd[j], &sendbuf, sizeof(sendbuf), 0);   //账号匹配成功,返回成功信号与密保问题

                                        }
                                    }
                                    else
                                    {
                                        sendbuf.sign = 1;                      
                                        send(fd[j], &sendbuf, sizeof(sendbuf), 0);         //账号匹配失败,返回失败信号

                                    }
                                    break;
                                }

                                case (2):    //接收密保答案
                                {
                                    sprintf(sql, "select key from ID_PASSWARD where username = '%s';", recvbuf.username);
                                    ret = sqlite3_exec(ppdb, sql, compare, recvbuf.key, NULL);
                                    if(ret != SQLITE_OK)
                                    {
                                        sendbuf.sign = 2;          //答案匹配成功,返回成功信号         
                                        send(fd[j], &sendbuf, sizeof(sendbuf), 0);

                                    }
                                    else
                                    {
                                        sendbuf.sign = 1;          //答案匹配失败,返回失败信号
                                        send(fd[j], &sendbuf, sizeof(sendbuf), 0);

                                    }
                                    break;
                                }

                                case(3):     //接收新密码
                                {
                                    sprintf(sql, "update ID_PASSWARD set passward = '%s' where username = '%s';", recvbuf.passward, recvbuf.username);
                                    ret = sqlite3_exec(ppdb, sql, NULL, NULL, NULL);
                                    sendbuf.sign = 1;
                                    send(fd[j], &sendbuf, sizeof(sendbuf), 0);      //返回修改成功
                                    break;
                                }

                            }
                            break;
                        }
                        case (4):        //断开连接
                        {
                            FD_CLR(fd[j], &readfds);
                            printf("\t\t\t客户端已断开连接\n", fd[j]);
                            close(fd[j]);
                            break;
                        }
                        case (5):
                        {
                            switch(recvbuf.option)
                            {
                                case(1):
                                {
                                    find_name(list, fd[j], &sendbuf);
                                    tmpfd = find_fd(list, recvbuf);

                                    if(recvbuf.chat == -1)
                                    {
                                        ret = on_line(list, &sendbuf, fd[j]);
                                        ret = send(fd[j], &sendbuf, sizeof(sendbuf), 0);
                                        printf("   \n");
                                        break;
                                    }
                                    else if(recvbuf.chat == 10)   //发起聊天
                                    {   
                                        if(0 == tmpfd)
                                        {
                                            sendbuf.sign = 1;
                                            send(fd[j], &sendbuf, sizeof(sendbuf), 0);
                                            break;
                                        }
                                        else
                                        {
                                            sendbuf.sign = 2;
                                            send(fd[j], &sendbuf, sizeof(sendbuf), 0);
                                            sendbuf.sign = 1;   //发送给聊天对象       
                                            send(tmpfd, &sendbuf, sizeof(sendbuf), 0);
                                            break;
                                        }

                                    }

                                    else if(recvbuf.chat == 1)
                                    {
                                        recvbuf.sign = 2;
                                        ret = on_line(list, &recvbuf, tmpfd);
                                        find_recvname(list, fd[j], &recvbuf);

                                        send(tmpfd, &recvbuf, sizeof(recvbuf), 0);
                                        break;
                                    }

                                }
                                case (2):
                                {
                                    link_list p = list->next;
                                    while(p != NULL)
                                    {
                                        if(p->fd != fd[j])
                                        {
                                            ret = on_line(list, &recvbuf, p->fd);
                                            find_recvname(list, fd[j], &recvbuf);
                                            recvbuf.sign = 3;
                                            send(p->fd, &recvbuf, sizeof(recvbuf), 0);
                                        }
                                        p = p->next;
                                    }
                                    break;
                                }
                                case (3):
                                {
                                    find_recvname(list, fd[j], &recvbuf);
                                    tmpfd = find_fd(list, recvbuf);
                                    if(recvbuf.sign == 1)
                                    {
                                        recvbuf.sign = 4;
                                        send(tmpfd, &recvbuf, sizeof(recvbuf), 0);
                                        break;
                                    }
                                    else if(recvbuf.sign == 2)
                                    {
                                        recvbuf.sign = 5;
                                        send(tmpfd, &recvbuf, sizeof(recvbuf), 0);
                                        break;
                                    }
                                    else if(recvbuf.sign == 3)
                                    {
                                        recvbuf.sign = 6;
                                        send(tmpfd, &recvbuf, sizeof(recvbuf), 0);
                                        break;
                                    }

                                }
                                case (4):
                                {
                                    find_name(list, fd[j], &recvbuf);
                                    sprintf(sql, "update ID_PASSWARD set member = '%s' where username = '%s';", recvbuf.member, recvbuf.username);
                                    ret = sqlite3_exec(ppdb, sql, NULL, NULL, NULL);
                                    ret = member_set(&list, recvbuf, fd[j]);

                                    break;
                                }
                                case (5):
                                {
                                    ret = find_speak(list, recvbuf);
                                    if(0 == ret)
                                    {
                                        sendbuf.sign = 1;
                                        send(fd[j], &sendbuf, sizeof(sendbuf), 0);
                                        break;
                                    }
                                    else if(-1 == ret)
                                    {
                                        sendbuf.sign = 2;

                                        send(fd[j], &sendbuf, sizeof(sendbuf), 0);
                                        break;
                                    }
                                    else
                                    {
                                        sendbuf.sign = -1;
                                        speak_set(&list, recvbuf);
                                        find_name(list, fd[j], &sendbuf);
                                        send(ret, &sendbuf, sizeof(sendbuf), 0);
                                        sendbuf.sign = 3;
                                        send(fd[j], &sendbuf, sizeof(sendbuf), 0);
                                        break;

                                    }

                                }
                                case (6):
                                {
                                    speak_set1(&list, fd[j]);
                                    break;
                                }
                                case (7):
                                {
                                    ret = find_fd(list, recvbuf);
                                    if(0 == ret)
                                    {
                                        sendbuf.sign = 1;
                                        send(fd[j], &sendbuf, sizeof(sendbuf), 0);
                                        break;
                                    }
                                    else
                                    {
                                        sendbuf.sign = -2;
                                        offline_set(&list, ret);
                                        find_name(list, fd[j], &sendbuf);
                                        send(ret, &sendbuf, sizeof(sendbuf), 0);
                                        sendbuf.sign = 2;
                                        send(fd[j], &sendbuf, sizeof(sendbuf), 0);
                                        break;
                                    }
                                }
                                case (-1):
                                {
                                    find_name(list, fd[j], &sendbuf);
                                    ret = list_delete(&list, fd[j]);
                                    printf("\t\t用户名 %s 已下线\n", sendbuf.username);
                                    break;
                                }
                            }
                            break;
                        }
                    }
                }
            }
        }
    }

    close(sockfd);
    return 0;
}

接口

#include "chatroom.h"

void signalhandler(void)
{
    sigset_t sigset;
    sigemptyset(&sigset);
    sigaddset(&sigset,SIGINT);
    sigaddset(&sigset,SIGQUIT);
    sigprocmask(SIG_BLOCK, &sigset, NULL);
}

int list_init(link_list *list)
{
    (*list) = (link_list)malloc(sizeof(Link));
    if(NULL == (*list))
    {
        return 0;
    }
    (*list)->next = NULL;
    return 1;
}

int list_compare(link_list list, struct info buf)
{
    link_list p = list->next;
    if(p == NULL)
    {
        return 0;
    }

    while (p != NULL)
    {
        if(strcmp(p->id, buf.username) == 0)
        {
            return -1;
        }
        p = p->next;
    }
    return 0;
}

int list_insert(link_list *list, struct info buf, int fd, int port)
{
    link_list p = *list;
    link_list n = (link_list)malloc(sizeof(Link));

    strcpy(n->id, buf.username);
    strcpy(n->name, buf.name);
    strcpy(n->member, buf.member);
    n->port = port;
    n->fd = fd;

    while(p->next != NULL)
    {
        p = p->next;
    }

    p->next = n;
    n->next = NULL;

    return 1;

}


int list_delete(link_list *list, int fd)
{
    link_list p = *list;
    link_list tmp;
    if(p->next == NULL)
    {
        return 0;
    }
    while(p->next != NULL)
    {
        if(p->next->fd == fd)
        {
            tmp = p->next;
            p->next = tmp->next;
            tmp->next = NULL;
            free(tmp);
            tmp = NULL;
            return 1;
        }
        p = p->next;
    }
    return 0;
}

int find_name(link_list list, int fd, struct info *sendbuf)
{
    link_list p = list->next;
    while(p != NULL)
    {
        if(p->fd == fd)
        {
            strcpy((*sendbuf).name, p->name);
            strcpy((*sendbuf).username, p->id);

            return 1;
        }
        p = p->next;
    }
    return 0;
}

int find_recvname(link_list list, int fd, struct info *buf)
{
    link_list p = list->next;
    while(p != NULL)
    {
        if(p->fd == fd)
        {
            strcpy((*buf).recvname, p->name);

            return 1;
        }
        p = p->next;
    }
    return 0;
}

int find_fd(link_list list, struct info recvbuf)
{
    int fd;
    link_list p = list->next;
    while(p != NULL)
    {
        if(strcmp(p->name, recvbuf.name) == 0)
        {
            fd = p->fd; 

            return fd;
        }
        p = p->next;
    }
    return 0;
}

int on_line(link_list list, struct info *sendbuf, int fd)
{
    link_list p = list->next;
    int i = 0;
    while(p != NULL)
    {
        if(fd == p->fd)
        {
            strcpy((*sendbuf).username, p->id);
            strcpy((*sendbuf).member, p->member);
            (*sendbuf).speak = p->speak;
            (*sendbuf).offline = p->offline;
        }
        strcpy((*sendbuf).n[i], p->name);
        (*sendbuf).online_num++;
        p = p->next;
        i++;
    }
    return 1;
}

int sendall(link_list list, struct info buf, int fd)
{
    link_list p = list->next;
    while(p != NULL)
    {
        if(p->fd != fd)
        {
            buf.sign = 3;
            send(p->fd, &buf, sizeof(buf), 0);
        }
        p = p->next;
    }
    return 0;
}

int member_set(link_list *list, struct info buf, int fd)
{
    link_list p = (*list)->next;
    while(p != NULL)
    {
        if(fd == p->fd)
        {
            strcpy(p->member, buf.member);
            return 0;
        }
        p = p->next;
    }
    return 1;
}

int find_speak(link_list list, struct info recvbuf)
{
    int fd;
    link_list p = list->next;
    while(p != NULL)
    {
        if(strcmp(p->name, recvbuf.name) == 0)
        {
            if(p->speak == 1)
            {
                return -1;
            }
            fd = p->fd; 
            return fd;
        }
        p = p->next;
    }
    return 0;
}

int speak_set(link_list *list, struct info buf)
{
    link_list p = (*list)->next;
    while(p != NULL)
    {
        if(strcmp(p->name, buf.name) == 0)
        {
            p->speak = 1;
            return 0;
        }
        p = p->next;
    }
    return 1;
}

int speak_set1(link_list *list, int fd)
{
    link_list p = (*list)->next;
    while(p != NULL)
    {
        if(p->fd == fd)
        {
            p->speak = -1;
            return 0;
        }
        p = p->next;
    }    
    return 1;
}

int offline_set(link_list *list, int fd)
{
    link_list p = (*list)->next;
    while(p != NULL)
    {
        if(p->fd == fd)
        {
            p->offline = 1;
            return 0;
        }
        p = p->next;
    }
    return 1;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值