LINUX 系统编程 局域网聊天室项目

一.所需工具

1.vmwarestation虚拟机软件

2.ubuntu系统(64位)

3.vscode编译器(方便模块化编程)

4. mysql8.0,以及相关指令

1)安装数据库服务器:sudo apt-get install mysql-server
2)安装数据库客户端:sudo apt-get install mysql-client
3)安装数据库开发包:sudo apt-get install libmysqlclient-dev
5.TCP协议,以及其相关函数        
        ps :shell终端里ifconfig可以查看ip。
6.线程及其相关函数
7.多路IO复用及其相关函数(这里用的select)
8.相关功能
1)登录系统(注册)(登录)(注销)        >>已完成
2)群聊                                                      >>已完成
3)私聊(单向)                                        >>已完成
4)显示昵称,时间                                     >>已完成
5)查看聊天内容(数据库)                       >>已完成
6)管理员系统(禁言,解禁,踢出群聊)  >>已完成
7)文件上传                                                 >>未完成(本人能力所限,正在研究)
        本人刚学Linux系统编程,才疏学浅,次帖子做学习记录所用,代码结构也比较混乱,其中不乏有许多小bug,也望大家批评指教,
        所用的ip是宏定义,只需在宏定义时把ip改成自己的即可,ip查看方法时在shell终端输入
ifconfig。
主函main.c
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <denglu.h>
#include "server.h"


int main()
{
	server();//开启服务器
	sleep(3);

	return (0);
}

服务器端server.c

#include "server.h"

#define IP "192.168.134.10"
#define PORT 6667
#define PEOPLE 10//连接客户端的数量


extern struct infor
{   char id[20];//承接账号
    char pw[20];//承接密码
    char name[20];//承接昵称
    int fd;       //承接通信套节字
    int index;    //承接轮训下标,即count的值
    int jinzhi;//承接禁止标识位
    int onlin;
}zhanghao;                                             //账号系统的结构体,存放id,pw,name,fd等信息
struct infor zhanghao_shuzu[PEOPLE];//制作结构体数组,用来存储登陆的在线人员信息

pthread_t pth_id;//线程的进程号
int ser_ac_fd[PEOPLE];//通信描述符
int count;//统计在线人数,count表示最后加入的人在数组的下标
struct timeval shijian;//轮询用的结构体,里面是时间
int maxfd;
fd_set backup;
struct sockaddr_in clien;//此结构体存放连接的客户端信息


 
void server(void)
{   
    //mysql数据库
    MYSQL * my = mysql_init(NULL);//初始化MYSQL结构体
    // MYSQL_RES * res;//存放执行结果函数的返回值
    // MYSQL_ROW row;//返回结果集的下一行的返回值
    int chat_ret;//mysql_real_query返回值
    char chat_buf[100];//通信用的字符串
    my = mysql_real_connect(my,"localhost","root",
    "1","wechat",0,NULL,0);//连接MYSQL服务器,在终端执行可执行程序时需要加sudo
    if(my==NULL)
        printf("连接数据库失败!\n");
    
    //TCP通信
    int i,j,ret;//maxfd:最大描述符。i,j:循环。ret:承接返回值
    int ser_fd,flag=1;//套节字相关变量
    fd_set set;//多路io复用相关变量
    char buf[100];//通信用的字符串
    shijian.tv_usec=2;//轮询时间定为2秒;usec2毫秒
    socklen_t len;

    //TCP客户端
    ser_fd = socket(AF_INET,SOCK_STREAM,0);//创建套节字
    setsockopt(ser_fd,SOL_SOCKET,SO_REUSEADDR,&flag,sizeof(flag));//重用本地地址和端口
    struct sockaddr_in ser_addr;//此结构体存放服务器信息
    ser_addr.sin_family = AF_INET;
    ser_addr.sin_port = htons(PORT);
    ser_addr.sin_addr.s_addr=inet_addr(IP);
    bind(ser_fd,(struct sockaddr *)&ser_addr,sizeof(ser_addr));//绑定服务器地址
    listen(ser_fd,PEOPLE);//监听,设置服务器在同一时刻允许多少客户端连接

    len=sizeof(clien);

    FD_ZERO(&backup);//清空集合
    FD_SET(ser_fd,&backup);//将连接套节字放到集合中    
    maxfd = ser_fd;//将连接套节字设置为初始最大描述符

    while(1)
    {
       // printf("进入while循环\n");
            set = backup;
            ret = select(maxfd+1,&set,NULL,NULL,&shijian);//创建select项目/
               // perror("select");
            //printf("\n创建select项目\n");
            
            //连接套节字变化
            if(FD_ISSET(ser_fd,&set))//连接套节字发生变化,说明有客户端要连接,上accpet
            {
                ser_ac_fd [count]= accept(ser_fd,(struct sockaddr *)&clien,&len);//等待客户端连接,成功返回通信套节子,失败返回-1
                //这个地方加线程,即在accept之后recv之前做线程,利用通信套节字来然让客户端进行账号系统的使用
                pthread_create(&pth_id,NULL,dengluxiancheng,NULL);
                    // perror("pthread_create");
            }   

            //通信套节字变化
            for(i=0;i<count;i++)
            {
                //printf("进入通信套接字循环\n");
                if(FD_ISSET(ser_ac_fd[i],&set))//判断通信套节字是否变化
                { 
                    printf("11111进入通信套接字循环11111\n");
                    memset(buf,0,sizeof(buf));
                    ret=recv(ser_ac_fd[i],buf,sizeof(buf),0);//recv返回值是接收到的字节,当客户端下线,recv不再阻塞,且返回0
                    if(ret==0)//客户端下线消息转发
                        sprintf(buf,"客户端:%s:%d,name=%s,id=%s下线",inet_ntoa(clien.sin_addr),ntohs(clien.sin_port),
                                                zhanghao_shuzu[i].name,zhanghao_shuzu[i].id);
                    
                    if(zhanghao_shuzu[i].jinzhi==0)//等于0说明没有被禁言,把他的发的消息存入数据库
                    {
                        memset(chat_buf,0,sizeof(chat_buf));
                        sprintf(chat_buf,"insert into chat_date values('%s')",buf);//将mysql数据库指令写入
                        chat_ret= mysql_real_query(my,chat_buf,strlen(chat_buf));//指令执行
                        if(chat_ret!=0)
                            printf("查询发生错误!chat_ret==%d/n",chat_ret);//mysql不能使用perror
                    }

                    //转发客户端消息
                    for(j=0;j<count;j++)
                    {
                        //禁言
                        if(zhanghao_shuzu[i].jinzhi==1)//禁言标识符为1,说明要禁言
                            break;
                        ///群聊
                        printf("接收到:buf=%s\n",buf);
                        if(j!=i)
                        send(ser_ac_fd[j],buf,strlen(buf),0);//给除了发消息的客户端外所有客户端发送消息,即群聊功
                    
                    }
                    //客户端下线处理
                    if((ret==0))//清理下线的客户端
                    {
                        printf("开始清除1\n");
                        //1.从轮询集合中删除该通信符
                        FD_CLR(ser_ac_fd[i],&backup);
                        //2.将下线的客户端从数组里删除(覆盖)
                        for(j=i;j<count;j++)
                            ser_ac_fd[j]=ser_ac_fd[j+1];
                        //3.统计客户端-1,
                        count--;
                        //4.更新文件最大描述符
                        maxfd=ser_fd;
                        for (j=0;j<count;j++)
                        {
                            if(ser_ac_fd[j]>maxfd)
                                maxfd=ser_ac_fd[j];
                        }
                        //5.将对应的结构体数组删除(覆盖)
                        for(j=i;j<count;j++)
                            zhanghao_shuzu[j]=zhanghao_shuzu[j+1];

                    }            
                }
                if(zhanghao_shuzu[i].onlin==0)//被管理员踢出去的成员信息的清理
                {
                        printf("开始清除2\n");
                      //1.从轮询集合中删除该通信符
                        FD_CLR(ser_ac_fd[i],&backup);
                        //2.将下线的客户端从数组里删除(覆盖)
                        for(j=i;j<count;j++)
                            ser_ac_fd[j]=ser_ac_fd[j+1];
                        //3.统计客户端-1,
                        count--;
                        //4.更新文件最大描述符
                        maxfd=ser_fd;
                        for (j=0;j<count;j++)
                        {
                            if(ser_ac_fd[j]>maxfd)
                                maxfd=ser_ac_fd[j];
                        }
                        //5.将对应的结构体数组删除(覆盖)
                        for(j=i;j<count;j++)
                            zhanghao_shuzu[j]=zhanghao_shuzu[j+1];
                }

           }
    }
    return;
}



