linux mkfif() 错误,1 server - n clients 模型实现(select)

拓扑结构:

7c50663bbc5b400a0e76839e779aa07e.png

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

#include

#include

#include

#include

#include

#include

#include

#include

#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

#include

#include

#include

#include

#include

#include

#include

#include

#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));

}

}

}

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事件. 同时 ...

随机推荐

UITableView使用

@多组数据和多组cell 1根数组内含数组时,只可以开启一组cell,内含多个数组时,可以开启多组cell 2多个数组开启多个cell时,既可以竖向开启,也可以横向里层开启,就好比猜题的模式 @注意点 ...

CSS - Transform(Translate) abnormal shadow in firefox

问题:当在Firefox中实现动画translate时,会出现虚影的状况: 经查找相关的解决方法,父容器添加样式:outline: 1px solid transparent;//即可解决问题. 但不 ...

【转】MSSQL获取指定表的列名信息,描述,数据类型,长度

/* --作用:根据特定的表名查询出字段,以及描述,数据类型,长度,精度,是否自增,是否为空等信息 --作者:wonder QQ:37036846 QQ群:.NET顶级精英群 ID:124766907 ...

AndroidStudio1.4 manifest 中注册Activity时的错误提示解决办法

问题截图如下: 解决办法截图如下: 1: File->setting->Editor->Language Injections到如下界面 2:双击右侧选中的Item进入编辑界面 3: ...

Struts 上下文

Struts  上下文 ActionContext .ServletActionContext 是继承关系  ActionContext  ActionContext context = Action ...

CodeForces 482C Game with Strings

意甲冠军: n一定长度m串  隐藏的字符串相等的概率  然后,你猜怎么着玩家隐藏的字符串  的询问字符串的一个位置  再不断的知道一些位置后  游戏者就能够确定藏起来的串是什么  问  游戏者的期望步 ...

Coffee

Coffee 从接触Spring 到现在已经差不多2年多了,期间用它做过几个项目,从个人使用角度来说,Spring无疑是非常的成熟和方便的,但是知道怎么用,却不知道原理是码农和攻城师的区别,现在准备自 ...

Openjudge-计算概论(A)-鸡兔同笼

描述一个笼子里面关了鸡和兔子(鸡有2只脚,兔子有4只脚,没有例外).已经知道了笼子里面脚的总数a,问笼子里面至少有多少只动物,至多有多少只动物输入第1行是测试数据的组数n,后面跟着n行输入.每组测试数 ...

【转】Java中super和this的几种用法与区别

1. 子类的构造函数如果要引用super的话,必须把super放在函数的首位.   class Base {   Base() {   System.out.println("Base&qu ...

JS第二部分--DOM文档对象模型

一.DOM的概念 二.DOM可以做什么 三.DOM对象的获取 四.事件的介绍 五.DOM节点标签样式属性的操作 六.DOM节点对象对值的操作 七.DOM节点-标签属性的操作(例如id class sr ...

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值