基于sqlite3数据库实现UDP聊天室

实现了基于sqlite3数据库使用UDP协议完成登录注册群聊、私聊、上线下线通知、查看聊天记录功能的聊天室

关于UDP协议


UDP 协议(用户数据报协议)是建立在 IP 协议基础之上的,用在传输层的协议。UDP 提供了无连接的数据报服务。UDP 和 IP 协议一样,是不可靠的数据报服务。

1.UDP 提供无连接服务
2.UDP 缺乏可靠性支持,应用程序必须实现:确认、超时、重传、流控等
3.UDP 面向记录服务


UDP编程的一般步骤


1.UDP编程的服务器端一般步骤是:
  1、创建一个socket,用函数socket();
  2、设置socket属性,用函数setsockopt();* 可选
  3、绑定IP地址、端口等信息到socket上,用函数bind();
  4、循环接收数据,用函数recvfrom();
  5、关闭网络连接;

2.UDP编程的客户端一般步骤是:
  1、创建一个socket,用函数socket();
  2、设置socket属性,用函数setsockopt();* 可选
  3、绑定IP地址、端口等信息到socket上,用函数bind();* 可选
  4、设置对方的IP地址和端口等属性;
  5、发送数据,用函数sendto();
  6、关闭网络连接;

流程图

sqlite3数据库

建两个表 usr表用来存储用户名和密码,用来完成注册登录操作,record表用来记录聊天信息。

服务器

/*===============================================
 *   文件名称:ser.c
 *   创 建 者:     
 *   创建日期:2023年08月28日
 *   描    述:
 ================================================*/
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <string.h>
#include <arpa/inet.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include <sys/wait.h>
#include <dirent.h>
#include <sys/stat.h>
#include <signal.h>
#include <pthread.h>
#include <sqlite3.h>


#define ERRLOG(errmsg) do{\
    printf("%s %s %id\n", __FILE__, __func__, __LINE__);\
    perror(errmsg);\
    exit(-1);\
}while(0)

typedef struct wm{
    int type;
    char name[30];
    char destname[30];
    char data[100];
}MSG;

typedef struct node{
    struct wm user;
    struct sockaddr_in addr;
    struct node * next;
}*linklist,linknode;


typedef struct {
    int fd;
    int len; 
    struct wm his;
    struct sockaddr_in hddr;
}call;

linklist list_create(){
    linklist H=(linklist)malloc(sizeof(linknode));
    if(NULL==H){
        printf("create fail\n");
        return NULL;
    }
    H->next=NULL;
    return H;
}

void insert_linklist(MSG msg,struct sockaddr_in cliaddr,linklist p){
    linklist t=p;
    while(t->next!=NULL){
        t=t->next;
    }
    linklist new=(linklist)malloc(sizeof(linknode));
    new->addr=cliaddr;
    new->user=msg;
    new->next=NULL;
    t->next=new;
    return;
}

void delete_linklist(linklist p,MSG*msg){
    linklist l=p;
    if(p->next==NULL) {
        perror("linklist NULL");
        exit(-1);
    }
    while(l->next!=NULL){
        if(strcmp(l->next->user.name,msg->name)==0)
        {

            if(l->next->next==NULL){
                l->next=NULL; 
                printf("%s从链表中删除\n",msg->name);
                break;
            }  
            l->next=l->next->next;
            printf("%s从链表中删除\n",msg->name);
            break;
        }
        else
        {
            l=l->next;
        }
    } 
}


void zhuce(int fd,MSG*msg,sqlite3*db,struct sockaddr_in cliaddr,int len){

    char *errmsg;
    char sql[200]={0};
    sprintf(sql,"INSERT INTO usr VALUES('%s','%s')",msg->name,msg->data);
    printf("%s\n",sql);
    if( sqlite3_exec(db,sql,NULL,NULL,&errmsg)!=SQLITE_OK)
    { 

        sprintf(msg->data,"name %s exit",msg->name);
        printf("%s\n",msg->data);
    }
    else
    {
        sprintf(msg->data,"zhuce success");
        printf("%s\n",msg->data);
    }

    if(sendto(fd,msg,sizeof(MSG),0,(struct sockaddr * )&cliaddr,len)==-1){
        perror("send error\n");
        exit(-1);
    }
    return;
}