void * dengluxiancheng(void *arg)
{
        char sel[2]={0};
        char xuanze_buf[30];//进行群聊或私聊的选择,,传输名字
        char private_chat[100],private_keep[80];//承接私聊的内容,keep用来续接
        int k;
        int fd = ser_ac_fd [count];//fd局部变量,承接通信套节字
        pthread_t id = pthread_self();//获取当前线程号,结束时要结束该线程
        int ret = denglu(fd);//跳转进登陆系统,把要进行账号操作的客户端的套节字传过去
            perror("denglu");
         //1代表登陆成功,2代表注册成功,3,注销成功,4,管理员登陆,5,退出登陆,0代表失败
   
        //1.登陆成功
        if(ret==1)
        {
            printf("*********************\n");
            printf("*      登陆成功!     *\n");
            printf("*1.加入群聊          *\n");
            printf("*2.进行私聊          *\n");
            printf("*********************\n");
            
            zhanghao_shuzu[count]=zhanghao;//将连接的客户端的id,pw,name给到对应的结构体里,
            zhanghao_shuzu[count].fd=fd;    //将连接的客户端的通信套节字描述符给到对应的结构体里,
            zhanghao_shuzu[count].index=count;//将连接的客户端的通信套节字描是第几个给到对应的结构体里,
            zhanghao_shuzu[count].onlin=1;      //将在线标志符online赋值为1,表示在线;
            printf("id=%s,pw=%s,name=%s,fd=%d,index=%d\n",zhanghao_shuzu[count].id,zhanghao_shuzu[count].pw,
                zhanghao_shuzu[count].name,zhanghao_shuzu[count].fd,zhanghao_shuzu[count].index);       //检查denglu是否能把数据传过来

            memset(xuanze_buf,0,sizeof(xuanze_buf));
            recv(fd,xuanze_buf,sizeof(xuanze_buf),0);//群聊选择1,私聊选择2;       🍤第一次接收消息
        
            //1.加入群聊
            if(strcmp(xuanze_buf,"1")==0)//直接结束线程,进入群聊
            {
                //将连接的新的通信套节字加入轮询
                FD_SET(ser_ac_fd[count],&backup);//向集合中添加通信套节字
                if(ser_ac_fd[count]>maxfd)//让maxfd一直都是最大描述符
                    maxfd = ser_ac_fd[count];
                count++;
                printf("count=%d\n",count);
                printf("客户端上线!\n连接的客户端信息:%s:%d\n",inet_ntoa(clien.sin_addr),ntohs(clien.sin_port));//显示客户端的ip和端口 
                        
                pthread_cancel(id);//结束进入群聊的线程
            }
            
            
            //2.进行私聊,发送人数名单给客户端(由于本人技术问题无法建立双向私聊,这只是单向私聊消息)
            else if(strcmp(xuanze_buf,"2")==0)
            {   
                printf("id=%s,pw=%s,name=%s,fd=%d,index=%d\n",zhanghao_shuzu[count].id,zhanghao_shuzu[count].pw,
                    zhanghao_shuzu[count].name,zhanghao_shuzu[count].fd,zhanghao_shuzu[count].index);       //检查denglu是否能把数据传过来
                for(k=0;k<count;k++)
                {
                    send(fd,zhanghao_shuzu[k].name,strlen(zhanghao_shuzu[k].name),0);//发送在线的客户端的通信套节字的描述符
                    printf("🍤zhanghao[%d].name=%s\n",k,zhanghao_shuzu[k].name);
                    usleep(10);
                }   
                send(fd,"0",1,0);//发送0,以便让客户端知道发送结束                                               🍤第一次发送消息
                printf("text2\n");
            star:  
                memset(xuanze_buf,0,sizeof(xuanze_buf));
                recv(fd,xuanze_buf,sizeof(xuanze_buf),0);//                                         🍤第二次接收消息(姓名)
                printf("接收到的姓名是:%s...\n",xuanze_buf);
                for(k=0;k<count;k++)//对比name
                {
                    printf("遍历姓名:%s...\n",zhanghao_shuzu[k].name);
                    if(strcmp(xuanze_buf,zhanghao_shuzu[k].name)==0)//对比传输的名字,一样的话进入if语句
                    {
                        usleep(50);
                        send(fd,"私聊建立成功!",23,0);//发送在线的客户端的通信套节字的描述符       🍤第二次发送消息;
                        printf("第二次发送的信息是:私聊建立成功!");
                        //第k个结构体中的name是客户端想要私聊的用户
                        //将连接的新的通信套节字加入轮询
                        printf("(私聊!\n连接的客户端信息:%s:%d\n",inet_ntoa(clien.sin_addr),ntohs(clien.sin_port));//显示客户端的ip和端口 
                        // k;接收端
                        //count;发送端
                        while(1)
                        {   
                            memset(private_chat,0,sizeof(private_chat));
                            memset(private_keep,0,sizeof(private_keep));
                            int ret = recv(fd,private_keep,sizeof(private_keep),0);                        // 🍤第三次接收
                            sprintf(private_chat,"(私聊)");//在chat前面加上提示(私聊)
                            strcat(private_chat,private_keep);//把内容续接上
                            printf("接收到的消息是%s\n",private_keep);//接收的消息储存到keep里
                            send(zhanghao_shuzu[k].fd,private_chat,strlen(private_chat),0);   //发送私聊信息给私聊对象
                            printf("发送的私聊信息是:%s\n",private_chat);
                            if(ret==0)
                                break;
                        }
                        printf("私聊结束1\n");
                        pthread_cancel(id);
                        printf("私聊结束2\n");
                    }
                    
                }
                send(fd,"私聊建立失败!",sizeof(xuanze_buf),0);//发送在线的客户端的通信套节字的描述符
                memset(xuanze_buf,0,sizeof(xuanze_buf));
                recv(fd,xuanze_buf,sizeof(xuanze_buf),0);               //🍤第三次接收
                if(strcmp(xuanze_buf,"y")==0)
                    goto star;//重新输入
                else
                    return 0;//退出登陆
            
            }

        }
      
        //2.注册成功
        else if(ret==2)
        {
            printf("注册成功!\n");
            pthread_cancel(id);
        }
       
        //3.注销成功
        else if (ret == 3)
        {
            printf("注销成功!\n");
            pthread_cancel(id);
        }
       
        //4.管理员登陆
        else if (ret==4)
        {
            printf("+---------------------------------------+\n");
            printf("|                 登陆系统                |\n");
            printf("|1.禁言                                  |\n");//已完成
            printf("|2.解禁                                  |\n");//已完成
            printf("|3.提出群聊                               |\n");//已完成
            printf("+---------------------------------------+\n");//已完成       
mingdan:
            printf("**********count=%d\n\n",count);
            for(k=0;k<count;k++)//发送在线名单
            {
                usleep(10);
                send(fd,zhanghao_shuzu[k].name,strlen(zhanghao_shuzu[k].name),0);//发送在线的客户端的通信套节字的描述符
                printf("🍤zhanghao[%d].name=%s\n",k,zhanghao_shuzu[k].name);
                usleep(10);
            }   
            send(fd,"0",1,0);//发送0,以便让客户端知道发送结束                                               🍌第一次发送消息           
            printf("text2\n");



            memset(xuanze_buf,0,sizeof(xuanze_buf));
            recv(fd,xuanze_buf,sizeof(xuanze_buf),0);                                   //   🍌第二次接收消息(姓名)
            printf("接收到的姓名是:%s...\n",xuanze_buf);           //😡



            printf("请选择您要对其进行的操作\n");
            memset(sel,0,sizeof(sel));
            recv(fd,sel,sizeof(sel),0);                     //🍌第三次接收
            for(k=0;k<count;k++)//对比name
                if(strcmp(xuanze_buf,zhanghao_shuzu[k].name)==0)//对比传输的名字,一样的话进入if语句,此时,k下标的结构体是要禁言的用户
                    break;
            printf("要禁言的用户的fd是%d,id是:%s,name是%s\n",zhanghao_shuzu[k].fd,zhanghao_shuzu[k].id,zhanghao_shuzu[k].name);
            if (strcmp(sel,"1")==0)//禁言
            {   printf("禁言。。。。\n");
                zhanghao_shuzu[k].jinzhi=1;
                send(fd,"禁言成功!",20,0);                            //下面3个if是    🍌第二次发送消息
                usleep(10);
                goto mingdan;
            }
            else if (strcmp(sel,"2")==0)//解禁
            {
                zhanghao_shuzu[k].jinzhi=0;
                send(fd,"解禁成功!",20,0);  
                usleep(10);
                goto mingdan; 
            }
            else if (strcmp(sel,"3")==0)//提出群聊
            {
                close(zhanghao_shuzu[k].fd);//关闭对应name的通信套节字
                zhanghao_shuzu[k].onlin=0;//将online标识符设置为0,表示不再线
                send(fd,"踢出群聊成功!",20,0);   
                usleep(10);
                goto mingdan;   
            }
            else if (strcmp(sel,"4")==0)//退出管理系统 
            {
                return 0;
            }
        }
       
        //5.查看聊天记录成功
        else if (ret==4)
            {
                printf("查看聊天记录成功\n");
                
            }

        //6.退出登陆
        else if (ret==6)
        {
            printf("退出登陆!\n");
            pthread_cancel(id);
        }
        
        //0.失败
        else if (ret == 0)
        {
            printf("操作失败!\n");
            pthread_cancel(id);
        }
        
        return NULL;
}

