使用tcp+poll+多线程实现聊天室项目

聊天室主要实现以下功能,分为一级界面和二级界面,下面附有源代码

 1.首先是服务器代码

server.h代码


#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include<arpa/inet.h>
#include <stdlib.h>
#include<string.h>
#include <unistd.h>
#include<signal.h>
#include <netinet/in.h>
#include<fcntl.h>
#include<sys/wait.h>
#include<signal.h>
#include<pthread.h>
#include<sys/time.h>
#include<sys/select.h>
#include<poll.h>
#include<sqlite3.h>
#define PORT 11111
#define MaxNum 1024 //最多允许1000客户端同时登录
#define SUCCESS 1
#define FALIURE -1
#define Client_Size 1024  //同时连接客户端的最大人数同时连接客户端的最大人数
struct message
{
    char name[20];      //姓名
    int id;             //id
    char password[20];  //密码
    int age;            //年龄
    int cmd;            //协议
    char code[20];      //密保
    char sex[10];          //性别
    char message[200];     //要传的信息
    char toname [20];     //要传的人的姓名
    char filename[20];    //文件名字
    char file_message[1024];    //文件内容
    char online_name[1024];  //用户在线姓名
};
enum
{
    REGISTER,//注册
    LOGIN,  //登录
    LOGIN_REPEAT, //重复登录
    PRIVATE, //私聊
    GROUP,  //群聊
    REPLAY,  //回复
    REPEAT,  //重复  
    NONENTITY, //用户不存在
    SEND,      //发送数据
    FAIL,       //发送数据失败
    SILENT,    //禁言
    SILENT_FAILURE, //禁言失败
    BAN,            //你已被禁言标志
    REMOVE_BAN,     //禁言解除
    ADMINISTER,  //管理员
    ADMINISTER_FAILURE,  //管理员申请失败返回标志
    VIP_REPEAT, //vip重复申请标识符
    NORMAL_USER,  //普通用户标识符
    REMOVE,      //解除禁言
    REMOVE_FAILURE, //解除禁言失败
    FORCE,         //强制下线
    FORCE_SUCCESS, //返回给客户端表示踢人成功
    EXIT,         //退出
    FILE_SEND,    //发送文件
    VIEW,         //聊天记录
    ONLINE,    // 查看在线用户
};

struct online
{
    char name[20];
    int cfd;
    struct online*next;
};

/**
 * @brief 初始化服务端套接字
 * 
 * @param socket 
 * @return ** int 
 * 成功返回 SUCCESS;
 * 失败返回 FAILURE;
 */
int Socket_Init(int* sockfd);


//void fds_init(struct pollfd*fds);//将文件描述符集全部初始化-1

/**
 * @brief 创建一个数据库,并在数据库中创建一个表,用来保存用户信息
 * 
 * @return int
 * 成功返回 SUCCESS;
 * 失败返回 FAILURE;
 */
int Sqlite_Init();
int Sqlite_Init2();  //用于保存用户之间的聊天记录


/**
 * @brief 用户注册接收函数
 * 
 * @return int
 * 成功返回 SUCCESS;
 * 失败返回 FAILURE;
 */
int client_register(int clientfd,struct message*user);
int my_callback(void *para,int columncount,char**columvalue,char**columname);//回调函数,用来判定用户是否已经存在





/**
 * @brief 用户登录处理函数函数
 * 
 * @return int
 * 成功返回 SUCCESS;
 * 失败返回 FAILURE;
 */
int client_login(int clientfd,struct message*user);





/**
 * @brief 私聊函数
 * 
 * @return int
 * 成功返回 SUCCESS;
 * 失败返回 FAILURE;
 */
int client_private(int clientfd,struct message*user);  //私聊函数



/**
 * @brief 私聊函数
 * 
 * @return int
 * 成功返回 SUCCESS;
 * 失败返回 FAILURE;
 */
int client_group(int clientfd,struct message*user);  //群聊函数


int Applying_Administrator(int clientfd,struct message*user);//管理员申请


int user_silent(int clientfd,struct message*user); //用户禁言函数


int Remove_banned(int clientfd,struct message*user);//解除禁言



int Forced_offline(int clientfd,struct message*user);//用户强制下线函数



int user_exit();//用户主动退出,并关闭客户端


int file_transfer(int clientfd,struct message*user);//文件传输


int Viewing_Chat_History(int clientfd,struct message*user); //查看聊天记录