int denglu(int fd,MSG*msg,sqlite3*db,struct sockaddr_in cliaddr,int len,linklist p){
    char *errmsg;
    char sql[200]={0};
    char **result;
    int row;
    int col;
    linklist pp=p->next;
    printf("%s正在登陆\n",msg->name);
    sprintf(sql,"select * from usr where name = '%s' and pass = '%s'",msg->name,msg->data);
    if( sqlite3_get_table(db,sql,&result,&row,&col,&errmsg)!=SQLITE_OK)
    { 
        printf("%s",errmsg);
        exit(-1);
    }
    else
    {
        if(row==0){
            sprintf(msg->data,"用户名或密码错误");
            if(sendto(fd,msg,sizeof(MSG),0,(struct sockaddr * )&cliaddr,len)==-1){
                perror("send error\n");
                exit(-1);
            }
            return -1;
        } 
        else
        {
            sprintf(msg->data,"denglu success");
            if(sendto(fd,msg,sizeof(MSG),0,(struct sockaddr * )&cliaddr,len)==-1){
                perror("send error\n");
                exit(-1);
            }

            while(pp!=NULL)
            {   
                sprintf(msg->data,"%s上线啦\n",msg->name);
                printf("%s\n",msg->data);  
                if(sendto(fd,msg,sizeof(MSG),0,(struct sockaddr *)&pp->addr,sizeof(pp->addr)) < 0)
                {
                    perror("sendto\n");
                    exit(-1);
                }
                pp=pp->next;

            }
            return 1;
        }

    }
}



void chat(int fd,MSG*msg,sqlite3*db,struct sockaddr_in cliaddr,int len,linklist p){

    char sql[300];
    char *errmsg;
    char date[100]={0};
    linklist q,m,n;
    q=p->next;
    m=p->next;
    n=p->next;
    if(q==NULL)
    {
        perror("linklist NULL");
        exit(-1);
    }

    time_t tm;
    struct tm *tmp_ptr = NULL;
    time(&tm);
    tmp_ptr = localtime(&tm);
    sprintf (date,"%d-%02d-%02d %02d:%02d:%02d", (1900+tmp_ptr->tm_year), (1+tmp_ptr->tm_mon), tmp_ptr->tm_mday, tmp_ptr->tm_hour, tmp_ptr->tm_min, tmp_ptr->tm_sec);

    sprintf(sql,"insert into record values('%s','%s','%s')",msg->name,date,msg->data);
    printf("%s",date);

    if(sqlite3_exec(db,sql,NULL,NULL,&errmsg)!=SQLITE_OK){
        printf("%s\n",errmsg);
        exit(-1);
    }

    while(q!=NULL)
    {   
        if(strcmp(q->user.name,msg->name)==0){
            q=q->next;
        }
        else{
            if(strcmp(msg->data,"quit")==0){
                sprintf(msg->data,"[%s]已离线",msg->name);
                delete_linklist(p,msg);
                printf("%s is quit\n",msg->name);
            }else{
                char temp[100]={0};
                strcpy(temp,msg->data);
                sprintf(msg->data,"%s\n[%s]:%s\n",date,msg->name,temp);
                printf("%s\n",msg->data);  
            }
            if(sendto(fd,msg,sizeof(MSG),0,(struct sockaddr *)&q->addr,sizeof(q->addr)) < 0)
            {
                perror("sendto\n");
                exit(-1);
            }
            else{
                q= q->next;
            }

        }
    }

    return;
}


//2. 列数3.列的结果 4.列的名字
int hiscallback(void *arg,int num,char** value,char ** name){
    call s=*(call*)arg;
    sprintf(s.his.data,"%s [%s]:%s",value[1],value[0],value[2]);
    sendto(s.fd,&s.his,sizeof(MSG),0,(struct sockaddr *)&(s.hddr),s.len);
    return 0;
}


int history(int fd,MSG msg,sqlite3*db,struct sockaddr_in cliaddr,int len){
    printf("find history\n");
    call c;
    c.fd=fd;
    c.hddr=cliaddr;
    c.len=len;
    c.his=msg;
    char *errmsg;

    if( sqlite3_exec(db ,"select * from record", hiscallback , (void*)&c , &errmsg)!=SQLITE_OK)
    { 
        printf("error:%s\n",errmsg);
        sqlite3_free(errmsg);
        exit(-1);
    }
    //解决阻塞
    strcpy(msg.data, "*");
    if(sendto(fd,&msg,sizeof(MSG),0,(struct sockaddr * )&cliaddr,len)==-1)
    {
        perror("fail to send");
        exit(-1);
    }
    return 1;

}