服务器端包含的头文件server.h

#ifndef SERVER_H
#define SERVER_H
#include <stdio.h>
#include <sys/types.h>        
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include <sys/select.h>
#include <fcntl.h>
#include <pthread.h>

#include "denglu.h"

void * dengluxiancheng(void *);
void server(void);



#endif

账号系统denglu.c(在server.c里线程中被引用)

#include "denglu.h"



struct infor
{   
    char id[20];//承接账号
    char pw[20];//承接密码
    char name[20];//承接昵称
    int fd;       //承接通信套节字
    int index;    //承接轮训下标,即count的值
    int jinzhi;//承接禁止标识位
}zhanghao;


char denglu_buf[100];
char sel[2];
char client_ret[2];

int denglu(int denglu_fd)//传参是通信套节字描述符,后期用来私聊
{
 //账号系统
    char command[100]={0};//承接mysql的命令
    MYSQL * my = mysql_init(NULL);//初始化MYSQL结构体
    MYSQL_RES * res;//存放执行结果函数的返回值
    MYSQL_ROW row;//返回结果集的下一行的返回值
    int chat_num;//承接结果值的行数
    int ret;//ret是query返回值,choose是goto要用;
    int num;
    my = mysql_real_connect(my,"localhost","root",
    "1","wechat",0,NULL,0);//连接MYSQL服务器,在终端执行可执行程序时需要加sudo
    printf("my = %p\n",my);
 star:
    recv(denglu_fd,sel,sizeof(sel),0);//接收客户端发送的选择
    // printf("账号4!\n");
  
 

    //1.登陆
    if(strcmp(sel,"1")==0)
    {
denglu:
        //接收新连接客户端的消息
        num = recv(denglu_fd,&zhanghao,sizeof(zhanghao),0);//接收新加入的客户端输入的结构体信息
        if(num!=0)
            send(denglu_fd,"服务器接收成功!",sizeof(denglu_buf),0);                                                             
        else                                                    //发送第一条消息给客户端确认收到结构体  //🎏第一次发送
            send(denglu_fd,"服务器接收失败!",sizeof(denglu_buf),0);
        printf("id=%s\n",zhanghao.id);



        //判读id所对应的密码是否正确
        memset(command,0,sizeof(command));
        sprintf(command,"select pw from idandpw where id = '%s'",zhanghao.id);//从idandpw表中找出密码,条件是账号=输入的id,里面不需加
            printf("command = %s\n",command);
        ret = mysql_real_query(my,command,strlen(command));//指令执行
        res = mysql_store_result(my);//存放执行结果, 
        row = mysql_fetch_row(res);//返回结果集的下一行,
            
        if(row==0)//没有一个符合的
        {
            printf("登陆7!\n");
            send(denglu_fd,"您输入的账号不存在,按y重新输入,按任意按键退出登陆。",sizeof(denglu_buf),0);
            memset(client_ret,0,sizeof(client_ret));
            recv(denglu_fd,client_ret,sizeof(client_ret),0);                                            //🎏第一次接收
            if(strcmp(client_ret,"y")==0)
                goto star;//重新输入
            else
                return 0;//退出登陆
        }
        
        else if(strcmp(row[0],zhanghao.pw)==0) //输入的密码符合id
        {
            // printf("登陆8!\n");
            send(denglu_fd,"登陆成功!",sizeof(denglu_buf),0);//发送消息,返回
            printf("登陆成功\n");
            memset(command,0,sizeof(command));
            sprintf(command,"select name from idandpw where id = '%s'",zhanghao.id);//从idandpw表中找出姓名,条件是对应姓名
                printf("command = %s\n",command);
            ret = mysql_real_query(my,command,strlen(command));//指令执行
            res = mysql_store_result(my);//存放执行结果, 
            row = mysql_fetch_row(res);//返回结果集的下一行,
            printf("name= %s\n",*row);
            strcpy(zhanghao.name,*row);//把名字传给结构体,以便于服务器的结构体数组使用
            send(denglu_fd,*row,strlen(*row),0);//发送姓名,因为row是char*类型,所以用strlen统计一下name的字节大小
            
            if (strcmp(sel,"3")==0)
                goto zhuxiaozhanghao;   //注销账户用的,因为注销账户需要跑一遍登陆


            return 1;
        }
        else 
        {
            // printf("登陆9!\n");
            send(denglu_fd,"密码错误!",sizeof(denglu_buf),0);//发送消息
            return 0;
        }
    }

    //2.注册账号
    else if (strcmp(sel,"2")==0)
    {
        memset(zhanghao.id,0,sizeof(zhanghao.id));
        memset(zhanghao.pw,0,sizeof(zhanghao.pw));
        memset(zhanghao.name,0,sizeof(zhanghao.name));
        num = recv(denglu_fd,&zhanghao,sizeof(zhanghao),0);//接收新加入的客户端输入的结构体信息
        printf("id=%s,pw=%s,name=%s\n",zhanghao.id,zhanghao.pw,zhanghao.name);
        if(num!=0)
            send(denglu_fd,"服务器接收成功!",sizeof(denglu_buf),0);                                                             
        else                                                    //发送第一条消息给客户端确认收到结构体          第一次send
            send(denglu_fd,"服务器接收失败!",sizeof(denglu_buf),0);



        printf("id=%s\n",zhanghao.id);

        memset(command,0,sizeof(command));

        //id//
        //printf("请输入您要注册的账号id:%s",zhanghao.id);
        //scanf("%s",zhanghao.id);
        sprintf(command,"select id from idandpw");//表中id信息
        printf("command=%s\n",command);
        ret = mysql_real_query(my,command,strlen(command));//指令执行
            perror("mysql_real_query");
        res=mysql_store_result(my);//存放执行结果, 
            perror("mysql_store_result");
        int row_num = mysql_num_rows(res);                    
            perror("mysql_num_rows");
        printf("num===%d\n",row_num);
        for(int i=0;i<row_num;i++)//遍历数据库表中所有id
        {   

            printf("测试\n");   
            row=mysql_fetch_row(res);//返回结果集的下一行   
            printf("row=%s\n",row[0]);                                          //段错误                
            if(strcmp(row[0],zhanghao.id)==0)
            {
                send(denglu_fd,"此id已经注册!按y回到登陆界面,按任意按键退出登陆。",sizeof(denglu_buf),0);//发送选择y  第二次send
                memset(client_ret,0,sizeof(client_ret));
                recv(denglu_fd,client_ret,sizeof(client_ret),0);
                if(strcmp(client_ret,"y")==0)
                    goto star;//重新输入
                else
                    return 0;//退出登陆
                    
            }
        }
        send(denglu_fd,"id注册成功!",sizeof(denglu_buf),0);//                   注册id成功  第二次send
        
        
        //密码(密码无要求)//
        //printf("请输入您要注册账号的密码%s:",zhanghao.pw);
        //scanf("%s",zhanghao.pw);

        //昵称//
        //printf("请输入您要注册账号的昵称%s:",zhanghao.name);
        //scanf("%s",zhanghao.name);

        memset(command,0,sizeof(command));
        mysql_free_result(res);//释放
        sprintf(command,"select name from idandpw");//表中name信息
        printf("command=%s/n",command);
        ret = mysql_real_query(my,command,strlen(command));//指令执行
        res=mysql_store_result(my);//存放执行结果, 
        row_num=mysql_num_rows(res);
         printf("id=%s,pw=%s,name=%s\n",zhanghao.id,zhanghao.pw,zhanghao.name);   
        for(int i=0;i<row_num;i++)//遍历数据库中所有name
        {   
            printf("注册1\n");
            //printf("row_num=%d",row_num);
            row=mysql_fetch_row(res);//返回结果集的下一行
            if(strcmp(row[0],zhanghao.name)==0)
            {
                printf("row[%d]=%s,row_num=%d",i,row[i],row_num);
                printf("注册2\n");
                send(denglu_fd,"此name已经注册!按y回到登陆界面,按任意按键退出登陆。",sizeof(denglu_buf),0); //发送选择y  第三次send
                memset(client_ret,0,sizeof(client_ret));
                printf("注册2.1\n");
                recv(denglu_fd,client_ret,sizeof(client_ret),0);
                printf("注册2.2\n");
                if(strcmp(client_ret,"y")==0)
                    goto star;//重新输入
                else
                    return 0;//退出登陆
            }
        }
        send(denglu_fd,"昵称注册成功!",sizeof(denglu_buf),0);//                      注册昵称成功    第三次send

        //id,密码,name全部无重复,则将账号信息加入数据库中
        printf("id=%s,pw=%s,name=%s\n",zhanghao.id,zhanghao.pw,zhanghao.name);
        printf("注册3\n");
        memset(command,0,sizeof(command));
        printf("id=%s,pw=%s,name=%s\n",zhanghao.id,zhanghao.pw,zhanghao.name);
        sprintf(command,"insert into idandpw values(%s,%s,%s)",zhanghao.id,zhanghao.pw,zhanghao.name);//添加账号信息到数据库
        ret = mysql_real_query(my,command,strlen(command));//指令执行
        
        printf("注册4\n");
        send(denglu_fd,"注册成功!按y回到登陆界面,按任意按键退出登陆。",sizeof(denglu_buf),0); //发送选择y  //第四次send
        memset(client_ret,0,sizeof(client_ret));
        recv(denglu_fd,client_ret,sizeof(client_ret),0);
        if(strcmp(client_ret,"y")==0)
            goto star;//重新输入
        else
            return 2;//退出登陆
        
        
    }

    //3.注销账号信息(id,密码,昵称)
    else if (strcmp(sel,"3")==0)
    {
        goto denglu;
    zhuxiaozhanghao:   //相当于跑一边登陆
        printf("请问您真的要注销吗?,输入y确认,输入其他字符退出\n");
        memset(client_ret,0,sizeof(client_ret));
        recv(denglu_fd,client_ret,sizeof(client_ret),0);                                           //🍎第一次接收哦
        if(strcmp(client_ret,"y")==0)
        {
            memset(command,0,sizeof(command));
            sprintf(command,"delete from idandpw where id='%s'",zhanghao.id);//从idandpw表中找出姓名,条件是对应姓名
                printf("command = %s\n",command);
            ret = mysql_real_query(my,command,strlen(command));//指令执行
            if(ret==0)
            {   memset(denglu_buf,0,sizeof(denglu_buf));
                sprintf(denglu_buf,"删除id为%s的账户!",zhanghao.id);
                    printf("发送%s\n",denglu_buf);
                send(denglu_fd,denglu_buf,sizeof(denglu_buf),0);                                 //🍎第一次发送
                //接收到y返回登陆界面,其余退出程序
                memset(client_ret,0,sizeof(client_ret));
                recv(denglu_fd,client_ret,sizeof(client_ret),0);                                 //🍎第二次接收
                if(strcmp(client_ret,"y")==0)
                    goto star;//重新输入
                else
                    return 3;//退出登陆
            }
        }
        else 
            return 0;

    }
    
    //4.4.管理员登陆(禁言,解禁,提出群聊)
    else if (strcmp(sel,"4")==0)
    {
        memset(denglu_buf,0,sizeof(denglu_buf));
        recv(denglu_fd,denglu_buf,sizeof(denglu_buf),0);                //🍌第一次接收
        if(strcmp(denglu_buf,"管理员登陆成功!")==0)
            return 4;//回到server操作
    }

    //5.查看聊天记录
    else if (strcmp(sel,"5")==0)
    {
        memset(command,0,sizeof(command));
        sprintf(command,"select * from chat_date");//从数据库选择聊天数据
        printf("command = %s\n",command);
        ret = mysql_real_query(my,command,strlen(command));//指令执行
        res = mysql_store_result(my);//存放执行结果, 
        chat_num=mysql_num_rows(res);//返回结果集的行数
        for(int k=0;k<chat_num;k++)
        {
            // printf("test1\n");
            row = mysql_fetch_row(res);//返回结果集的下一行,
            memset(command,0,sizeof(command));
            sprintf(command,"|第%d记录|_%s",k,row[0]);    //把聊天记录接到字符传串里
            //printf("%s\n",command);
            send(denglu_fd,command,sizeof(denglu_buf),0);//        🕒第一次发送聊天记录
            if(chat_num>10000);     //如果消息超过10000行,重新清空表格chat_date,即设置历史聊天记录的上限
            
        }
        send(denglu_fd,"0",sizeof(denglu_buf),0);         //给客户端发送0来确认发送结束 
        memset(client_ret,0,sizeof(client_ret));
        recv(denglu_fd,client_ret,sizeof(client_ret),0);
        if(strcmp(client_ret,"y")==0)
            goto star;//重新输入
        else
            return 5;//退出
        return 5;
    }

    //6.退出登陆 
    else if (strcmp(sel,"6")==0)
        return 6;//退出


    //1代表登陆成功,2代表注册成功,3,注销成功,4,管理员登陆,5,查看聊天记录成功,6,退出登陆,0代表失败

    return 0;
}