int Viewing_Online_Users(int clientfd,struct message*user); //查看在线用户





main.c代码

#include"server.h"
struct message user;
extern sqlite3 *ppdb;
extern sqlite3 *db;
int read_ret;
int main(int argc, char const *argv[])
{
    signal(SIGPIPE,SIG_IGN);//忽略SIGPIPE信号,防止服务器向客户端发送消息时,客户端已经关闭,导致服务器异常终止
  
    
    //创建一个数据库用来存储用户名信息
    if(Sqlite_Init()!=SUCCESS)
    {
        printf("Sqlite_Init is error\n");
        exit(1);
    }
    else
    {
        printf("Sqlite_Init is success\n");
    }

    //创建一个数据库用于保存用户之间的聊天记录
    if(Sqlite_Init2()!=SUCCESS)
    {
        printf("Sqlite_Init2 is error\n");
        exit(1);
    }
    else
    {
        printf("Sqlite_Init2 is success\n");
    }
   
   
    //服务器架构选择poll+tcp

    int sockfd;//创建服务器端套接字
    int clientfd;//用于接受客户端文件描述符
   
    struct pollfd fds[Client_Size];//同时连接客户端的最大人数
    for(int i=0;i<Client_Size;i++)
    {
        fds[i].fd=-1;
    }
    
    //fds_init(fds);//全部初始化为-1
    
    if(Socket_Init(&sockfd)==SUCCESS) //初始化套接字
    {
        printf("sockfet_init success\n");
    }
    else
    {
        printf("socket_init failure\n");
    }

    fds[0].fd=sockfd;
    fds[0].events=POLLIN;
    struct sockaddr_in client_addr;
    
    while(1)
    {
        //开始监听服务器套接字,有客户端连接时,sockfd会发生变化
        int Connect_Num=poll(fds,Client_Size,-1);
        if(fds[0].events==fds[0].revents)
        {          
            socklen_t addr_len=sizeof(struct sockaddr);
            clientfd=accept(sockfd,(struct sockaddr*)&client_addr,&addr_len);
            //clientfd:连接描述符:每连接一个客户端成功,就会生成一个文件描述符,只要知道这个描述符,就可以与对应客户通信
            if(clientfd<0)//防止没有客户端连接报错,非阻塞形式,连续看一下
            {
                //errno==EAGAIN,因为没有读到数据而报错
                 if(errno != EAGAIN && errno != EWOULDBLOCK && errno!=EINTR)
                {
                    perror("accept error!");
                    exit(1);
                }
                continue;
            }
            printf("connect client:ip=%s port=%d\n",inet_ntoa(client_addr.sin_addr),ntohs(client_addr.sin_port));
            
           
            for(int i=0;i<Client_Size;i++)
            {
                if(fds[i].fd==-1)
                {
                    
                    fds[i].fd=clientfd;
                    fds[i].events=POLLIN;
                    break;
                }
            }
            if(--Connect_Num<=0)
            {
                continue;
            } 
        }   
        for(int i=1;i<Client_Size;i++)
        {
            if(fds[i].fd!=-1)
            {
                if(fds[i].events==fds[i].revents)
                {
                    clientfd=fds[i].fd;
                    memset(&user,0,sizeof(user));
                    read_ret=recv(clientfd,&user,sizeof(user),0);
            

                    if(read_ret<0)
                    {
                        perror("read error!");
                        fds[i].fd=-1;
                    }
                    if(read_ret==0)
                    {
                        printf("client is close\n");
                        fds[i].fd=-1;
                        
                    }
                    if(read_ret>0)
                    {
                        //printf("已成功收到数据\n");
                        switch (user.cmd)
                        {
                            case REGISTER:

                            read_ret=client_register(clientfd,&user);//执行注册函数
                            if(read_ret!=SUCCESS)
                            {
                                printf("注册失败\n");
                            }
                            else
                            {
                                printf("注册成功\n");
                            }

                                break;
                            
                            case LOGIN:

                            read_ret=client_login(clientfd,&user);//执行登录函数
                                break;
                            
                            case  PRIVATE:
                             
                            read_ret=client_private(clientfd,&user);//执行私聊函数
                                
                                break;

                            
                            case GROUP:
                            read_ret=client_group(clientfd,&user);//执行群聊函数
                                break;
                           
                            case ADMINISTER:
                                Applying_Administrator(clientfd,&user);//管理员申请                               
                                break;
                           
                            case SILENT:
                                user_silent(clientfd,&user);//用户禁言函数
                                break;
                            case REMOVE:
                                Remove_banned(clientfd,&user);//用户解除禁言函数
                                break;
                            case FORCE:
                                Forced_offline(clientfd,&user);//用户强制下线函数
                                break;
                            case FILE_SEND:
                                file_transfer(clientfd,&user);//文件发送函数
                                break;
                            case VIEW:
                                Viewing_Chat_History(clientfd,&user); //查看聊天记录
                                break;
                            case ONLINE:
                                 Viewing_Online_Users(clientfd,&user); //查看在线用户
                                break;
                            case EXIT:
                                user_exit(clientfd,&user);//用户主动退出,并关闭客户端,删除链表中对应姓名
                                break;


                               
                            default:
                                break;
                        }
                      
                    }
                    if(--Connect_Num<=0)
                    {
                        break;
                    }
                }
            }
           
        }









    }

    



    return 0;
}

