1 server - n clients 模型实现(select)
拓扑结构:

各个客户端创建读写管道,通过“上下线信息管道”向服务器发送上下线信息和读写管道名称。服务器接受信息,修改链表(存储客户端信息)。客户端、服务器打开读写管道,服务器通过“W”管道接收从客户端发来的信息,在根据链表同个其他各个“R”管道向其他客户端发送信息。
具体流程:
1、建立上下线信息管道
服务器:
mkfifo(path_name, );// 创建管道 —— 专用于接收客户端上下线信息
printf("mkfifo over!\n");
fd_listen = open(path_name, O_RDONLY);
if(fd_listen == -)
{
printf("open server_fifo fail!\n");
exit();
}
客户端:
//打开上下线管道
int fd_server ;
char path_name[]="";
char fifo_name[] ;
char msg[] ="" ;
char fifo1[], fifo2[] ;
int fd_recv, fd_send ;
sprintf(path_name, "%s/%s", PIPE_PATH, PIPE_NAME); fd_server = open(path_name, O_WRONLY);
if(fd_server == -)
{
printf("open fail!\n");
exit() ;
}
2、客户端建立读写管道
// 建造读写管道 pid_r.fifo pid_w.fifo
//
memset(fifo_name, , );
sprintf(fifo_name, "%u_r.fifo", getpid());
memset(path_name, , sizeof(path_name));
sprintf(path_name, "%s/%s", PIPE_PATH, fifo_name); strcpy(fifo1, path_name);
if(- == mkfifo(path_name, ) )
{
printf("mkfif fail: %s\n", path_name);
exit() ;
} printf("%s open\n", path_name); memset(fifo_name, , );
sprintf(fifo_name, "%u_w.fifo", getpid());
memset(path_name, , sizeof(path_name));
sprintf(path_name, "%s/%s", PIPE_PATH, fifo_name); strcpy(fifo2, path_name);
if(mkfifo(path_name, ) == - )
{ printf("mkfif fail: %s\n", path_name);
exit() ;
}
printf("%s open\n", path_name); printf("mkfifo over!\n");
3、上线处理
客户端发送上线信息:
//发送上线信息
sprintf(msg, "%u on\n", getpid());
printf("msg: %s\n", msg);
write(fd_server, msg, strlen(msg));
服务器监听到后处理:
// 读写是针对客户端而言的: pid_r.fifo(c_r - s_w) pid_w.fifo(c_w - s_r)
printf("client: %d on\n", client_pid);
//pid_r.fifo s_w
//构建管道名字符串
memset(fifo_name, , ) ;
sprintf(fifo_name, "%d_r.fifo", client_pid);
memset(path_name, , ) ;
sprintf(path_name, "%s/%s", PIPE_PATH, fifo_name); //新增链表节点
pnew = (pCLIENT)calloc(, sizeof(CLIENT));
pnew ->m_id = client_pid ;
printf("pid_r.fifo: %s\n", path_name);
pnew ->m_send = open(path_name, O_WRONLY);
printf("send_fd: %d\n", pnew ->m_send); //打开“W”管道 pid_w.fifo s_r
memset(fifo_name, , ) ;
sprintf(fifo_name, "%d_w.fifo", client_pid);
memset(path_name, , ) ;
sprintf(path_name, "%s/%s", PIPE_PATH, fifo_name); printf("pid_w.fifo: %s\n", path_name);
pnew ->m_recv = open(path_name, O_RDONLY);
printf("recv_fd: %d\n", pnew ->m_recv);
printf("open client fifo: %d, %d\n", pnew ->m_send, pnew ->m_recv); FD_SET(pnew ->m_recv, &rd_sets);//把pid_w.fifo 句柄 加到读集合中去 主要:更新的是读集合,而不是副本集合 pnew ->m_next = plist ; //插入链表
plist = pnew ;
客户端也打开管道:
memset(fifo_name, , );
sprintf(fifo_name, "%u_r.fifo", getpid());
memset(path_name, , sizeof(path_name));
sprintf(path_name, "%s/%s", PIPE_PATH, fifo_name); fd_recv = open(path_name, O_RDONLY); memset(fifo_name, , );
sprintf(fifo_name, "%u_w.fifo", getpid());
memset(path_name, , sizeof(path_name));
sprintf(path_name, "%s/%s", PIPE_PATH, fifo_name); fd_send = open(path_name, O_WRONLY); printf("fifo open %d %d\n", fd_send, fd_recv);
4、客户端、服务器对通信信息处理
客户端监听到键盘的输入信息,则转发给服务器:
if(FD_ISSET(, &rd_sets))
{
memset(msg, , sizeof(msg)) ;
sprintf(msg, "from %u: ", getpid());
write(fd_send, msg, strlen(msg)); }
客户端监听服务器发来的信息,并打印:
if(FD_ISSET(fd_recv, &rd_sets))
{
memset(msg, , sizeof(msg)) ;
read(fd_recv, msg, );
write(, msg, strlen(msg));
}
服务器监听到客户端发来的信息,根据链表内客户端的信息,进行转发:
//2、遍历链表,监听其他的管道文件(用于服务器和客户端通信)句柄
pcur = plist ;
while(pcur)
{
if(FD_ISSET(pcur ->m_recv, &bak_sets))// translate
{
memset(msg, , );
read(pcur -> m_recv, msg, ); dispatch_msg(plist,pcur, msg);
}
pcur = pcur ->m_next ;
}
void dispatch_msg(pCLIENT phead,pCLIENT pcur, char* msg)
{
while(phead)
{
if(phead!=pcur)
write(phead ->m_send, msg, strlen(msg));
phead = phead ->m_next ;
}
}
完整代码
server:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<sys/stat.h>
#include<sys/types.h>
#include<fcntl.h>
#include<sys/select.h>
#include<sys/time.h>
#define PIPE_PATH "/home/soso/Desktop/2-17/FIFO"
#define PIPE_NAME "server.fifo"
typedef struct tag
{
int m_id ;
int m_send;
int m_recv;
struct tag* m_next ;
}CLIENT, *pCLIENT; void dispatch_msg(pCLIENT phead,pCLIENT pcur, char* msg)
{
while(phead)
{
if(phead!=pcur)
write(phead ->m_send, msg, strlen(msg));
phead = phead ->m_next ;
}
} int main(int argc, char* argv[])
{
int fd_listen ; //文件句柄
char path_name[] = "" ;
char fifo_name[] ;
char msg[]; char client_stat[] = "";//客户端状态
int client_pid ; // 客户端进程ID
sprintf(path_name, "%s/%s", PIPE_PATH, PIPE_NAME);//路径名 = 路径/管道名 mkfifo(path_name, );// 创建管道 —— 专用于接收客户端上下线信息 printf("mkfifo over!\n"); fd_listen = open(path_name, O_RDONLY);
if(fd_listen == -)
{
printf("open server_fifo fail!\n");
exit();
} pCLIENT plist = NULL, pcur, pnew, ppre ; fd_set rd_sets, bak_sets; //读集合 和 备份读集合
FD_ZERO(&rd_sets);//初始化清空
FD_ZERO(&bak_sets);
FD_SET(fd_listen, &rd_sets); //将fd_listen句柄 加入到 读集合
while()
{
bak_sets = rd_sets ;//每次循环更新副本集合
printf("selecting...\n");
select(, &bak_sets, NULL, NULL, NULL);//监听集合 //1、监听fd_listen 管道文件句柄(专用于服务器接收客户端上下线信息)
if(FD_ISSET(fd_listen, &bak_sets)) //若监听到 fd_listen
{
memset(msg,, );
if( read(fd_listen, msg, ) == ) //读取管道信息;但没有客户端 write的时候,read的返回值是0
{
printf("no clients!\n");
continue ;
} memset(client_stat, , sizeof(client_stat));
sscanf(msg, "%d%s", &client_pid, client_stat);//信息格式:“client_pid client_stat\n”
if(strncmp("on", client_stat, ) == )//client on"pid on\n"
{// 读写是针对客户端而言的: pid_r.fifo(c_r - s_w) pid_w.fifo(c_w - s_r)
printf("client: %d on\n", client_pid);
//pid_r.fifo s_w
//构建管道名字符串
memset(fifo_name, , ) ;
sprintf(fifo_name, "%d_r.fifo", client_pid);
memset(path_name, , ) ;
sprintf(path_name, "%s/%s", PIPE_PATH, fifo_name); //新增链表节点
pnew = (pCLIENT)calloc(, sizeof(CLIENT));
pnew ->m_id = client_pid ;
printf("pid_r.fifo: %s\n", path_name);
pnew ->m_send = open(path_name, O_WRONLY);
printf("send_fd: %d\n", pnew ->m_send); //pid_w.fifo s_r
memset(fifo_name, , ) ;
sprintf(fifo_name, "%d_w.fifo", client_pid);
memset(path_name, , ) ;
sprintf(path_name, "%s/%s", PIPE_PATH, fifo_name); printf("pid_w.fifo: %s\n", path_name);
pnew ->m_recv = open(path_name, O_RDONLY);
printf("recv_fd: %d\n", pnew ->m_recv);
printf("open client fifo: %d, %d\n", pnew ->m_send, pnew ->m_recv); FD_SET(pnew ->m_recv, &rd_sets);//把pid_w.fifo 句柄 加到读集合中去 主要:更新的是读集合,而不是副本集合 pnew ->m_next = plist ; //插入链表
plist = pnew ; }else//client off "pid off\n"
{
printf("client: %d off\n", client_pid);
ppre = NULL ;//前驱指针
pcur = plist ;
while(pcur && pcur ->m_id != client_pid) //遍历到该客户端PID
{
ppre = pcur ;
pcur = pcur ->m_next ;
} if(pcur == NULL)
{
printf("not exist!\n");
continue ;
}else
{
//删除节点
if(ppre == NULL)
{
plist = pcur ->m_next ;
}else
{
ppre ->m_next = pcur ->m_next ;
} //关闭文件
close(pcur ->m_send) ;
close(pcur ->m_recv) ; //把 pcur ->m_recv 从读集合中删除
FD_CLR(pcur ->m_recv, &rd_sets); free(pcur); //释放内存
printf("clear ok !\n"); }
}
} //2、遍历链表,监听其他的管道文件(用于服务器和客户端通信)句柄
pcur = plist ;
while(pcur)
{
if(FD_ISSET(pcur ->m_recv, &bak_sets))// translate
{
memset(msg, , );
read(pcur -> m_recv, msg, ); dispatch_msg(plist,pcur, msg);
}
pcur = pcur ->m_next ;
}
}
return ;
}
client:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<sys/stat.h>
#include<sys/types.h>
#include<fcntl.h>
#include<sys/select.h>
#include<sys/time.h>
#define PIPE_PATH "/home/soso/Desktop/2-17/FIFO"
#define PIPE_NAME "server.fifo"
int main(int argc, char* argv[])
{
//1、向服务器通知上线下线信息 //打开上下线管道
int fd_server ;
char path_name[]="";
char fifo_name[] ;
char msg[] ="" ;
char fifo1[], fifo2[] ;
int fd_recv, fd_send ;
sprintf(path_name, "%s/%s", PIPE_PATH, PIPE_NAME); fd_server = open(path_name, O_WRONLY);
if(fd_server == -)
{
printf("open fail!\n");
exit() ;
} // 建造读写管道 pid_r.fifo pid_w.fifo
//
memset(fifo_name, , );
sprintf(fifo_name, "%u_r.fifo", getpid());
memset(path_name, , sizeof(path_name));
sprintf(path_name, "%s/%s", PIPE_PATH, fifo_name); strcpy(fifo1, path_name);
if(- == mkfifo(path_name, ) )
{
printf("mkfif fail: %s\n", path_name);
exit() ;
} printf("%s open\n", path_name); memset(fifo_name, , );
sprintf(fifo_name, "%u_w.fifo", getpid());
memset(path_name, , sizeof(path_name));
sprintf(path_name, "%s/%s", PIPE_PATH, fifo_name); strcpy(fifo2, path_name);
if(mkfifo(path_name, ) == - )
{ printf("mkfif fail: %s\n", path_name);
exit() ;
}
printf("%s open\n", path_name); printf("mkfifo over!\n"); //发送上线信息
sprintf(msg, "%u on\n", getpid());
printf("msg: %s\n", msg); write(fd_server, msg, strlen(msg)); //
memset(fifo_name, , );
sprintf(fifo_name, "%u_r.fifo", getpid());
memset(path_name, , sizeof(path_name));
sprintf(path_name, "%s/%s", PIPE_PATH, fifo_name); fd_recv = open(path_name, O_RDONLY); memset(fifo_name, , );
sprintf(fifo_name, "%u_w.fifo", getpid());
memset(path_name, , sizeof(path_name));
sprintf(path_name, "%s/%s", PIPE_PATH, fifo_name); fd_send = open(path_name, O_WRONLY); printf("fifo open %d %d\n", fd_send, fd_recv); fd_set rd_sets ;
FD_ZERO(&rd_sets);
while()
{
FD_SET(, &rd_sets);
FD_SET(fd_recv, &rd_sets); select(, &rd_sets, NULL, NULL, NULL); if(FD_ISSET(, &rd_sets))
{
memset(msg, , sizeof(msg)) ;
sprintf(msg, "from %u: ", getpid());
if(read(, msg + strlen(msg), - strlen(msg) ) == )
{
printf("off!\n");
memset(msg, , sizeof(msg));
sprintf(msg, "%d off\n", getpid());
write(fd_server, msg, strlen(msg)); close(fd_send);
close(fd_recv); unlink(fifo1);
unlink(fifo2);
break ;
}
write(fd_send, msg, strlen(msg)); }
if(FD_ISSET(fd_recv, &rd_sets))
{
memset(msg, , sizeof(msg)) ;
read(fd_recv, msg, );
write(, msg, strlen(msg));
}
}
}
1 server - n clients 模型实现(select)的更多相关文章
- linux下多路复用模型之Select模型
Linux关于并发网络分为Apache模型(Process per Connection (进程连接) ) 和TPC , 还有select模型,以及poll模型(一般是Epoll模型) Select模 ...
- Windows socket I/O模型 之 select(2)
在Windows socket I/O模型 之 select(1)中.我们仅仅是在console中简单的模拟了select的处理方法. 还有非常多特性不能改动.比方仅仅能写,不能读. 没使用线程.也 ...
- SQL Server 中心订阅模型(多发布单订阅)
原文:SQL Server 中心订阅模型(多发布单订阅) 大多数SQL Server 复制拓扑都是基于中心发布模型,它是由一个发布复制到一个或者多个订阅.另一个复制模型是中心订阅模型,它使用事务复制由 ...
- [编织消息框架][网络IO模型]NIO(select and poll)
上面测试论证系统内核在read data时会阻塞,如果我们在把第一个阶段解决掉那么性能就会提高 NIO 编程 JDK 1.4中的java.nio.*包中引入新的Java I/O库,其目的是提高速度.实 ...
- Socket I/O模型之select模型
socket网络编程中有多种常见的I/O模型: 1.blocking阻塞 2.nonblocking非阻塞 3.I/O multiplexing复用 4.signal driven 5.asynchr ...
- 1高并发server:多路IO之select
1 select A:select能监听的文件描写叙述符个数受限于FD_SETSIZE,一般为1024.单纯改变进程打开 的文件描写叙述符个数并不能改变select监听文件个数 B:解决1024 ...
- IO模型,非阻塞IO模型,select实现多路复用
1. IO阻塞模型 IO问题: 输入输出 我要一个用户名用来执行登陆操作,问题用户名需要用户输入,输入需要耗时, 如果输入没有完成,后续逻辑无法继续,所以默认的处理方式就是 等 将当前进程阻塞住,切换 ...
- socket阻塞与非阻塞,同步与异步、I/O模型,select与poll、epoll比较
1. 概念理解 在进行网络编程时,我们常常见到同步(Sync)/异步(Async),阻塞(Block)/非阻塞(Unblock)四种调用方式: 同步/异步主要针对C端: 同步: 所谓同步,就 ...
- Hadoop的Server及其线程模型分析
早期的一篇文章,针对Hadoop 2.6.0. 一.Listener Listener线程,当Server处于运行状态时,其负责监听来自客户端的连接,并使用Select模式处理Accept事件. 同时 ...
随机推荐
- 创建FILE GEODATABASE 和栅格目录及向栅格目录中添加影像
using System;using System.IO;using ESRI.ArcGIS.DataSourcesGDB;using ESRI.ArcGIS.DataSourcesRaster;us ...
- customerized convert from field type to DB field's type
@LastModifiedDate @Convert(converter = LocalDateTime2TimestampConverter.class) @Slf4j public class L ...
- 关于同步VSS服务器上的代码发生Eclipse里面的项目全部不见了
有次在同步VSS服务器上的代码的时候突然发生了错误(同步的代码的项目竟然消失了)....如下图 Could not open the editor: The file does not exist. ...
- saltstack实战2--远程执行之模块(Modules)
本来转自http://www.cnblogs.com/MacoLee/p/5753640.html 版权归原作者所有 说明 salt '*' sys.list_modules #列出当前版本支持的模 ...
- Socket编程初探
一.什么是Socket? 通常也称作"套接字",用于描述IP地址和端口,是一个通信链的句柄.在Internet上的主机一般运行了多个服务软件,同时提供几种服务.每种服务都打开一个S ...
- Bootstrap之Footer页尾布局(2015年05月28日)
直接上页尾部分的代码: <!--采用container-fluid,使得整个页尾的宽度为100%,并设置它的背景色--><footer class="container-f ...
- 媒体查询的应用以及在css3中的变革
CSS一直都支持设置与媒体相关联的样式表.它们可以适应不同媒体类型的显示.例如,文档在屏幕显示时使用sans-serif字体,在打印时则使用serif字体.screen和print是两种预定义的媒体类 ...
- 【转载】Apache Kafka:下一代分布式消息系统
http://www.infoq.com/cn/articles/kafka-analysis-part-1 Kafka是由LinkedIn开发的一个分布式的消息系统,使用Scala编写,它以可水平扩 ...
- vs转eclipse之工具快速上手篇
eclipse工具下载 首先说明,本篇内容适用于刚开始学java的同学,老手大牛等可以路过. 不得不说vs确实很强大,常用的都在安装包里集成了,几乎可以一键安装,直接使用,操作起来非常方便. ecli ...
- virtualbox下Centos6.5桥接模式上网配置方法
记得之前安装linux配置桥接模式,马上就能上网的,虚拟机上重装了系统后就不能上网了,折腾了好几次,不停地安装系统,原来应该怎么配置,我真是完全忘记了,年纪大了脑子不好使了!这里记录一下,免得下次再忘 ...