本地聊天室通过有名管道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 }