拓扑结构:
各个客户端创建读写管道,通过“上下线信息管道”向服务器发送上下线信息和读写管道名称。服务器接受信息,修改链表(存储客户端信息)。客户端、服务器打开读写管道,服务器通过“W”管道接收从客户端发来的信息,在根据链表同个其他各个“R”管道向其他客户端发送信息。
具体流程:
1、建立上下线信息管道
服务器:
1 mkfifo(path_name, 0666);// 创建管道 —— 专用于接收客户端上下线信息 2 3 printf("mkfifo over! "); 4 5 fd_listen = open(path_name, O_RDONLY); 6 if(fd_listen == -1) 7 { 8 printf("open server_fifo fail! "); 9 exit(1); 10 }
客户端:
1 //打开上下线管道 2 int fd_server ; 3 char path_name[128]=""; 4 char fifo_name[128] ; 5 char msg[1024] ="" ; 6 char fifo1[128], fifo2[128] ; 7 int fd_recv, fd_send ; 8 sprintf(path_name, "%s/%s", PIPE_PATH, PIPE_NAME); 9 10 fd_server = open(path_name, O_WRONLY); 11 if(fd_server == -1) 12 { 13 printf("open fail! "); 14 exit(1) ; 15 }
2、客户端建立读写管道
// 建造读写管道 pid_r.fifo pid_w.fifo // memset(fifo_name, 0, 128); sprintf(fifo_name, "%u_r.fifo", getpid()); memset(path_name, 0, sizeof(path_name)); sprintf(path_name, "%s/%s", PIPE_PATH, fifo_name); strcpy(fifo1, path_name); if(-1 == mkfifo(path_name, 0666) ) { printf("mkfif fail: %s ", path_name); exit(1) ; } printf("%s open ", path_name); memset(fifo_name, 0, 128); sprintf(fifo_name, "%u_w.fifo", getpid()); memset(path_name, 0, sizeof(path_name)); sprintf(path_name, "%s/%s", PIPE_PATH, fifo_name); strcpy(fifo2, path_name); if(mkfifo(path_name, 0666) == -1 ) { printf("mkfif fail: %s ", path_name); exit(1) ; } printf("%s open ", path_name); printf("mkfifo over! ");
3、上线处理
客户端发送上线信息:
1 //发送上线信息 2 sprintf(msg, "%u on ", getpid()); 3 printf("msg: %s ", msg); 4 write(fd_server, msg, strlen(msg));
服务器监听到后处理:
1 // 读写是针对客户端而言的: pid_r.fifo(c_r - s_w) pid_w.fifo(c_w - s_r) 2 printf("client: %d on ", client_pid); 3 //pid_r.fifo s_w 4 //构建管道名字符串 5 memset(fifo_name, 0, 128) ; 6 sprintf(fifo_name, "%d_r.fifo", client_pid); 7 memset(path_name, 0, 128) ; 8 sprintf(path_name, "%s/%s", PIPE_PATH, fifo_name); 9 10 //新增链表节点 11 pnew = (pCLIENT)calloc(1, sizeof(CLIENT)); 12 pnew ->m_id = client_pid ; 13 printf("pid_r.fifo: %s ", path_name); 14 pnew ->m_send = open(path_name, O_WRONLY); 15 printf("send_fd: %d ", pnew ->m_send); 16 17 //打开“W”管道 pid_w.fifo s_r 18 memset(fifo_name, 0, 128) ; 19 sprintf(fifo_name, "%d_w.fifo", client_pid); 20 memset(path_name, 0, 128) ; 21 sprintf(path_name, "%s/%s", PIPE_PATH, fifo_name); 22 23 24 printf("pid_w.fifo: %s ", path_name); 25 pnew ->m_recv = open(path_name, O_RDONLY); 26 printf("recv_fd: %d ", pnew ->m_recv); 27 printf("open client fifo: %d, %d ", pnew ->m_send, pnew ->m_recv); 28 29 30 FD_SET(pnew ->m_recv, &rd_sets);//把pid_w.fifo 句柄 加到读集合中去 主要:更新的是读集合,而不是副本集合 31 32 pnew ->m_next = plist ; //插入链表 33 plist = pnew ;
客户端也打开管道:
1 memset(fifo_name, 0, 128); 2 sprintf(fifo_name, "%u_r.fifo", getpid()); 3 memset(path_name, 0, sizeof(path_name)); 4 sprintf(path_name, "%s/%s", PIPE_PATH, fifo_name); 5 6 fd_recv = open(path_name, O_RDONLY); 7 8 memset(fifo_name, 0, 128); 9 sprintf(fifo_name, "%u_w.fifo", getpid()); 10 memset(path_name, 0, sizeof(path_name)); 11 sprintf(path_name, "%s/%s", PIPE_PATH, fifo_name); 12 13 fd_send = open(path_name, O_WRONLY); 14 15 printf("fifo open %d %d ", fd_send, fd_recv);
4、客户端、服务器对通信信息处理
客户端监听到键盘的输入信息,则转发给服务器:
1 if(FD_ISSET(0, &rd_sets)) 2 { 3 memset(msg, 0, sizeof(msg)) ; 4 sprintf(msg, "from %u: ", getpid()); 5 write(fd_send, msg, strlen(msg)); 6 7 }
客户端监听服务器发来的信息,并打印:
1 if(FD_ISSET(fd_recv, &rd_sets)) 2 { 3 memset(msg, 0, sizeof(msg)) ; 4 read(fd_recv, msg, 1024); 5 write(1, msg, strlen(msg)); 6 }
服务器监听到客户端发来的信息,根据链表内客户端的信息,进行转发:
1 //2、遍历链表,监听其他的管道文件(用于服务器和客户端通信)句柄 2 pcur = plist ; 3 while(pcur) 4 { 5 if(FD_ISSET(pcur ->m_recv, &bak_sets))// translate 6 { 7 memset(msg, 0, 1024); 8 read(pcur -> m_recv, msg, 1024); 9 10 dispatch_msg(plist,pcur, msg); 11 } 12 pcur = pcur ->m_next ; 13 }
1 void dispatch_msg(pCLIENT phead,pCLIENT pcur, char* msg) 2 { 3 while(phead) 4 { 5 if(phead!=pcur) 6 write(phead ->m_send, msg, strlen(msg)); 7 phead = phead ->m_next ; 8 } 9 }
完整代码
server:
1 #include<stdio.h> 2 #include<stdlib.h> 3 #include<string.h> 4 #include<unistd.h> 5 #include<sys/stat.h> 6 #include<sys/types.h> 7 #include<fcntl.h> 8 #include<sys/select.h> 9 #include<sys/time.h> 10 #define PIPE_PATH "/home/soso/Desktop/2-17/FIFO" 11 #define PIPE_NAME "server.fifo" 12 typedef struct tag 13 { 14 int m_id ; 15 int m_send; 16 int m_recv; 17 struct tag* m_next ; 18 }CLIENT, *pCLIENT; 19 20 21 void dispatch_msg(pCLIENT phead,pCLIENT pcur, char* msg) 22 { 23 while(phead) 24 { 25 if(phead!=pcur) 26 write(phead ->m_send, msg, strlen(msg)); 27 phead = phead ->m_next ; 28 } 29 } 30 31 int main(int argc, char* argv[]) 32 { 33 int fd_listen ; //文件句柄 34 char path_name[128] = "" ; 35 char fifo_name[128] ; 36 char msg[1024]; 37 38 char client_stat[5] = "";//客户端状态 39 int client_pid ; // 客户端进程ID 40 sprintf(path_name, "%s/%s", PIPE_PATH, PIPE_NAME);//路径名 = 路径/管道名 41 42 mkfifo(path_name, 0666);// 创建管道 —— 专用于接收客户端上下线信息 43 44 printf("mkfifo over! "); 45 46 fd_listen = open(path_name, O_RDONLY); 47 if(fd_listen == -1) 48 { 49 printf("open server_fifo fail! "); 50 exit(1); 51 } 52 53 54 pCLIENT plist = NULL, pcur, pnew, ppre ; 55 56 fd_set rd_sets, bak_sets; //读集合 和 备份读集合 57 FD_ZERO(&rd_sets);//初始化清空 58 FD_ZERO(&bak_sets); 59 FD_SET(fd_listen, &rd_sets); //将fd_listen句柄 加入到 读集合 60 while(1) 61 { 62 bak_sets = rd_sets ;//每次循环更新副本集合 63 printf("selecting... "); 64 select(1024, &bak_sets, NULL, NULL, NULL);//监听集合 65 66 //1、监听fd_listen 管道文件句柄(专用于服务器接收客户端上下线信息) 67 if(FD_ISSET(fd_listen, &bak_sets)) //若监听到 fd_listen 68 { 69 memset(msg,0, 1024); 70 if( read(fd_listen, msg, 1024) == 0 ) //读取管道信息;但没有客户端 write的时候,read的返回值是0 71 { 72 printf("no clients! "); 73 continue ; 74 } 75 76 memset(client_stat, 0, sizeof(client_stat)); 77 sscanf(msg, "%d%s", &client_pid, client_stat);//信息格式:“client_pid client_stat ” 78 if(strncmp("on", client_stat, 2) == 0)//client on"pid on " 79 {// 读写是针对客户端而言的: pid_r.fifo(c_r - s_w) pid_w.fifo(c_w - s_r) 80 printf("client: %d on ", client_pid); 81 //pid_r.fifo s_w 82 //构建管道名字符串 83 memset(fifo_name, 0, 128) ; 84 sprintf(fifo_name, "%d_r.fifo", client_pid); 85 memset(path_name, 0, 128) ; 86 sprintf(path_name, "%s/%s", PIPE_PATH, fifo_name); 87 88 //新增链表节点 89 pnew = (pCLIENT)calloc(1, sizeof(CLIENT)); 90 pnew ->m_id = client_pid ; 91 printf("pid_r.fifo: %s ", path_name); 92 pnew ->m_send = open(path_name, O_WRONLY); 93 printf("send_fd: %d ", pnew ->m_send); 94 95 //pid_w.fifo s_r 96 memset(fifo_name, 0, 128) ; 97 sprintf(fifo_name, "%d_w.fifo", client_pid); 98 memset(path_name, 0, 128) ; 99 sprintf(path_name, "%s/%s", PIPE_PATH, fifo_name); 100 101 102 printf("pid_w.fifo: %s ", path_name); 103 pnew ->m_recv = open(path_name, O_RDONLY); 104 printf("recv_fd: %d ", pnew ->m_recv); 105 printf("open client fifo: %d, %d ", pnew ->m_send, pnew ->m_recv); 106 107 108 FD_SET(pnew ->m_recv, &rd_sets);//把pid_w.fifo 句柄 加到读集合中去 主要:更新的是读集合,而不是副本集合 109 110 pnew ->m_next = plist ; //插入链表 111 plist = pnew ; 112 113 }else//client off "pid off " 114 { 115 printf("client: %d off ", client_pid); 116 ppre = NULL ;//前驱指针 117 pcur = plist ; 118 while(pcur && pcur ->m_id != client_pid) //遍历到该客户端PID 119 { 120 ppre = pcur ; 121 pcur = pcur ->m_next ; 122 } 123 124 125 if(pcur == NULL) 126 { 127 printf("not exist! "); 128 continue ; 129 }else 130 { 131 //删除节点 132 if(ppre == NULL) 133 { 134 plist = pcur ->m_next ; 135 }else 136 { 137 ppre ->m_next = pcur ->m_next ; 138 } 139 140 //关闭文件 141 close(pcur ->m_send) ; 142 close(pcur ->m_recv) ; 143 144 //把 pcur ->m_recv 从读集合中删除 145 FD_CLR(pcur ->m_recv, &rd_sets); 146 147 free(pcur); //释放内存 148 printf("clear ok ! "); 149 150 } 151 } 152 } 153 154 //2、遍历链表,监听其他的管道文件(用于服务器和客户端通信)句柄 155 pcur = plist ; 156 while(pcur) 157 { 158 if(FD_ISSET(pcur ->m_recv, &bak_sets))// translate 159 { 160 memset(msg, 0, 1024); 161 read(pcur -> m_recv, msg, 1024); 162 163 dispatch_msg(plist,pcur, msg); 164 } 165 pcur = pcur ->m_next ; 166 } 167 } 168 return 0 ; 169 }
client:
1 #include<stdio.h> 2 #include<stdlib.h> 3 #include<string.h> 4 #include<unistd.h> 5 #include<sys/stat.h> 6 #include<sys/types.h> 7 #include<fcntl.h> 8 #include<sys/select.h> 9 #include<sys/time.h> 10 #define PIPE_PATH "/home/soso/Desktop/2-17/FIFO" 11 #define PIPE_NAME "server.fifo" 12 int main(int argc, char* argv[]) 13 { 14 //1、向服务器通知上线下线信息 15 16 //打开上下线管道 17 int fd_server ; 18 char path_name[128]=""; 19 char fifo_name[128] ; 20 char msg[1024] ="" ; 21 char fifo1[128], fifo2[128] ; 22 int fd_recv, fd_send ; 23 sprintf(path_name, "%s/%s", PIPE_PATH, PIPE_NAME); 24 25 fd_server = open(path_name, O_WRONLY); 26 if(fd_server == -1) 27 { 28 printf("open fail! "); 29 exit(1) ; 30 } 31 32 33 34 // 建造读写管道 pid_r.fifo pid_w.fifo 35 // 36 memset(fifo_name, 0, 128); 37 sprintf(fifo_name, "%u_r.fifo", getpid()); 38 memset(path_name, 0, sizeof(path_name)); 39 sprintf(path_name, "%s/%s", PIPE_PATH, fifo_name); 40 41 strcpy(fifo1, path_name); 42 if(-1 == mkfifo(path_name, 0666) ) 43 { 44 printf("mkfif fail: %s ", path_name); 45 exit(1) ; 46 } 47 48 printf("%s open ", path_name); 49 50 memset(fifo_name, 0, 128); 51 sprintf(fifo_name, "%u_w.fifo", getpid()); 52 memset(path_name, 0, sizeof(path_name)); 53 sprintf(path_name, "%s/%s", PIPE_PATH, fifo_name); 54 55 strcpy(fifo2, path_name); 56 if(mkfifo(path_name, 0666) == -1 ) 57 { 58 59 printf("mkfif fail: %s ", path_name); 60 exit(1) ; 61 } 62 printf("%s open ", path_name); 63 64 printf("mkfifo over! "); 65 66 67 //发送上线信息 68 sprintf(msg, "%u on ", getpid()); 69 printf("msg: %s ", msg); 70 71 write(fd_server, msg, strlen(msg)); 72 73 // 74 memset(fifo_name, 0, 128); 75 sprintf(fifo_name, "%u_r.fifo", getpid()); 76 memset(path_name, 0, sizeof(path_name)); 77 sprintf(path_name, "%s/%s", PIPE_PATH, fifo_name); 78 79 fd_recv = open(path_name, O_RDONLY); 80 81 memset(fifo_name, 0, 128); 82 sprintf(fifo_name, "%u_w.fifo", getpid()); 83 memset(path_name, 0, sizeof(path_name)); 84 sprintf(path_name, "%s/%s", PIPE_PATH, fifo_name); 85 86 fd_send = open(path_name, O_WRONLY); 87 88 printf("fifo open %d %d ", fd_send, fd_recv); 89 90 fd_set rd_sets ; 91 FD_ZERO(&rd_sets); 92 while(1) 93 { 94 FD_SET(0, &rd_sets); 95 FD_SET(fd_recv, &rd_sets); 96 97 select(1024, &rd_sets, NULL, NULL, NULL); 98 99 if(FD_ISSET(0, &rd_sets)) 100 { 101 memset(msg, 0, sizeof(msg)) ; 102 sprintf(msg, "from %u: ", getpid()); 103 if(read(0, msg + strlen(msg), 1024 - strlen(msg) ) == 0) 104 { 105 printf("off! "); 106 memset(msg, 0, sizeof(msg)); 107 sprintf(msg, "%d off ", getpid()); 108 write(fd_server, msg, strlen(msg)); 109 110 close(fd_send); 111 close(fd_recv); 112 113 unlink(fifo1); 114 unlink(fifo2); 115 break ; 116 } 117 write(fd_send, msg, strlen(msg)); 118 119 } 120 if(FD_ISSET(fd_recv, &rd_sets)) 121 { 122 memset(msg, 0, sizeof(msg)) ; 123 read(fd_recv, msg, 1024); 124 write(1, msg, strlen(msg)); 125 } 126 } 127 }