从Unix System V 开始,系统提供有名管道和无名管道两种数据通信方式。
无名管道为建立管道的进程和子进程提供一种以比特流方式传送信息的通信管道。在逻辑上可以看作是管道文件,在物理上由文件系统的高速缓冲区构成,而很少起用外设。发送进程利用文件系统的 系统调用write (fd[1],buf,size)把buf中长度为size的字符送入管道入口fd[1],接受进程则使用系统调用read(fd[0],buf,size)从管道出口读取信息到buf。管道按照先进先出传送消息。只能单向传送。
命名管道程序设计的实现
1. 命名管道Server和Client间通信的实现流程
(1)建立连接:服务端通过函数CreateNamedPipe创建一个 命名管道的实例并返回用于今后操作的句柄,或为已存在的管道创建新的实例。如果在已定义超时值变为零以前,有一个实例管道可以使用,则创建成功并返回管道句柄,并用以侦听来自客户端的连接请求,该功能通过ConnectNamedPipe函数实现。
另一方面,客户端通过函数 WaitNamedPipe使服务进程等待来自客户的实例连接,如果在超时值变为零以前,有一个管道可以为连接使用,则WaitNamedPipe将返回True,并通过调用CreateFile或 CallNamedPipe来呼叫对 服务端的连接。此时服务端将接受客户端的连接请求,成功建立连接,服务端ConnectNamedPipe返回True,客户端CreateFile将返回一指向管道文件的句柄。
从时序上讲,首先是客户端通过 WaitNamedPipe使服务端的CreateFile在限时时间内创建实例成功,然后双方通过ConnectNamedPipe和CreateFile成功连接,并返回用以通信的 文件句柄,此时双方即可进行通信。
(2)通信实现:建立连接之后,客户端与服务器端即可通过ReadFile和WriteFile,利用得到的管道 文件句柄,彼此间进行信息交换。
(3)连接终止:当客户端与服务端的通信结束,或由于某种原因一方需要断开时,客户端应调用CloseFile,而服务端应接着调用DisconnectNamedPipe。当然服务端亦可通过单方面调用DisconnectNamedPipe终止连接。最后应调用函数CloseHandle来关闭该管道。
创建命名管道
#include
#include
#include
#include
int main()
{
int ret = mkfifo("/home/mkfifo", 0777);
if (ret == -1)
{
perror ("mkfifo");
return -1;
}
return 0;
}
命名管道间通信 12
1
#include
#include
#include
#include
#include
#include
#define SIZE 1024
int main()
{
int fd = open("/home/mkfifo", O_WRONLY);
if (fd== -1)
{
perror ("mkfifo");
return -1;
}
char buf[SIZE];
while (1)
{
fgets (buf, SIZE, stdin);
write (fd, buf, strlen(buf));
}
return 0;
}
2
#include
#include
#include
#include
#include
#include
#define SIZE 1024
int main()
{
int fd = open("/home/mkfifo", O_RDWR);
if (fd == -1)
{
perror ("mkfifo");
return -1;
}
char buf[SIZE];
while (1)
{
int ret = read (fd, buf, SIZE);
buf[ret] = '\0';
printf ("读到 %d 字节: %s\n", ret, buf);
}
return 0;
}
实现共享内存 12
1
#include
#include
#include
#include
#include
typedef struct _shm
{
int flag;
char msg[256];
}SHM;
int main()
{
// 1、创建或者获取一个共享内存
int shmid = shmget((key_t)1234, sizeof(SHM), 0666 | IPC_CREAT);
if (shmid == -1)
{
perror ("shmget");
return -1;
}
// 2、将共享内存映射到当前的进程空间
SHM* pshm = (SHM*)shmat(shmid, NULL, 0);
if(pshm == (SHM*)-1)
{
perror ("shmat");
return -1;
}
strcpy (pshm->msg, "hello");
// 解除共享内存映射,解除是值当前进程不能再使用共享内存
shmdt(pshm);
shmctl(shmid, IPC_RMID, NULL);
return 0;
}
2
#include
#include
#include
#include
#include
typedef struct _shm
{
int flag;
char msg[256];
}SHM;
int main()
{
// 1、创建或者获取一个共享内存
int shmid = shmget((key_t)1234, sizeof(SHM), 0666 | IPC_CREAT);
if (shmid == -1)
{
perror ("shmget");
return -1;
}
sleep(10);
// 2、将共享内存映射到当前的进程空间
SHM* pshm = (SHM*)shmat(shmid, NULL, 0);
if(pshm == (SHM*)-1)
{
perror ("shmat");
return -1;
}
printf ("%s\n", pshm->msg);
return 0;
}
多个终端同时运行,基本可以实现售票系统,有漏洞,需要靠信号来控制,信号的文章有完全的
#include
#include
#include
#include
#include
#include
typedef struct _shm
{
int flag;
int ticket;
}SHM;
void sellTicket(SHM* pshm)
{
while (1)
{
int time = rand() % 10 + 1;
usleep(time*100000);
if (pshm->flag == 1)
{
pshm->flag = 0;
if (pshm->ticket == 0) // 票卖完
{
pshm->flag = 1;
break;
}
printf ("卖掉一张票,座位号是 : %d\n", pshm->ticket);
pshm->ticket--;
pshm->flag = 1;
}
}
}
int main(int argc, char **argv)
{
srand ((unsigned int)time(NULL));
// 1、创建或者获取一个共享内存
int shmid = shmget((key_t)1234, sizeof(SHM), 0666 | IPC_CREAT);
if (shmid == -1)
{
perror ("shmget");
return -1;
}
// 2、将共享内存映射到当前的进程空间
SHM* pshm = (SHM*)shmat(shmid, NULL, 0);
if(pshm == (SHM*)-1)
{
perror ("shmat");
return -1;
}
// 如果命令行参数等于2 负责对共享内存进行初始化
if (argc == 2)
{
pshm->flag = 1;
pshm->ticket = 100;
}
// 开始卖票
sellTicket(pshm);
// 如果命令行参数等于2 负责对共享内存进行删除
if (argc == 2)
{
shmctl(shmid, IPC_RMID, NULL);
}
return 0;
}
注意事项
4. 命名管道 服务端可以通过新创建的管道句柄或已被连接过其他客户的管道句柄来使用ConnectNamedPipe函数,但在连接新的客户端之前,服务端必须用函数DisconnectNamedPipe切断之前的客户句柄,否则ConnectNamedPipe 将会返回False。
5.阻塞模式,这种模式仅对“字节传输管道"操作有效,并且要求客户端与 服务端不在同一机器上。如果用这种模式,则只有当函数通过网络向远端计算机管道缓冲器写数据成功时,才能有效返回。如果不用这种模式,系统会运行缺省方式以提高网络的工作效率。
6.用户必须用FILE—CREATE—PIPE—INSTANCE 来访问 命名管道对象。新的 命名管道建立后,来自安全参数的 访问控制列表定义了访问该命名管道的权限。所有 命名管道实例必须使用统一的管道传输方式、管道模式等参数。客户端未启动,管道 服务端不能执行阻塞读操作,否则会发生空等的 阻塞状态。当最后的 命名管道实例的最后一个句柄被关闭时,就应该删除该命名管道。.