• 售前

  • 售后

热门帖子
入门百科

基于(LinuxC语言)的udp局域网聊天室

[复制链接]
无缘人1 显示全部楼层 发表于 2022-1-13 02:37:21 |阅读模式 打印 上一主题 下一主题
【使用说明与相关缺陷】
    //1.关于两个文件夹的说明:(这部分是源文件压缩包的说明,这边没发所以可以忽略 )
      文件夹 udp_聊天室 的内容是本次项目的内容.
      
      文件夹 tcp_select 的内容是基于tcp和select写的服务器转发代码.
      
      udp_聊天室的代码是通过tcp_select的代码修改和添加功能得到的.
      
      
    2.在使用udp_聊天室时需要注意:
        1)server端需要用命令行传参的形式输入端口号.
        
        2)client端需要用命令行传参的形式输入服务器的ip地址和端口号.
        
        3)本聊天室仅限于同一个网段内的通信.
        
        
    3.主要功能:
        1)用户名不能重复,如果重复会提示重新输入,用户登录有提示.
        
        2)服务器转发客户端发来的消息给其他客户端.
        
        3)当前客户端输入quit可以退出自己,并且服务器转发退出提示.
        
        4)火力覆盖:服务器输入quit可以退出所有客户端并关闭自己.
        
        5)定点打击:服务器输入quit 用户名 可以指定让某一个用户退出.
        
        6)服务器会转发自己从终端输入的内容给所有客户端.
        
        7)有两个非法用户名(客户端输入会提示重新输入)一个是 name 一个是server
        name:是用户名关键字.
        server:是服务器的用户名.
        
        8)客户端使用Ctr+C强制退出也会被捕捉,之后进行正常的退出流程.
        
        
    4.小缺陷:
        1)在输入和输出的时候,如果输入一半,收到消息的话,会优先打印收到的内容.
           这会导致在多人聊天的时候,输入内容不够明确的问题.
           (目前没想到啥好办法,好像这是终端的缺陷)
           
        2)发送消息时,正式消息前的前缀太长,占用不必要的空间.
           解决方法(可以用一个字符作为通信前缀,但是代码已经成型修改起来太过耗时)
        
        3)目前用户名的可用长度为15个字节,比较短且还有两个非法用户名.
           解决方法(可以增加用户名所占的字节数,但是会增加占用的空间)
        
        4)如果服务器运行前还有残留的客户端没有退出的话,可能会导致服务器端崩溃
           当这个不受管制的客户端退出时会给服务器发送客户端退出消息,但是服务器的
           用户链表内并没有该用户,所以会访问非法空间导致段错误
           解决方法(在每次服务器退出的时候使用quit使所有客户端退出)
           
        5)服务器端使用的是select实现的多路复用,代码量与进程实现相比要大,并且不是
           特别好理解,但是不需要考虑父子进程之间通信的问题。
           
        6)客户端使用的虽然是进程实现多路复用,但是代码量也一百多行,而且由于信号和
           父子进程之间退出等问题,添加了很多不好理解的代码,降低了代码的易读性。

