1 server - n clients 模型实现(select)

来源:互联网 发布:微众银行程序员工资 编辑:程序博客网 时间:2024/05/17 03:41
拓扑结构:

 

各个客户端创建读写管道,通过“上下线信息管道”向服务器发送上下线信息和读写管道名称。服务器接受信息,修改链表(存储客户端信息)。客户端、服务器打开读写管道,服务器通过“W”管道接收从客户端发来的信息,在根据链表同个其他各个“R”管道向其他客户端发送信息。

 

具体流程:

1、建立上下线信息管道

服务器:

 1 mkfifo(path_name, 0666);// 创建管道 —— 专用于接收客户端上下线信息 2  3     printf("mkfifo over!\n"); 4  5     fd_listen = open(path_name, O_RDONLY); 6     if(fd_listen == -1) 7     { 8         printf("open server_fifo fail!\n"); 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!\n");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\n", path_name);        exit(1) ;    }    printf("%s open\n", 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\n", path_name);        exit(1) ;    }    printf("%s open\n", path_name);    printf("mkfifo over!\n");

3、上线处理

客户端发送上线信息:

1 //发送上线信息2     sprintf(msg, "%u on\n", getpid());3     printf("msg: %s\n", 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\n", 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\n", path_name);14                 pnew ->m_send = open(path_name, O_WRONLY);15                 printf("send_fd: %d\n", pnew ->m_send);16 17                 //打开“W”管道 pid_w.fifo  s_r18                 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\n", path_name);25                 pnew ->m_recv = open(path_name, O_RDONLY);26                 printf("recv_fd: %d\n", pnew ->m_recv);27                 printf("open client fifo: %d, %d\n", 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\n", 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!\n"); 45  46     fd_listen = open(path_name, O_RDONLY); 47     if(fd_listen == -1) 48     { 49         printf("open server_fifo fail!\n"); 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...\n"); 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!\n"); 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\n” 78             if(strncmp("on", client_stat, 2) == 0)//client on"pid on\n" 79             {// 读写是针对客户端而言的: pid_r.fifo(c_r - s_w)   pid_w.fifo(c_w - s_r) 80                 printf("client: %d on\n", 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\n", path_name); 92                 pnew ->m_send = open(path_name, O_WRONLY); 93                 printf("send_fd: %d\n", 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\n", path_name);103                 pnew ->m_recv = open(path_name, O_RDONLY);104                 printf("recv_fd: %d\n", pnew ->m_recv);105                 printf("open client fifo: %d, %d\n", 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\n"114             {115                 printf("client: %d off\n", client_pid);116                 ppre = NULL ;//前驱指针117                 pcur = plist ;118                 while(pcur && pcur ->m_id != client_pid) //遍历到该客户端PID119                 {120                     ppre = pcur ;121                     pcur = pcur ->m_next ;122                 }123 124 125                 if(pcur == NULL)126                 {127                     printf("not exist!\n");128                     continue ;129                 }else130                 {131                     //删除节点132                     if(ppre == NULL) 133                     {134                         plist = pcur ->m_next ;135                     }else136                     {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 !\n");149 150                 }151             }152         }153 154         //2、遍历链表,监听其他的管道文件(用于服务器和客户端通信)句柄155         pcur = plist ;156         while(pcur)157         {158             if(FD_ISSET(pcur ->m_recv, &bak_sets))// translate159             {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!\n"); 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\n", path_name); 45         exit(1) ; 46     } 47  48     printf("%s open\n", 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\n", path_name); 60         exit(1) ; 61     } 62     printf("%s open\n", path_name); 63  64     printf("mkfifo over!\n"); 65  66  67     //发送上线信息 68     sprintf(msg, "%u on\n", getpid()); 69     printf("msg: %s\n", 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\n", 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!\n");106                     memset(msg, 0, sizeof(msg));107                     sprintf(msg, "%d off\n", 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 }

 

0 0