账号系统denglu.h

#ifndef DENGLU_H
#define DENGLU_H


#include <mysql/mysql.h>
#include <stdio.h>
#include <string.h>
#include <sys/types.h>        
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <sys/select.h>
#include <fcntl.h>
#include <pthread.h>
#include <time.h>

int denglu(int denglu_fd);


#endif

客户端client.c

#include <stdio.h>
#include <sys/types.h>        
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include <pthread.h>
#include <sys/types.h>   
#include <sys/socket.h>
#include <time.h>


#define IP "192.168.134.10"
#define PORT 6667
void *ptrhead_read(void *arg);

int client_fd;
struct infor
{   char id[20];//承接账号
    char pw[20];//承接密码
    char name[20];//承接昵称
    int fd;       //承接通信套节字
    int index;    //承接轮训下标,即count的值
    int jinzhi;//承接禁止标识位
}zhanghao;

char buf[100];//承接服务器发送的语句
char sel[2];//选择

int main()
{
    char guanli_id[20]={0},guanli_pw[20]={0};

    int ret,choose,k;
    client_fd = socket(AF_INET,SOCK_STREAM,0);
    struct sockaddr_in ser_addr;//此结构体存放服务器信息
    ser_addr.sin_family = AF_INET;
    ser_addr.sin_port = htons(PORT);//服务端口
    ser_addr.sin_addr.s_addr=inet_addr(IP);//服务器IP
    ret = connect(client_fd,(struct sockaddr *)&ser_addr,sizeof(ser_addr));//要连接的服务器地址 
    if(ret == -1)
    {
        perror("connect");
        return -1;
    }


    //连接成功,进入登陆系统
star:
    printf("+---------------------------------------+\n");
    printf("|                 登陆系统               |\n");
    printf("|1.登陆账号                              |\n");//已完成
    printf("|2.注册账号                              |\n");//已完成
    printf("|3.注销账号信息(id,密码,昵称)              |\n");//已完成
    printf("|4.管理员登陆(禁言,解禁,提出群聊)           |\n");//已完成
    printf("|5.查看聊天记录(100条)                    |\n");//已完成
    printf("|6.退出登陆                              |\n");//已完成
    printf("+---------------------------------------+\n");//已完成
    scanf("%s",sel);
    send(client_fd,&sel,strlen(sel),0);//发送一个选择给服务器的登陆系统
    //1.登陆账号
    if(strcmp(sel,"1")==0)
    {
denglu:
    	printf("请输入账号id:");//发送账号信息,对账号信息作初步的填写传输
    	scanf("%s",zhanghao.id);
        printf("请输入账号密码:");
    	scanf("%s",zhanghao.pw);
        send(client_fd,&zhanghao,sizeof(zhanghao),0);//把结构体发送给服务器端的账号系统
        memset(buf,0,100);
        recv(client_fd,buf,sizeof(buf),0);//根据返回内容判断是否接收成功,阻塞		🎏第一次接收
        if(strcmp(buf,"服务器接收失败!")==0)//退出
        {
            printf("出错!服务器接收失败!buf=%s\n",buf);//goto一下
                return 0;
        }
        else if (strcmp(buf,"服务器接收成功!")==0)
            printf("服务器接收成功!buf=(%s)\n\n\n",buf);
    	
        memset(buf,0,100);
        recv(client_fd,buf,sizeof(buf),0);//接收登陆命令								🎏第二次接收
        if(strcmp(buf,"您输入的账号不存在,按y重新输入,按任意按键退出登陆。")==0)
        {
            printf("您输入的账号不存在,按y重新输入,按任意按键退出。\n");
            memset(buf,0,100);
            scanf("%s",buf);
            send(client_fd,buf,strlen(buf),0);
            if(strcmp(buf,"y")==0)
                goto star;
            else
                return 0;
        }

        else if(strcmp(buf,"登陆成功!")==0)//如果登陆成功,跳出循环,进入收消息的循环
        {    
            
            
        	memset(buf,0,100);
        	recv(client_fd,buf,sizeof(buf),0);//接收name
        	strcpy(zhanghao.name,buf);//将name写入结构体中
        	strcat(zhanghao.name,":");//在name后面写上':'
        	printf("name=%s\n",buf);

           if (strcmp(sel,"3")==0)
                goto zhuxiaozhanghao;       //注销账户用的,因为注销账户需要跑一遍登陆

        	printf("*********************\n");
            printf("*      登陆成功!     *\n");
            printf("*1.加入群聊          *\n");
            printf("*2.进行私聊          *\n");
            printf("*********************\n");
            scanf("%s",sel);
            send(client_fd,&sel,strlen(sel),0);//发送一个选择给服务器的服务系统子线程       🍤第一次发送,选择群聊或者私聊
            if(strcmp(sel,"1")==0)//直接goto到收发消息
         	    goto chat;
            else if(strcmp(sel,"2")==0)//选择私聊
            {   
                printf("在线名单:\n");
                for(k=0;;k++)//接收名单
                {
                    memset(buf,0,100);
                    recv(client_fd,buf,sizeof(buf),0);//根据返回内容判断是否接收成功,阻塞		🍤第一次接收
                    if(strcmp(buf,"0")==0)    
                    {    
                        printf("接收名单结束,共%d个在线人员\n",k);
            private_chat:      
                        printf("请输入您想要私聊的人的name:\n");
                        memset(buf,0,100);
                        scanf("%s",buf);
                        send(client_fd,buf,strlen(buf),0);//发送想要私聊的对象    🍤第二次发送
                        printf("您输入的name是:%s\n",buf);
                        recv(client_fd,buf,sizeof(buf),0);//根据返回内容判断是否接收成功,阻塞		🍤第二次接收
                        printf("第二次接收的信息是:%s\n",buf);
                        if(strcmp(buf,"私聊建立成功!")==0)   
                        {
                            printf("**私聊建立成功!**\n");
                            goto chat;
                        }
                        else if(strcmp(buf,"私聊建立失败!")==0)
                        {
                            printf("**私聊建立失败!**输入y重新私聊,输入其他按键退出\n");
                            memset(buf,0,100);
                            scanf("%s",buf);
                            send(client_fd,buf,strlen(buf),0);                            //🍤第三次发送   ,或者chat        
                            if(strcmp(buf,"y")==0)
                                goto private_chat;
                            else
                                return 0;
                        }
                    }
                        printf("第%d个的昵称是:%s\n",k+1,buf);

                }

        }   
    	else if(strcmp(buf,"密码错误!")==0)
    	{
    		printf("密码错误!退出");
    		return 0;
    	}	
	}
    }
    
    //2.注册账号
    else if(strcmp(sel,"2")==0)
    {
        printf("请输入您要注册的的账号id:");
        scanf("%s",zhanghao.id);
        printf("请输入您要注册的的账号密码:");
        scanf("%s",zhanghao.pw);
        printf("请输入您要注册的的账号name:");
        scanf("%s",zhanghao.name);
        send(client_fd,&zhanghao,sizeof(zhanghao),0);//把结构体发送给服务器端的账号系统

        memset(buf,0,100);
        recv(client_fd,buf,sizeof(buf),0);//根据返回内容判断是否接收成功,阻塞		😀第一次接收
        if(strcmp(buf,"服务器接收失败!")==0)//退出
        {
            printf("出错!服务器接收失败!buf=%s\n",buf);
                return 0;
        }
        else if (strcmp(buf,"服务器接收成功!")==0)
            printf("服务器接收成功!buf=(%s)\n\n\n",buf);

        memset(buf,0,100);
        recv(client_fd,buf,sizeof(buf),0);//根据返回内容判断是否接收成功,阻塞	    😀第二次接收
        if(strcmp(buf,"此id已经注册!按y回到登陆界面,按任意按键退出登陆。")==0)
        {
            printf("此id已经注册!按y回到登陆界面,按任意按键退出登陆。\n");
            memset(buf,0,100);
            scanf("%s",buf);
            send(client_fd,buf,strlen(buf),0);
            if(strcmp(buf,"y")==0)
                goto star;
            else
                return 0;
        }
        else if (strcmp(buf,"id注册成功!")==0)
            printf("id注册成功!buf=(%s)\n\n",buf);
        
        memset(buf,0,100);
        recv(client_fd,buf,sizeof(buf),0);//根据返回内容判断是否接收成功,阻塞	    😀第三次接收
        if(strcmp(buf,"此name已经注册!按y回到登陆界面,按任意按键退出登陆。")==0)     
        {
            printf("此name已经注册!按y回到登陆界面,按任意按键退出登陆。");
            memset(buf,0,100);
            scanf("%s",buf);
            send(client_fd,buf,strlen(buf),0);
            if(strcmp(buf,"y")==0)
                goto star;
            else
                return 0;
        }
        else if (strcmp(buf,"昵称注册成功!")==0)
            printf("昵称注册成功!buf=(%s)\n\n\n",buf);
        
        memset(buf,0,100);
        recv(client_fd,buf,sizeof(buf),0);//根据返回内容判断是否接收成功,阻塞	    😀第四次接收
        if(strcmp(buf,"注册成功!按y回到登陆界面,按任意按键退出登陆。")==0)     
        {
            printf("注册成功!按y回到登陆界面,按任意按键退出登陆。\n");
            memset(buf,0,100);
            scanf("%s",buf);
            send(client_fd,buf,strlen(buf),0);
            if(strcmp(buf,"y")==0)
                goto star;
            else
                return 0;
        }

        
        


    }


    //3.注销账号信息(id,密码,昵称)
    else if (strcmp(sel,"3")==0)
    {
        goto denglu;
        zhuxiaozhanghao:        //相当于跑一边登陆
        printf("请问您真的要注销吗?,输入y确认,输入其他字符退出\n");
        memset(buf,0,100);
        scanf("%s",buf);
        send(client_fd,buf,strlen(buf),0);                          //🍎第一次发送
        if(strcmp(buf,"y")==0)
            printf("正在注销id为%s的账号!\n",zhanghao.id);//客户端开始删除操作
        else    
            return 0;
        memset(buf,0,100);
        recv(client_fd,buf,sizeof(buf),0);                           //🍎第一次接收
        printf("%s\n",buf);

            printf("注册成功!按y回到登陆界面,按任意按键退出登陆。\n");
            memset(buf,0,100);
            scanf("%s",buf);
            send(client_fd,buf,strlen(buf),0);                        //🍎第二次发送
            if(strcmp(buf,"y")==0)
                goto star;
            else
                return 0;
    }


    //4.管理员登陆
    else if (strcmp(sel,"4")==0)
    {
        memset(guanli_id,0,sizeof(guanli_id));
        printf("请输入管理员id:");
        scanf("%s",guanli_id);
        if(strcmp(guanli_id,"yao")==0)
            printf("管理员id正确\n");
        else
        {   
            printf("管理员id错误,即将退出\n");
            sleep(1);
            return 0;
        }

        memset(guanli_pw,0,sizeof(guanli_pw));
        printf("请输入管理员pw:");
        scanf("%s",guanli_pw);
        if(strcmp(guanli_pw,"yao")==0)
            printf("管理员pw正确\n");
        else
        {   
            printf("管理员id错误,即将退出\n");
            sleep(1);
            return 0;
        }

        send(client_fd,"管理员登陆成功!",sizeof(buf),0);                        //🍌第一次发送
mingdan:    
        printf("在线名单:\n");
        for(k=0;;k++)//接收名单
        {
            memset(buf,0,100);
            recv(client_fd,buf,sizeof(buf),0);//根据返回内容判断是否接收成功,阻塞	🍌第一次接收(名单)
            if(strcmp(buf,"0")==0)    
            {    
                printf("接收名单结束,共%d个在线人员\n",k);
                break;
            }
            printf("第%d个在线用户:%s\n",k+1,buf);

        }

        printf("请输入您想要进行从操作的用户的name\n");                 /😡
        memset(buf,0,100);
        scanf("%s",buf);
        printf("您输入的名字是:%s\n",buf);
        send(client_fd,buf,strlen(buf),0);//发送想要私聊的对象    🍌第二次发送(姓名)  

        printf("+---------------------------------------+\n");
        printf("|                 管理系统                |\n");
        printf("|1.禁言                                  |\n");
        printf("|2.解禁                                  |\n");
        printf("|3.提出群聊                               |\n");
        printf("+---------------------------------------+\n");      
        printf("请选择您要对其进行的操作\n");
        memset(sel,0,sizeof(sel));
        scanf("%s",sel);
        send(client_fd,&sel,strlen(sel),0);//发送一个选择给服务器的登陆系统         //🍌第三次发送
        
        if (strcmp(sel,"1")==0) //3个选择🍌第四次接收
        {
            memset(buf,0,100);
            recv(client_fd,buf,sizeof(buf),0);
            printf("禁言=%s\n",buf);
            if(strcmp(buf,"禁言成功!")==0)  
            {  
                printf("禁言成功!即将返回管理系统\n");
                goto mingdan;           
            }    
        }
        else if (strcmp(sel,"2")==0)
        {
            memset(buf,0,100);
            recv(client_fd,buf,sizeof(buf),0);
            if(strcmp(buf,"解禁成功!"))  
            {  
                printf("解禁成功!即将返回管理系统\n");
                goto mingdan;           
            }     
        }
        else if (strcmp(sel,"3")==0)
        {
            memset(buf,0,100);
            recv(client_fd,buf,sizeof(buf),0);
            if(strcmp(buf,"踢出群聊成功!"))  
            {  
                printf("踢出群聊成功!即将返回管理系统\n");
                goto mingdan;           
            }     
        }
        else if (strcmp(sel,"4")==0)//退出管理系统
        {
            return 0;
        }
        

        return 0;
    }

    //5.查看聊天记录
    else if (strcmp(sel,"5")==0)
    {
        printf("********查询历史记录开始********\n");
        while(1)
        {    memset(buf,0,100);
            ret=recv(client_fd,buf,sizeof(buf),0);         //🕒第一次接收数据
            printf("%s\n",buf);
            if(strcmp(buf,"0")==0)
            {
                printf("********查询历史激励完成********\n");
                printf("**按y回到菜单,按任意按键退出。***\n");
                memset(buf,0,100);
                scanf("%s",buf);
                send(client_fd,buf,strlen(buf),0);
                if(strcmp(buf,"y")==0)
                    goto star;
                else
                    return 0;
            } 
        }
    }                      

    //6.退出登陆 
    else if (strcmp(sel,"6")==0)
    {   
         return 0;
    }

    //else.输入其他提示错误,退出
    else
    {
            printf("输入错误,即将退出!\n");
        return 0;
    }

    //从服务器端收信息
    chat:
    {
        printf("进入聊天\n");
        //避免并法,创建线程,做到收发“同时进行”
        pthread_t id;
        pthread_create(&id,NULL,ptrhead_read,NULL);
        while(1)//此循环是收信息,,//后期区分群聊和私聊
        {
            memset(buf,0,100);
            ret=recv(client_fd,buf,sizeof(buf),0);
            if(ret==0)
            {
                printf("服务器已断开\n");
                return 0;
            }
            printf("%s\n",buf);

        
        }
	
    }

    return 0;
 
}
	
    //发信息到服务器端  
