Linux系统编程练习之本地聊天室

本地聊天室通过有名管道FIFO实现
qq_ipc.h

  1 #ifndef _QQ_IPC_H_
  2 #define _QQ_IPC_H_
  3 //num = 0 新用户注册
  4 //num = 1 聊天信息
  5 //num = 2 服务器更新列表信息
  6 typedef struct{
  7     int num;
  8     char name[10];
  9     char aims[10];
 10     char content[256];
 11 }package;  
 12 #endif

各客户端通过公有FIFO,向服务端发送数据,服务端进行解析
若为0号包,则将该客户端加入在线链表
若为1号包,则将相应数据通过目标客户端的私有FIFO发送给目标客户端
若为2号包,则将客户端从在线链表取出,客户端下线

各客户端不断读自己的私有FIFO,接受数据显示到屏幕上。

server.c

  1 #include<stdio.h>
  2 #include<sys/types.h>
  3 #include<sys/stat.h>
  4 #include<unistd.h>
  5 #include<string.h>
  6 #include<stdlib.h>
  7 #include<fcntl.h>
  8 #include"qq_ipc.h"
  9 
 10 struct node{
 11     char name[10];
 12     int fd;
 13     struct node *next;
 14 };
 15 struct node *head=NULL;
 16 struct node *end=NULL;
 17 void add(char name[10],int fd)//客户端上线,加入在线链表
 18 {
 19     if(end == NULL)
 20     {
 21         end =(struct node *)malloc(sizeof(struct node));
 22         strcpy(end->name,name);
 23         end->fd = fd;
 24         head = end;
 25     }
 26     else{
 27         end->next =(struct node *)malloc(sizeof(struct node));
 28         strcpy(end->next->name,name);
 29         end->next->fd = fd;
 30         end=end->next;
 31     }
 32 }
 33 int find_delete(char name[10],int flag){//flag为1时查找目标客户端fifo写端文件描述符
 34     struct node *p = head;              //否则,客户端下线,更新在线链表
 35     struct node *before=p;
 36     while(p !=NULL)
 37     {
 38         if(strcmp(p->name,name) == 0){
 39             if(flag == 1)
 40                 return p->fd;
 41             else{
 42                 close(p->fd);
 43                 before->next = p->next;
 44                 free(p);
 45             }
 46             break;
 47         }
 48         before = p;
 49         p = p->next;
 50     }
 51     return -1;
 52 }
 53 int main(void)
 54 {
 55     if(access("server_fifo",F_OK) == -1)//判断公共FIFO管道是否存在
 56     {
 57         if(mkfifo("server_fifo",0644) == -1){//不存在,则建立公共fifo
 58             perror("mkfifo error:");
 59             exit(1);
 60         }
 61     }
 62     int fd1,fd2;
 63     int fd = open("server_fifo",O_RDONLY);//只读阻塞 打开公共FIFO
 64     if(fd == -1)
 65     {
 66         perror("open server_fifo error:");
 67         exit(1);
 68     }
 69     package buf;
 70     int num = 0;
 71     while(1)
 72     {
 73         num = read(fd,&buf,sizeof(buf));//阻塞读公共管道
 74         if(num == -1){
 75             perror("read error:");
 76             exit(1);
 77         }
 78         if(num > 0)
 79         {
 80             switch(buf.num)
 81             {
 82                 case 0://若为0号包则,建立对应客户端私有FIFO,并更新在线链表
 83                     if(access(buf.name,F_OK) == -1){
 84                         if(mkfifo(buf.name,0644) == -1)
 85                         {
 86                             perror("case 1 mkfifo error:");
 87                             exit(1);
 88                         }
 89                     }
 90                     int dummyfd = open(buf.name,O_RDONLY|O_NONBLOCK);//对于写阻塞打开FIFO必须先打开读,否则会出错,报错为No such device or address
 91                                                                     //通过dummyfd可以避免,控制读端和写端的打开顺序
 92                     fd1 = open(buf.name,O_WRONLY|O_NONBLOCK);//写阻塞打开看客户端私有FIFO
 93                     if(fd1 == -1)
 94                     {
 95                         perror("open case_0 error:");
 96                         exit(1);
 97                     }
 98                     add(buf.name,fd1);//调用函数更新在线链表
 99                     printf("%s上线\n",buf.name);
100                     break;
101                 case 1://若为1号包,则对目标客户端发送信息。
102                     fd2 = find_delete(buf.aims,1);//查看目标客户端是否在线
103                     if(fd2 == -1)//不在线则向源客户端发送不在线提示
104                     {
105                         buf.num = -1;
106                         strcpy(buf.content,"不在线");
107                         if(write(find_delete(buf.name,1),&buf,sizeof(buf)) == -1)
108                         {
109                             perror("write name error:");
110                             exit(1);
111                         }
112                     }
113                     else{//在线则发送信息
114                         if(write(fd2,&buf,sizeof(buf)) == -1){
115                             perror("write aims error:");
116                             exit(1);
117                         }
118                     }
119                     break;
120                 case 2://若为2号包则下线对应客户端,更新在线链表
121                     find_delete(buf.name,0);
122                     printf("%s下线\n",buf.name);
123                     break;
124             }
125         }
126     }
127     return 0;
128 }