server(客户端代码)(注释不多。。。。。。因为在下懒)
  1. #include<stdio.h>
  2. #include<string.h>
  3. #include <sys/types.h>
  4. #include <sys/socket.h>
  5. #include <netinet/in.h>
  6. #include <netinet/ip.h>
  7. #include <sys/select.h>
  8. #include <sys/time.h>
  9. #include <unistd.h>
  10. #include <stdlib.h>
  11. typedef struct node_t
  12. {
  13.         char name[16];
  14.         struct sockaddr_in linkclient;
  15.         struct node_t *next;
  16. }linklist;
  17. //建立有头链表的头
  18. linklist *create();
  19. //添加内容
  20. int add(linklist *p,char *name,struct sockaddr_in linkclient);
  21. //删除指定的数据
  22. int del_post(linklist *p,char *name);
  23. //查看重复
  24. int look_name(linklist *p,char *name);
  25. //根据用户名获取保存ip的结构体
  26. struct sockaddr_in *name_struct(linklist *p,char *name);
  27. //转发消息
  28. void show(linklist *p,int sockfd,char *name,char *buf);
  29. int main(int argc, const char *argv[])
  30. {
  31.     if(argc!=2)
  32.     {
  33.         printf("请输入端口号\n");
  34.         return -1;
  35.     }
  36.         int quitserver=0;
  37.         linklist *p=create();
  38.         if(p==NULL)
  39.         {
  40.                 perror("linklist create err.");
  41.                 return -1;
  42.         }
  43.         int sockfd;
  44.         sockfd=socket(AF_INET,SOCK_DGRAM,0);
  45.         if(sockfd<0)
  46.         {
  47.                 perror("socket err.");
  48.                 return -1;
  49.         }
  50.         struct sockaddr_in serveraddr;
  51.         serveraddr.sin_family=AF_INET;
  52.         serveraddr.sin_port=htons(atoi(argv[1]));
  53.         serveraddr.sin_addr.s_addr=htonl(INADDR_ANY);
  54.         struct sockaddr_in clientaddr;
  55.         socklen_t len=sizeof(clientaddr);
  56.         if(bind(sockfd,(struct sockaddr *)&serveraddr,sizeof(serveraddr))<0)
  57.         {
  58.                 perror("bind err.");
  59.                 return -1;
  60.         }
  61.         fd_set readfds,tmpfds;
  62.         FD_ZERO(&readfds);
  63.         FD_SET(0,&readfds);
  64.         FD_SET(sockfd,&readfds);
  65.         char buf[128]="",name[16]="",tmpbuf[128]="";
  66.         int ch,maxfd,i,recvch,j;
  67.         maxfd=sockfd;
  68.         pid_t pid;
  69.         while(1)
  70.         {
  71.                 tmpfds=readfds;
  72.                 ch=select(maxfd+1,&tmpfds,NULL,NULL,NULL);
  73.                 if(ch<0)
  74.                 {
  75.                         perror("select err.");
  76.                         return -1;
  77.                 }
  78.                 for(i=0;i<=maxfd;i++)
  79.                 {
  80.                         if(FD_ISSET(i,&tmpfds))
  81.                         {
  82.                                 if(i==0)
  83.                                 {
  84.                                         memset(buf,0,sizeof(buf));
  85.                                         strcpy(buf,"server说: ");
  86.                                         fgets(buf+11,128,stdin);
  87.                                         if(buf[strlen(buf)-1]=='\n')
  88.                                                 buf[strlen(buf)-1]='\0';
  89.                                         printf("%s\n",buf);
  90.                                         if(!strncmp(buf+11,"quit",4))
  91.                                         {
  92.                                                 if(!strcmp(buf+11,"quit"))
  93.                                                 {
  94.                                                         printf("强制退出所有客户端和本服务器\n");
  95.                                                         show(p,sockfd,"name",buf);
  96.                                                         quitserver=1;
  97.                                                         break;
  98.                                                 }
  99.                                                 else
  100.                                                 {
  101.                                                         sendto(sockfd,buf,128,0,(struct sockaddr *)name_struct(p,buf+11+5),len);
  102.                                                 }
  103.                                         }
  104.                                         show(p,sockfd,"name",buf);
  105.                                 }
  106.                                 else if(i==sockfd)
  107.                                 {
  108.                                         recvch=recvfrom(sockfd,buf,128,0,(struct sockaddr *)&clientaddr,&len);
  109.                                         if(recvch<0)
  110.                                         {
  111.                                                 perror("recv err.");
  112.                                                 return -1;
  113.                                         }
  114.                                         else
  115.                                         {
  116.                                                 if(!strncmp(buf,"name",4))
  117.                                                 {
  118.                                                         if(look_name(p,buf+4))
  119.                                                                 sendto(sockfd,"quit",4,0,(struct sockaddr *)&clientaddr,len);
  120.                                                         else
  121.                                                         {
  122.                                                                 add(p,buf+4,clientaddr);
  123.                                                                 strcpy(tmpbuf,"用户 ");
  124.                                                                 strcat(tmpbuf,buf+4);
  125.                                                                 strcat(tmpbuf," 登录。");
  126.                                                                 printf("%s\n",tmpbuf);
  127.                                                                 show(p,sockfd,buf+4,tmpbuf);
  128.                                                                 sendto(sockfd,"ok..",4,0,(struct sockaddr *)&clientaddr,len);
  129.                                                         }
  130.                                                 }
  131.                                                 else if(!strcmp(buf+16+strlen("说: ")+strlen(buf),"quit"))
  132.                                                 {
  133.                                                         strcpy(tmpbuf,"用户 ");
  134.                                                         strcat(tmpbuf,buf);
  135.                                                         strcat(tmpbuf," 退出。");
  136.                                                         printf("%s\n",tmpbuf);
  137.                                                         show(p,sockfd,buf,tmpbuf);
  138.                                                         del_post(p,buf);
  139.                                                 }
  140.                                                 else
  141.                                                 {
  142.                                                         strncpy(name,buf,16);
  143.                                                         printf("buf:%s\n",buf+16);
  144.                                                         show(p,sockfd,name,buf+16);
  145.                                                 }
  146.                                         }
  147.                                 }
  148.                         }
  149.                 }
  150.                 if(quitserver==1)
  151.                         break;
  152.         }
  153.         close(sockfd);
  154.         return 0;
  155. }
  156. //建立有头链表的头
  157. linklist *create()
  158. {
  159.         linklist *p=(linklist *)malloc(sizeof(linklist));
  160.         if(NULL==p)
  161.         {
  162.                 perror("malloc err.");
  163.                 return NULL;
  164.         }
  165.         p->next=NULL;
  166.         return p;
  167. }
  168. //添加内容
  169. int add(linklist *p,char *name,struct sockaddr_in linkclient)
  170. {
  171.         linklist *pnew=(linklist *)malloc(sizeof(linklist));
  172.         if(NULL==pnew)
  173.         {
  174.                 perror("malloc err.");
  175.                 return -1;
  176.         }
  177.         while(p->next!=NULL)
  178.                 p=p->next;
  179.         pnew->next=NULL;
  180.         strcpy(pnew->name,name);
  181.         pnew->linkclient=linkclient;
  182.         p->next=pnew;
  183.         return 0;
  184. }
  185. //删除指定的数据
  186. int del_post(linklist *p,char *name)
  187. {
  188.         while(p->next!=NULL)
  189.         {
  190.                 if(!strcmp(p->next->name,name))
  191.                         break;
  192.                 p=p->next;
  193.         }
  194.         linklist *del=p->next;
  195.         p->next=del->next;
  196.         free(del);
  197.         del=NULL;
  198.         return 0;
  199. }
  200. //查看重复
  201. int look_name(linklist *p,char *name)
  202. {
  203.         while(p->next!=NULL)
  204.         {
  205.                 if(!strcmp(p->next->name,name))
  206.                 {
  207.                         printf("用户名相同\n");
  208.                         return 1;
  209.                 }
  210.                 p=p->next;
  211.         }
  212.         printf("用户名不同\n");
  213.         return 0;
  214. }
  215. //根据文件描述符获取用户名(这段没用)
  216. /*char *acceptfd_name(linklist *p,int linkacceptfd)
  217.   {
  218.   while(p->next!=NULL)
  219.   {
  220.   if(p->next->linkacceptfd==linkacceptfd)
  221.   break;
  222.   p=p->next;
  223.   }
  224.   return p->next->name;
  225.   }*/
  226. //根据用户名获取保存ip的结构体
  227. struct sockaddr_in *name_struct(linklist *p,char *name)
  228. {
  229.         while(p->next!=NULL)
  230.         {
  231.                 p=p->next;
  232.                 if(!strcmp(p->name,name))
  233.                         return &(p->linkclient);
  234.         }
  235.         printf("查无此用户\n");
  236.         return NULL;
  237. }
  238. //转发消息
  239. void show(linklist *p,int sockfd,char *name,char *buf)
  240. {
  241.         while(p->next!=NULL)
  242.         {
  243.                 p=p->next;
  244.                 if(!strcmp(p->name,name))
  245.                         continue;
  246.                 sendto(sockfd,buf,128,0,(struct sockaddr *)&(p->linkclient),sizeof(p->linkclient));
  247.         }
  248. }