void siliao(int fd,MSG*msg,sqlite3*db,struct sockaddr_in cliaddr,int len,linklist q){
    printf("in siliao\n");
    char sql[300];
    char *errmsg;
    char date[100]={0};
    linklist m=q->next;
    linklist n=q->next;

    if(m==NULL){
        printf("kong linklist\n");   
        perror("linklist"); 
        exit(-1);
    }
    else{
        printf("%s\n",m->user.name);

        printf("链表不为空\n");
    }


    while(m!=NULL)
    {
        if(strcmp(m->user.name,msg->destname)==0){
            char temp[100]={0};
            strcpy(temp,msg->data);
            sprintf(msg->data,"%s to %s:%s",msg->name,msg->destname,temp);
            printf("%s\n",msg->data);

            if(sendto(fd,msg,sizeof(MSG),0,(struct sockaddr *)&m->addr,sizeof(m->addr)) < 0)
            {
                perror("sendto\n");
                exit(-1);
            }
            else{ printf("send success\n");
    time_t tm;
    struct tm *tmp_ptr = NULL;
    time(&tm);
    tmp_ptr = localtime(&tm);
    sprintf (date,"%d-%02d-%02d %02d:%02d:%02d", (1900+tmp_ptr->tm_year), (1+tmp_ptr->tm_mon), tmp_ptr->tm_mday, tmp_ptr->tm_hour, tmp_ptr->tm_min, tmp_ptr->tm_sec);

    sprintf(sql,"insert into record values('%s','%s','%s')",msg->name,date,msg->data);
    printf("%s",sql);

    if(sqlite3_exec(db,sql,NULL,NULL,&errmsg)!=SQLITE_OK){
        printf("%s\n",errmsg);
        exit(-1);
    }
                break;
            } 

        }

        else{
            m=m->next;
            printf("goto next\n");
        }

    }

    if(m==NULL){
        printf("destname not exit\n");
        sprintf(msg->data,"...[%s] not online",msg->destname);
        if(sendto(fd,msg,sizeof(MSG),0,(struct sockaddr *)&cliaddr,len) < 0)
        {
            perror("sendto\n");
            exit(-1);
        }
    }
}


int main(int argc, const char *argv[]){

    MSG msg;
    sqlite3 *db;
    //入参合理性检查
    if(3 != argc){
        printf("Usage : %s <IP> <PORT>\n", argv[0]);
        exit(-1);
    }


    if(sqlite3_open("my.db",&db)!=SQLITE_OK)
    {
        printf("%s\n",sqlite3_errmsg(db));
        return -1;
    }
    else{
        printf("打开数据库成功\n"); 
    }

    //创建用户数据报套接字
    int fd = socket(AF_INET, SOCK_DGRAM, 0);
    if(-1 == fd){
        ERRLOG("socket error");
    }

    //填充网络信息结构体
    struct sockaddr_in serveraddr;
    memset(&serveraddr, 0, sizeof(serveraddr));
    serveraddr.sin_family = AF_INET;
    serveraddr.sin_port = htons(atoi(argv[2]));
    serveraddr.sin_addr.s_addr = inet_addr(argv[1]);

    socklen_t serveraddr_len = sizeof(serveraddr);

    //3.绑定
    if(-1 == bind(fd, (struct sockaddr *)&serveraddr, serveraddr_len)){
        ERRLOG("bind error");
    }

    //定义结构体保存客户端的信息
    //如果使用UDP时不保存客户端的网络信息结构体
    //可以接受到客户端发来的数据  但是没法回信给客户端 因为sendto的后两个参数需要用到
    struct sockaddr_in cliaddr;
    int len = sizeof(cliaddr);

    linklist p=list_create();//群聊
    linklist q=list_create();//私聊
    while(1){
        int ret =recvfrom(fd,&msg,sizeof(MSG), 0, (struct sockaddr *)&cliaddr, &len);
        if(ret>0)
        {
            switch(msg.type)
            {
                case 1:
                    zhuce(fd,&msg,db,cliaddr,len);
                    break;
                case 2:
                    if(denglu(fd,&msg,db,cliaddr,len,p)==1){
                        insert_linklist(msg,cliaddr,p);
                        insert_linklist(msg,cliaddr,q);
                        break;
                    }else{
                        break;}
                case 3:
                    chat(fd,&msg,db,cliaddr,len,p);
                    break;
                case 4:
                    history(fd,msg,db,cliaddr,len);
                    break;
                case 5:
                    siliao(fd,&msg,db,cliaddr,len,q);
                    break;
defalut:
                    printf("recv error\n");
            }

        }

    }

    close(fd); 
    return 0;
}




客户端

/*===============================================
 *   文件名称:cli.c
 *   创 建 者:     
 *   创建日期:2023年08月28日
 *   描    述:
 ================================================*/
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <string.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <string.h>
#include <sys/wait.h>
#include <dirent.h>
#include <sys/stat.h>