client.c

  1 #include<sys/types.h>
  2 #include<string.h>
  3 #include<sys/stat.h>
  4 #include<unistd.h>
  5 #include<stdio.h>
  6 #include"qq_ipc.h"
  7 #include<fcntl.h>
  8 #include<stdlib.h>
  9 int main(void)
 10 {
 11     package usr;//注册包
 12     char name[10],name_2[10];
 13     printf("输入客户端名称:");
 14     scanf("%s",name);
 15     printf("输入聊天客户端:");
 16     scanf("%s",name_2);
 17     usr.num=0;//填充注册包
 18     strcpy(usr.name,name);
 19     int fd = open("server_fifo",O_WRONLY);//只写打开公共FIFO
 20     if(fd == -1){
 21         perror("open server_fifo error:");
 22         exit(1);
 23     }
 24     write(fd,&usr,sizeof(usr));//向服务器注册
 25     int fd_1 = open(name,O_RDONLY|O_NONBLOCK);//读阻塞客户端私有FIFO
 26     if(fd_1 == -1)
 27     {
 28         perror("open client_fifo error:");
 29         exit(1);
 30     }
 31     int num;
 32     package cont,send;//接受包和发送包
 33     send.num=1;//填充发送包
 34     strcpy(send.name,name);
 35     strcpy(send.aims,name_2);
 36     char content[256];//接受标准输入
 37     while(1)
 38     {
 39         strcpy(content,"");//将两个数组初始化,避免循环显示信息
 40         strcpy(cont.content,"");
 41         num = read(fd_1,&cont,sizeof(cont));//读客户端私有FIFO
 42         if(num > 0 && strlen(cont.content) > 0)//若接收到信息则显示在屏幕上
 43         {
 44             if(cont.num == 1)
 45                 printf("%s: %s\n",cont.name,cont.content);
 46             else printf("客户端%s%s\n",cont.aims,cont.content);
 47         }
 48 
 49         //将标准输入设置为非阻塞,不会阻塞等待输入,可以显示收到的信息
 50         int flag = fcntl(STDIN_FILENO,F_GETFL);
 51         if(flag < 0){
 52             perror("fcntl error:");
 53             exit(1);
 54         }
 55         fcntl(STDIN_FILENO,F_SETFL,flag|O_NONBLOCK);
 56 
 57         scanf("%s",content);//接受输入
 58         if(strcmp(content,"q") == 0)//若输入为q,客户端下线
 59         {
 60             usr.num=2;
 61             write(fd,&usr,sizeof(usr));//向服务端发送下线包
 62             break;
 63         }
 64         else{//否则向服务端发送聊天包
 65             if(strlen(content) > 0)
 66             {
 67                 strcpy(send.content,content);
 68                 write(fd,&send,sizeof(send));
 69             }
 70         }
 71     }
 72     return 0;
 73 }

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值