void *ptrhead_read(void *arg)
{
		time_t t;
		char time_date[50];//时间信息
        char date[80];
     	while(1)
        {
            memset(buf,0,sizeof(buf));
            strcpy(buf,zhanghao.name);//将name:复制到strcpy中
            scanf("%s",date);//承接输入的消息
            strcat(buf,date);			//将信息接到name之后
            getchar();
       	    t = time(NULL);	//获取秒数值
       	    struct tm * now = localtime(&t);//转换为本地时间
       	    strftime(time_date,sizeof(time_date),"\t%Y-%m-%d %H:%M:%S",now);//设置格式化字符串
            strcat(buf,time_date);			//将时间接到信息之后
            send(client_fd,buf,strlen(buf),0);
            perror("send");
        }
}
    



      

                                
  • 14
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
客户端,采用多线程。一个接收服务器消息,一个发送消息给服务器服务器,采用select()进行IO复用。 编译文件是Makefile。 (1)用户登录: 【1】client端接收用户名和密码->存于结构体中->将结构体发送给server端。 【2】server端接收client发送的结构体->打开存储用户名密码的文件->文件写入链表中->遍历链表验证用户信息。 【3】server端验证正确发送“登陆成功”消息,错误发回“登陆失败”消息。client端接收,“登陆成功”则进入聊天,“登陆失败”则跳出。 【4】若验证成功,server端产生一个新的套接字newfd,将它与用户名封装于同一个结构体中,存储在线用户的信息。 消息、存储在线用户信息结构体: typedef struct message { int type; //服务器用于判断该执行的功能 int fd; int mode; //标志位,表示用户的发言权限,1为正常,0为禁言 char name[NAMELEN]; char mima[NAMELEN]; char from[20]; char to[20]; //聊天时的收信人 char file_name[20]; //发送文件时的文件名 char mtext[100]; //聊天时发送的消息内容 struct message *next; }Mess; (2)一对多聊天: 【1】client端发送欲发送的信息给server端。 【2】server端遍历在线人信息链表,找到每个在线人的套接字描述符,将消息发送给链表中的每个人。 【3】可以通过输入“:)”, “:(”, “bye”来发送笑脸,悲伤脸和退出聊天;检测敏感词汇“fuck”、“shit”,禁止发送。 (3)一对一聊天: 【1】client端发送欲发送的信息和信息的接收者给server端。 【2】server端根据收到的接收者名字在在线人链表中查找该接收者的套接字描述符,找到后就将消息发送给该接收者。 【3】可以通过输入“:)”, “:(”, “bye”来发送笑脸,悲伤脸和退出聊天;检测敏感词汇“fuck”、“shit”,禁止发送。 (4)文件传输 【1】client端发送预发送的文件名和接收者名字到server端。 先打开(不存在则创建)一个文件,将文件内容读到缓冲区buffer,再将buffer的内容复制到结构体Mess中,最后将结构体发送给server端。 【2】server端先将接收到的文件重命名(因为相同文件目录下不能存在同名文件),再将收到的文件和新的文件名一同放入tab1中(并且在tab1开头写“#”)发送给client端。 【3】当client端收到以“#”开头的消息,执行文件接收,先创建一个文件,再写入相应内容。 (5)管理员模式 【1】禁言 【2】解禁
### 回答1: QT局域网聊天室UDP是一种使用QT编程语言开发的局域网聊天室,其通信协议采用UDP协议。 为了实现局域网聊天室的功能,首先需要建立一个服务器和多个客户端之间的通信连接。在QT中,可以使用QUdpSocket类实现UDP的网络通信功能。服务器端首先需要创建一个QUdpSocket对象来监听指定端口,以便接收来自客户端的消息。然后,服务器需要使用bind函数将QUdpSocket对象绑定到一个指定的IP地址和端口号上。 客户端在连接服务器之前,需要先创建一个QUdpSocket对象用于发送和接收消息。客户端可以使用writeDatagram函数将消息发送给服务器端,并使用bind函数将QUdpSocket对象绑定到一个指定的端口号上,以便接收来自服务器端的回复消息。 服务器端在接收到客户端发来的消息后,可以使用readDatagram函数获取消息内容,并在回复消息时使用writeDatagram函数将回复信息发送给客户端。 在QT局域网聊天室UDP中,可以通过显示聊天内容的GUI界面来实现用户之间的实时聊天。用户可以通过输入框输入自己要发送的消息,点击发送按钮后,消息将通过 UDP 协议发送给服务器端,然后服务器转发给其他在线用户。同时,服务器端接收其他用户的消息并转发给当前用户,用户可以在GUI界面看到其他用户的消息内容。 总之,QT局域网聊天室UDP通过使用UDP协议和QT编程语言的特性,实现了简单的局域网聊天功能。用户可以在局域网内实时交流信息,具有便捷、高效的特点。 ### 回答2: QT局域网聊天室是一种基于User Datagram Protocol(UDP)的网络聊天应用程序。UDP是一种无连接的协议,它带有较少的开销,使其成为实现快速通信的理想选择。 QT局域网聊天室通过创建一个UDP服务器来提供聊天服务。在局域网中的每台计算机上都可以运行一个客户端应用程序,与服务器进行通信。客户端应用程序可以发送和接收消息,实现用户之间的即时聊天。 在QT局域网聊天室中,用户可以输入要发送的消息,并且可以选择向特定的用户发送消息,也可以发送给所有在线用户。使用UDP协议的优点之一是可以轻松地广播消息给所有用户,提高了用户之间信息交流的效率。 UDP协议的主要特点之一是它的开销较低,并且不保证消息的可靠性。这意味着在传输过程中,消息可能会丢失或顺序混乱。但是,对于局域网聊天室这种要求快速通信的应用程序来说,这个问题并不重要。即使消息丢失或顺序不正确,用户还是能获取大部分信息,很少会对用户体验产生重大影响。 总结来说,QT局域网聊天室使用UDP协议提供了一种快速和高效的局域网通信方式。用户可以通过发送和接收消息进行实时聊天,并且可以广播给所有用户或特定用户。尽管UDP不保证消息的可靠性,但在这种应用程序中通常不会对用户造成重大影响。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值