#define Z 1  //注册
#define D 2  //登陆
#define C 3  //聊天
#define H 4  //历史
#define S 5  //悄悄话

#define ERRLOG(errmsg) do{\
    printf("%s %s %d\n", __FILE__, __func__, __LINE__);\
    perror(errmsg);\
    exit(-1);\
}while(0)

typedef struct wm{
    int type;
    char name[30];
    char destname[30];
    char data[100];
}MSG;


void zhuce(int fd,MSG*msg,struct sockaddr_in saddr,int len){
    msg->type=1;
    printf("\033[1;33m输入网名\n");
    fgets(msg->name,30,stdin);
    msg->name[strlen(msg->name)-1]='\0';

    printf("\033[0;35m输入密码\n");
    fgets(msg->data,100,stdin);
    msg->data[strlen(msg->data)-1]='\0';


    if(sendto(fd,msg,sizeof(MSG),0,(struct sockaddr * )&saddr,len)==-1)
    {
        perror("send 失败");
        exit(-1);
    }
    if(recvfrom(fd,msg,sizeof(MSG),0,NULL,NULL)==-1)
    {
        perror("recv 失败");
        exit(-1);
    }
    printf("%s\n",msg->data);
    return;
}


int denglu(int fd,MSG*msg,struct sockaddr_in saddr,int len){
    msg->type=2;
    printf("\033[1;36m输入网名\n");
    fgets(msg->name,30,stdin);
    msg->name[strlen(msg->name)-1]='\0';

    printf("\033[0;33m输入密码\n");
    fgets(msg->data,100,stdin);
    msg->data[strlen(msg->data)-1]='\0';


    if(sendto(fd,msg,sizeof(MSG),0,(struct sockaddr * )&saddr,len)==-1)
    {
        perror("send 失败");
        exit(-1);
    }
    if(recvfrom(fd,msg,sizeof(MSG),0,NULL,NULL)==-1)
    {
        perror("recv 失败");
        exit(-1);
    }

    if(strncmp(msg->data,"denglu success",14)==0){
        printf("\033[0;35m%s 登陆成功,欢迎回来\n",msg->name);    
        return 1;
    }else{
        printf("\033[1;33m%s\n",msg->data);    
        return -1;
    }
}


void chat(int fd,MSG*msg,struct sockaddr_in saddr,int len){

    MSG*temp; 
    msg->type=3;
    pid_t pid= fork();     
    if(pid<0){
        perror("fork");
        exit(-1);
    }
    else if(pid==0)
    {
        while(1)
        {
            //接收
            //   memset(temp->name,0,sizeof(temp->data));
            //   memset(temp->data,0,sizeof(temp->data));
            if(recvfrom(fd,temp,sizeof(MSG),0,NULL,NULL)==-1)
            {
                perror("recv 失败");
                exit(-1);
            }
            printf("\033[1;33m%s\n",temp->data);
        }
    }

    else{
        //发送
        while(1){
            //   memset(msg->data,0,sizeof(msg->data));
            fgets(msg->data,100,stdin);
            msg->data[strlen(msg->data)-1]='\0';

            if(strcmp(msg->data,"quit")==0)
            {
            if(sendto(fd,msg,sizeof(MSG),0,(struct sockaddr * )&saddr,len)==-1)
            {
                perror("send 失败");
                exit(-1);
            }
                kill(pid,SIGKILL);     
                wait(NULL);
                exit(1);  
                break;
            }
            if(sendto(fd,msg,sizeof(MSG),0,(struct sockaddr * )&saddr,len)==-1)
            {
                perror("send 失败");
                exit(-1);
            }
        }
    }
    close(fd);
    return;
}



int history(int fd,MSG*msg,struct sockaddr_in saddr,int len){
    msg->type=4;
    if(sendto(fd,msg,sizeof(MSG),0,(struct sockaddr * )&saddr,len)==-1)
    {
        perror("历史失败");
        exit(-1);
    }
    while(1){
        if(recvfrom(fd,msg,sizeof(MSG),0,(struct sockaddr*)&saddr,&len)==-1)
        {
            perror("recv 失败");
            exit(-1);
        }
        //解决阻塞 
        if(0==strcmp(msg->data,"*"))
        {
            break;
        }

        printf("%s\n",msg->data);    
    }

    return 1;

}