server.c代码

#include"server.h"
sqlite3 *ppdb;
sqlite3 *db;
int flag=0;
struct online *head = NULL;
void* insert_Node(struct online* Node)  //头插,将cfd和姓名插入
{
    Node->next = head;
    head=Node;
    return head;
}


int Socket_Init(int* sockfd) 
{
    //socket:创建套接字,通过参数传递socket地址
    //初始化结构体struct sockaddr_in
    //bind:绑定IP地址和端口号
    //调用listen,函数转换成被动监听描述符(等待连接)
    *sockfd = socket(AF_INET,SOCK_STREAM|SOCK_NONBLOCK,0);
    //将套接字设置成非阻塞
    struct sockaddr_in server_addr;
    
    server_addr.sin_family=AF_INET;//地址组协议,IPV4协议
    server_addr.sin_port=htons(PORT);
    server_addr.sin_addr.s_addr=inet_addr("192.168.152.130");

    int opt = 1;
    setsockopt(*sockfd,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));//设置可重复绑定使用一个端口号

    int ret=bind(*sockfd,(struct sockaddr*)&server_addr,sizeof(server_addr));//bind:绑定IP地址和端口号
    if(ret<0)
    {
        perror("bind error!");
        exit(1);
    }
    else
    {
        printf("bind is successfully\n");
    }
    
    //监听
    if(listen(*sockfd,MaxNum)<0)
    {
        perror("listen is error!");
        exit(1);
    }

    return SUCCESS;
}


int Sqlite_Init()  //用于保存用户信息
{

    //打开或者创建数据库
    int ret=sqlite3_open("stu.db",&ppdb);
    if(ret!=SQLITE_OK)
    {
    	printf("sqlite3 open:%s\n",sqlite3_errmsg(ppdb));
        return FALIURE;		   
    }
    //创建表
    char sql[128]={0};
    sprintf(sql,"create table if not exists stu(id int,name text,sex text,password text,code text,age integer,VIP integer,flag integer);");
    ret=sqlite3_exec(ppdb,sql,NULL,NULL,NULL);
    if(ret!=SQLITE_OK)
    {
        printf("sqlite3_exec2:%s\n",sqlite3_errmsg(ppdb));
        return FALIURE;
    }
    return SUCCESS;
}

int Sqlite_Init2()  //用于保存用户之间的聊天记录
{

    //打开或者创建数据库
    int ret=sqlite3_open("stu2.db",&db);
    if(ret!=SQLITE_OK)
    {
    	printf("sqlite3 open:%s\n",sqlite3_errmsg(db));
        return FALIURE;		   
    }
    //创建表
    char sql[128]={0};
    sprintf(sql,"create table if not exists stu2(datetime numeric,name text,toname text,message text);");
    ret=sqlite3_exec(db,sql,NULL,NULL,NULL);
    if(ret!=SQLITE_OK)
    {
        printf("sqlite3_exec2:%s\n",sqlite3_errmsg(ppdb));
        return FALIURE;
    }
    return SUCCESS;
}

int my_callback(void *para,int columncount,char**columvalue,char**columname)//数据库查询的回调函数
{
    
    flag=1;
 
    return 0;
}

int client_register(int clientfd,struct message*user)
{
    //首显是遍历数据库,查看数据库,用户名是否已经存在,如果不存在,插入用户数据
    printf("已进入注册界面\n");

    char*errmsg;
    char sql[1024];
    memset(sql,0,sizeof(sql));
    sprintf(sql,"select *from 
  • 4
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

李 同学

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值