复制代码
client(客户端代码)    (没注释,因为在下懒............)
[code]#include#include#include                #include               #include               #include               #include               #include                 #include                #include                   #include #include #include #include pid_t qpid;struct sockaddr_in qserveraddr;int sockqfd;char NNbuf[128]="";void handler(int sig){        printf("已经退出\n");        kill(qpid,SIGQUIT);        wait(NULL);        sendto(sockqfd,NNbuf,128,0,(struct sockaddr *)&qserveraddr,sizeof(qserveraddr));        exit(0);}int main(int argc, const char *argv[]){    if(argc!=3)    {        printf("请输入服务器端的ip地址和端口号\n");        return -1;    }        int sockfd;        char name[16]="";        char Nbuf[128]="";        sockfd=socket(AF_INET,SOCK_DGRAM,0);        if(sockfd=15)                {                        printf("名字需要低于长度十五个字节\n");                        fgets(Nbuf,128,stdin);                }                else                {                        strcat(Nbuf,name);                        printf("name:%s\n",name);                        sendto(sockfd,Nbuf,128,0,(struct sockaddr *)&serveraddr,len);                        recv(sockfd,Nbuf,128,0);                        if(!strncmp(Nbuf,"quit",4))                        {                                printf("用户名重复,请重新输入\n");                                continue;                        }                        else if(!strncmp(Nbuf,"ok..",4))                        {                                printf("输入成功!\n");                                break;                        }                }        }        strcpy(NNbuf,name);        strcat(NNbuf+16,name);        strcat(NNbuf+16,"说: ");        strcat(NNbuf+16,"quit");        pid_t pid=fork();        int ch,i;        if(pid0)        {                qpid=pid;                signal(SIGINT,handler);                while(1)                {                        memset(Nbuf,0,sizeof(Nbuf));                        strcpy(Nbuf,name);                        strcat(Nbuf+16,name);                        strcat(Nbuf+16,"说: ");                        printf("请输入:");                        fgets(buf,64,stdin);                        if(buf[strlen(buf)-1]=='\n')                                buf[strlen(buf)-1]='\0';                        strcat(Nbuf+16,buf);                        sendto(sockfd,Nbuf,128,0,(struct sockaddr *)&serveraddr,len);                        if(!strcmp(buf,"quit"))                        {                                kill(pid,SIGQUIT);                                break;                        }                }                wait(NULL);        }        else        {                while(1)                {                        ch=recvfrom(sockfd,Nbuf,128,0,(struct sockaddr *)&serveraddr,&len);                        if(ch

帖子地址: 

回复

使用道具 举报

分享
推广
火星云矿 | 预约S19Pro,享500抵1000!
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

草根技术分享(草根吧)是全球知名中文IT技术交流平台,创建于2021年,包含原创博客、精品问答、职业培训、技术社区、资源下载等产品服务,提供原创、优质、完整内容的专业IT技术开发社区。
  • 官方手机版

  • 微信公众号

  • 商务合作