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

拓扑结构:

各个客户端创建读写管道,通过“上下线信息管道”向服务器发送上下线信息和读写管道名称。服务器接受信息,修改链表(存储客户端信息)。客户端、服务器打开读写管道,服务器通过“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_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\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) //遍历到该客户端PID
119                 {
120                     ppre = pcur ;
121                     pcur = pcur ->m_next ;
122                 }
123
124
125                 if(pcur == NULL)
126                 {
127                     printf("not exist!\n");
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 !\n");
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!\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 }
时间: 2024-10-10 16:28:13

1 server - n clients 模型实现(select)的相关文章

SQL Server 中心订阅模型(多发布单订阅)

原文:SQL Server 中心订阅模型(多发布单订阅) 大多数SQL Server 复制拓扑都是基于中心发布模型,它是由一个发布复制到一个或者多个订阅.另一个复制模型是中心订阅模型,它使用事务复制由多个发布复制到一个订阅中. 中心订阅模型用于从多个数据源中合并数据,例如: >从多个数据仓库中汇总库存到企业总部的中心服务器: >同一个公司中从持续发送远程办公数据到中心办公系统中: >合并订单信息到同一个地方集中处理. 默认情况下,订阅通过快照代理初始化生成快照并被分发代理应用.当快照被应

几种典型的服务器网络编程模型归纳(select poll epoll)

1.同步阻塞迭代模型 同步阻塞迭代模型是最简单的一种IO模型. 其核心代码如下: bind(srvfd); listen(srvfd); for(;;) { clifd = accept(srvfd,...); //开始接受客户端来的连接 read(clifd,buf,...); //从客户端读取数据 dosomthingonbuf(buf); write(clifd,buf)//发送数据到客户端 } 上面的程序存在如下一些弊端: 1)如果没有客户端的连接请求,进程会阻塞在accept系统调用处

[转]IO模型及select、poll、epoll和kqueue的区别

(一)首先,介绍几种常见的I/O模型及其区别,如下: blocking I/O nonblocking I/O I/O multiplexing (select and poll) signal driven I/O (SIGIO) asynchronous I/O (the POSIX aio_functions)—————异步IO模型最大的特点是 完成后发回通知. 阻塞与否,取决于实现IO交换的方式.      异步阻塞是基于select,select函数本身的实现方式是阻塞的,而采用sel

epoll模型与select模型的区别

Nginx  --->epoll模型 Apache --->select模型 处理大量连接的读写时,Apache所采用的select网络I/O模型比较低,用两个通俗的比喻来解释二者的区别: 第一个比喻: 例如你在大学读书,住的宿舍楼有很多房间,你的朋友要来找你,select版宿管大妈就会 带着你的朋友到各个房间挨个去找,直到找到为止.而epoll版宿管大妈会先记下每位入住同学的房间号码,当你朋友来找你时,只需告诉你的朋友你住在哪个房间?不用亲自带着你的朋友满宿舍的找.如果同时来了100个人,都

linux下多路复用模型之Select模型

Linux关于并发网络分为Apache模型(Process per Connection (进程连接) ) 和TPC , 还有select模型,以及poll模型(一般是Epoll模型) Select模型极其作用:这文章讲述的很好,没必要重述已有的东西,就直接给链接 http://blog.csdn.net/turkeyzhou/article/details/8609360 我的理解: 1 /* According to POSIX.1-2001 */ 2 #include <sys/selec

Socket I/O模型之select模型

socket网络编程中有多种常见的I/O模型: 1.blocking阻塞 2.nonblocking非阻塞 3.I/O multiplexing复用 4.signal driven 5.asynchronous I/O异步 这里我们主要介绍I/O multiplexing模型中的代表select模型:select模型将多个套接字放在一个集合里,然后统一检查这些套接字的状态,每次调用套接字后会更新这些套接字的状态,然后做判断,如果套接字可读,就执行read操作.这样就巧妙地避免了阻塞,达到同时处理

1高并发server:多路IO之select

?? 1 select A:select能监听的文件描写叙述符个数受限于FD_SETSIZE,一般为1024.单纯改变进程打开 的文件描写叙述符个数并不能改变select监听文件个数 B:解决1024下面client时使用select是非常合适的,但假设链接client过多,select採用的是轮询模型,会大大减少server响应效率.不应在select上投入很多其它精力 2 依赖的头文件 #include <sys/select.h> /* According to earlier stan

socket阻塞与非阻塞,同步与异步、I/O模型,select与poll、epoll比较

1. 概念理解 在进行网络编程时,我们常常见到同步(Sync)/异步(Async),阻塞(Block)/非阻塞(Unblock)四种调用方式: 同步/异步主要针对C端: 同步:      所谓同步,就是在c端发出一个功能调用时,在没有得到结果之前,该调用就不返回.也就是必须一件一件事做,等前一件做完了才能做下一件事. 例如普通B/S模式(同步):提交请求->等待服务器处理->处理完毕返回 这个期间客户端浏览器不能干任何事 异步:      异步的概念和同步相对.当c端一个异步过程调用发出后,调

Winsock IO模型之select模型

之所以称其为select模型是因为它主要是使用select函数来管理I/O的.这个模型的设计源于UNIX系统,目的是允许那些想要避免在套接字调用上阻塞的应用程序有能力管理多个套接字. int select( int nfds,                                                 // 忽略,仅是为了与Berkeley套接字兼容 fd_set* readfds,                                  // 指向一个套接字集合,