void siliao(int fd,MSG*msg,struct sockaddr_in saddr,int len){

   MSG*temp; 
   msg->type=5;
   printf("请输入你要连接人的名字\n");
   fgets(msg->destname,30,stdin);
   msg->destname[strlen(msg->destname)-1]='\0';
  
             printf("输入你想要发送的数据\n");
   pid_t pid=fork();
  if(pid<0){
   perror("fork\n");
   exit(-1);
  }
 else if(pid==0)
    {
        while(1)
        {
            //接收
            //   memset(temp->name,0,sizeof(temp->data));
            //   memset(temp->data,0,sizeof(temp->data));
            if(recvfrom(fd,temp,sizeof(MSG),0,NULL,NULL)==-1)
            {
                perror("recv 失败");
                exit(-1);
            }
            printf("\033[1;33m%s\n",temp->data);
        }
    }

    else{
        //发送
        while(1){
            //   memset(msg->data,0,sizeof(msg->data));
            fgets(msg->data,100,stdin);
            msg->data[strlen(msg->data)-1]='\0';

            if(strcmp(msg->data,"quit")==0)
            {

                kill(pid,SIGKILL);     
                wait(NULL);
                exit(1);  
                break;
            }
            if(sendto(fd,msg,sizeof(MSG),0,(struct sockaddr * )&saddr,len)==-1)
            {
                perror("send 失败");
                exit(-1);
            }
        }
    }
    close(fd);
    return;

}




void chat_menu(int fd,MSG*msg,struct sockaddr_in saddr,int len){
    int  n;
    while(1) {
        printf("\033[1;35m🌵🌵🌵🌵🌵🌵🌵🌵🌵🌵🌵🌵\n");
        printf("\033[1;31m登陆成功!你来啦! %s  !\n",msg->name);
        printf("\033[0;35m        使用功能      \n");
        printf("\033[1;36m1.加入群聊   \033[1;33m2.查看历史聊天记录\n");
        printf("\033[1;34m3.加入悄悄话             \033[1;32m4.退出\n");
        printf("\033[1;35m🌵🌵🌵🌵🌵🌵🌵🌵🌵🌵🌵🌵\n");

        int sc=scanf("%d",&n);
        if(sc==-1){
            perror("scanf\n");
            exit(-1);
        }
        getchar();
        switch(n)
        {
            case 1:
                chat(fd,msg,saddr,len);
                break;
            case 2:
            history(fd,msg,saddr,len);
                break;
            case 3:
                siliao(fd,msg,saddr,len);
                break;
            case 4:
                printf("\033[1;36m感谢使用,欢迎下次再来\n");
                close(fd);
                exit(0);
                break;

            default:
                printf("\033[1;31m输入错误\n");
                break;
        }
    }
    return;
}



int main(int argc, const char *argv[]){
    //入参合理性检查
    if(3 != argc){
        printf("Usage : %s <IP> <PORT>\n", argv[0]);
        exit(-1);
    }

    //1.创建用户数据报套接字
    int fd = socket(AF_INET, SOCK_DGRAM, 0);
    if(-1 == fd){
        ERRLOG("socket error");
    }
    //2.填充服务器的网络信息结构体
    struct sockaddr_in seraddr;
    memset(&seraddr, 0, sizeof(seraddr));
    seraddr.sin_family = AF_INET;
    seraddr.sin_port = htons(atoi(argv[2]));
    seraddr.sin_addr.s_addr = inet_addr(argv[1]);
    int len = sizeof(seraddr);

    int choose;
    while(1){
        MSG msg;    
        printf("\033[1;35m🌵🌵🌵🌵🌵🌵🌵🌵🌵🌵🌵🌵\n");
        printf("\033[1;33m   欢迎使用聊天室系统   \n");
        printf("\033[1;36m        选择功能        \n");
        printf("\033[1;31m1.注册  \033[1;35m2.登陆    \033[1;32m3.退出\n");
        printf("\033[1;35m🌵🌵🌵🌵🌵🌵🌵🌵🌵🌵🌵🌵\n");

        int sc=scanf("%d",&choose);
        if(sc==-1){
            perror("scanf\n");
            exit(-1);
        }
        getchar();

        switch(choose)
        {
            case 1:
                zhuce(fd,&msg,seraddr,len);
                break;
            case 2:
                if(denglu(fd,&msg,seraddr,len)==1){
                    chat_menu(fd,&msg,seraddr,len);  
                }
                break;
            case 3:
                printf("\033[1;34m感谢使用,欢迎下次再来\n");
                close(fd);
                exit(0);
            default:
                printf("\033[1;34m输入错了请重新输入\n");
        }  
    }

    return 0;
}

关于编译

服务器:

客户端:

 运行结果图

1.注册功能:

2.登录功能,密码匹配

3.群聊功能

4.私聊功能

5.查看聊天记录功能

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值