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

2024-06-02 04:58

本文主要是介绍1 server - n clients 模型实现(select),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

http://blog.csdn.net/u011239443/article/details/51655333

拓扑结构:

 

各个客户端创建读写管道,通过“上下线信息管道”向服务器发送上下线信息和读写管道名称。服务器接受信息,修改链表(存储客户端信息)。客户端、服务器打开读写管道,服务器通过“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 }

 

这篇关于1 server - n clients 模型实现(select)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1023081

相关文章

C++使用栈实现括号匹配的代码详解

《C++使用栈实现括号匹配的代码详解》在编程中,括号匹配是一个常见问题,尤其是在处理数学表达式、编译器解析等任务时,栈是一种非常适合处理此类问题的数据结构,能够精确地管理括号的匹配问题,本文将通过C+... 目录引言问题描述代码讲解代码解析栈的状态表示测试总结引言在编程中,括号匹配是一个常见问题,尤其是在

Java实现检查多个时间段是否有重合

《Java实现检查多个时间段是否有重合》这篇文章主要为大家详细介绍了如何使用Java实现检查多个时间段是否有重合,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录流程概述步骤详解China编程步骤1:定义时间段类步骤2:添加时间段步骤3:检查时间段是否有重合步骤4:输出结果示例代码结语作

使用C++实现链表元素的反转

《使用C++实现链表元素的反转》反转链表是链表操作中一个经典的问题,也是面试中常见的考题,本文将从思路到实现一步步地讲解如何实现链表的反转,帮助初学者理解这一操作,我们将使用C++代码演示具体实现,同... 目录问题定义思路分析代码实现带头节点的链表代码讲解其他实现方式时间和空间复杂度分析总结问题定义给定

Java覆盖第三方jar包中的某一个类的实现方法

《Java覆盖第三方jar包中的某一个类的实现方法》在我们日常的开发中,经常需要使用第三方的jar包,有时候我们会发现第三方的jar包中的某一个类有问题,或者我们需要定制化修改其中的逻辑,那么应该如何... 目录一、需求描述二、示例描述三、操作步骤四、验证结果五、实现原理一、需求描述需求描述如下:需要在

如何使用Java实现请求deepseek

《如何使用Java实现请求deepseek》这篇文章主要为大家详细介绍了如何使用Java实现请求deepseek功能,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录1.deepseek的api创建2.Java实现请求deepseek2.1 pom文件2.2 json转化文件2.2

python使用fastapi实现多语言国际化的操作指南

《python使用fastapi实现多语言国际化的操作指南》本文介绍了使用Python和FastAPI实现多语言国际化的操作指南,包括多语言架构技术栈、翻译管理、前端本地化、语言切换机制以及常见陷阱和... 目录多语言国际化实现指南项目多语言架构技术栈目录结构翻译工作流1. 翻译数据存储2. 翻译生成脚本

如何通过Python实现一个消息队列

《如何通过Python实现一个消息队列》这篇文章主要为大家详细介绍了如何通过Python实现一个简单的消息队列,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录如何通过 python 实现消息队列如何把 http 请求放在队列中执行1. 使用 queue.Queue 和 reque

Python如何实现PDF隐私信息检测

《Python如何实现PDF隐私信息检测》随着越来越多的个人信息以电子形式存储和传输,确保这些信息的安全至关重要,本文将介绍如何使用Python检测PDF文件中的隐私信息,需要的可以参考下... 目录项目背景技术栈代码解析功能说明运行结php果在当今,数据隐私保护变得尤为重要。随着越来越多的个人信息以电子形

使用 sql-research-assistant进行 SQL 数据库研究的实战指南(代码实现演示)

《使用sql-research-assistant进行SQL数据库研究的实战指南(代码实现演示)》本文介绍了sql-research-assistant工具,该工具基于LangChain框架,集... 目录技术背景介绍核心原理解析代码实现演示安装和配置项目集成LangSmith 配置(可选)启动服务应用场景

使用Python快速实现链接转word文档

《使用Python快速实现链接转word文档》这篇文章主要为大家详细介绍了如何使用Python快速实现链接转word文档功能,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 演示代码展示from newspaper import Articlefrom docx import