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 }

在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章