Linux——基于共享内存 消息队列和基于Socket的进程间的通信

一.基于内存的通信

以下几种通信方式编程步骤都比较固定 按部就班就可以

    0.一组内核内存的工具

        ipcs  
        ipcs -m 共享内存
        ipcs -q 共享消息队列
        ipcs -s    共享信号                    
        ipcrm -m shmid(共享内存id) 删除共享内存(一个进程根据一个key创建共享内存并退出后,如果没有删除共享内存那么再次用此key创建共享内存时会创建失败,一个key只能创建一次,但删除共享内存后,再用这个key创建共享内存得到的shmid可能不一样)

    1.普通的父子进程之间的匿名内存共享映射    
    2.内核共享内存
        编程模型
        2.1.创建共享内存,得到一个ID  shmget(shered memery get)
        2.2.把ID影射成虚拟地址(挂载)  shmat
        2.3.使用虚拟地址访问内核共享内存 使用任何内存函数与运算符号                                                
        2.4.卸载虚拟地址 shmdt
        2.5.删除共享内存 shctl(修改/获取共享内存的属性)
            
        共享内存的属性    
                
案例:
    A.创建共享内存,并且每隔几秒中修改内存数据。然后用B进程访问
    1.创建共享内存
    int shmget(key_t key,//为什么需要key 使用这个数能找到共享内存
        int size,//共享内存大小
        int flags//共享内存的属性与权限
            )
        为什么要key_t:
                约定创建与访问的是同一个共享内存。
        第三个参数:
            方式|权限
            方式:创建 IPC_CREAT  IPC_EXCL
            打开:0
        常见的两种方式:
        创建:IPC_CREAT|IPC_EXCL | 0666;
        打开:0
                            
    返回:
            成功返回共享内存ID

            失败返回-1

       

    B.根据ID得到共享,并且访问内存数据。
            void shmat(int id,
                void *startaddr,//0:系统指定首地址
                int flags)//挂载方式,建议0,可以使用IPC_RDONLY
    C.删除
            int shmctl(int id,//被操作的共享内存ID
                    int how,//操作方式:一共三种操作
                    struct shmid_ds*ds)//共享内存属性
            how:
            IPC_STAT
            IPC_SET

            IPC_RMID



shmA.c

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <sys/shm.h>
#include <sys/ipc.h>
key_t key;
int shmid;
int *p;
int i=0;
void deal(int s)
{
	if(s==SIGINT)
	{
		//4.卸载共享内存shmdt
		shmdt(p);
		//5.删除共享内存shctl
		shmctl(shmid,IPC_RMID,0);
		exit(0);
	}
}
main()
{
	
	signal(SIGINT,deal);
	//1.创建共享内存shmget 就像在内存中创建了个文件
	key=ftok(".",255);//file to key
	if(key==-1) printf("ftok error:%m\n"),exit(-1);
	
	shmid=shmget(key,4,IPC_CREAT|IPC_EXCL|0666);
	if(shmid==-1) printf("get error:%m\n"),exit(-1);
	//2.挂载共享内存shmat
	p=shmat(shmid,0,0);
	if(p==(int*)-1) printf("at error:%m\n"),exit(-1);
	//3.访问共享内存
	while(1)
	{
		*p=i;
		sleep(1);
		i++;
	 }
	
}

shmB.c

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <sys/shm.h>
#include <sys/ipc.h>
key_t key;
int shmid;
int *p;
void deal(int s)
{
	if(s==2)
	{
		//4.卸载共享内存shmdt
		shmdt(p);
		exit(0);
	}
}
main()
{
	signal(SIGINT,deal);	
	//1.创建共享内存shmget
	key=ftok(".",255);//把目录转化为一个整数
	if(key==-1) printf("ftok error:%m\n"),exit(-1);
	
	shmid=shmget(key,4,0);
	if(shmid==-1) printf("get error:%m\n"),exit(-1);
	//2.挂载共享内存shmat
	p=shmat(shmid,0,0);
	if(p==(int*)-1) printf("at error:%m\n"),exit(-1);
	//3.访问共享内存
	while(1)
	{		
		sleep(1);
		printf("%d\n",*p);
	}
	
	
}

    3.消息队列(有序)本质也是共享内存 只是结构大小是内核控制的
        编程模型:
            3.1.创建共享队列/得到队列msgget
            3.2.使用队列(发送消息msgsnd/接收消息msgrcv)
            3.3.删除队列msgctl
案例:
    A:创建共享队列
            int msgget(key_t,int);                        
    B:发送消息
            int msgsnd(
                int id,//消息队列ID
                const void *msg,//要发送消息
                size_t len,//消息的长度
                int flags//发送消息的方式,建议为0
                );
            返回:
                    -1:失败
                        0:成功    
            第二个参数的消息有固定的格式
                        4字节:表示消息的类型
                        若干字节:消息内容。
            第三个参数:

                        消息的大小,不包含类型的4个字节

msgA.c//发送消息队列

#include <unistd.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct msgbuf
{
	long type;
	char data[32];
};
main()
{
	key_t key;
	int msgid;
	int i;
	struct msgbuf msg;
	
	//1创建消息队列
	key=ftok(".",200);
	if(key==-1) printf("ftok err:%m\n"),exit(-1);
	
	msgid=msgget(key,/*IPC_CREAT|IPC_EXCL|0666*/0);
	if(msgid==-1)printf("get err:%m\n"),exit(-1);
	
		
	//2//3构造消息和发送消息
	for(i=1;i<=10;i++)
	{
		bzero(msg.data,sizeof(msg.data));
		msg.type=1;
		sprintf(msg.data,"MessageI:%d",i);
		msgsnd(msgid,&msg,sizeof(msg.data),0);
	}
	for(i=1;i<=10;i++)
	{
		bzero(msg.data,sizeof(msg.data));
		msg.type=2;
		sprintf(msg.data,"MessageII:%d",i);
		
		msgsnd(msgid,&msg,sizeof(msg.data),0);
	}
	for(i=1;i<=10;i++)
	{
		bzero(msg.data,sizeof(msg.data));
		msg.type=1;
		sprintf(msg.data,"MessageI:%d",i);
		msgsnd(msgid,&msg,sizeof(msg.data),0);
	}
	//4删除队列
	//msgctl(msgid,IPC_RMID,0);
}

msgB.c//接收消息队列

#include <unistd.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct msgbuf
{
	long type;
	char data[32];
};
main()
{
	key_t key;
	int msgid;
	int i;
	struct msgbuf msg;
	//1得到消息队列
	key=ftok(".",200);
	if(key==-1) printf("ftok err:%m\n"),exit(-1);
	
	msgid=msgget(key,0);
	if(msgid==-1)printf("get err:%m\n"),exit(-1);
	//2构造消息
		
	//3接收消息
	while(1)
	{
		sleep(1);
		bzero(&msg,sizeof(msg));
		msg.type=1;
		msgrcv(msgid,&msg,sizeof(msg.data),1,0);
		printf("%s\n",msg.data);
	}
	
}



二.基于socket文件的IPC

    socket文件的通信方式,比较重要,原因:网络采用这种通信模型。
    两种模型:
                对等模型
                C/S模型

    1.对等模型:

基于本地文件   给我的感觉有点像上一篇的基于普通文件的IPC 一个往文件写 一个读

接收端

        1.建立socket:socket
            int socket(
                int domain,//地址族的类型AF_UNIX AF_INET
                int type,//支持的数据格式:流SOCK_STREAM/报文SOCK_DGRAM

                int protocol);//支持的协议,建议为0,采用默认协议 地址格式和数据格式指定后

                                                                          大部分情况下协议就确定了。

            返回值:
                成功则在内存映射内存并返回文件描述符号。
                失败返回-1;
        2.绑定在地址上(文件目录地址)URL(Universe Resource Location)
            协议://路径/文件名
            file:///usr/bin/ls

            http://192.168.0.72/index.php

            ftp://......


            struct sockaddr;
            struct sockaddr_un;un=unix文件
            struct sockaddr_in;in=internet网卡
            int bind(int fd,//socket描述符号 然后网描述副上写数据就写道了相应的文件或网卡 类似于映射

                    struct sockaddr*addr,//绑定地址 这是一个通用地址

                                                      用的时候用具体地址 网卡sockaddr_in或本地文件sockaddr_un

                    socklen_t size);//地址长度
                    
                
        3.接收数据
                read/recv/recvfrom

        4.关闭socket

socketA.c

#include <sys/socket.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <linux/un.h>

main()
{
	int fd;
	int r;
	char buf[200];
	//1.建立socket 即在内存中建立缓冲
	fd=socket(AF_UNIX,SOCK_DGRAM,0);
	if(fd==-1) printf("socket err:%m\n"),exit(-1);
	printf("socket成功!\n");
	//2.构造本地文件地址close(fd);
	//6.删除socket文件
	unlink("my.sock");//<span style="color:#FF0000;">否则当文件存在时会bind失败</span>
	
	struct sockaddr_un addr={0};
	addr.sun_family=AF_UNIX;
	memcpy(addr.sun_path,"my.sock",
					strlen("my.sock"));
	
	//3.把socket绑定在地址上
	r=bind(fd,(struct sockaddr*)&addr,sizeof(addr));
	if(r==-1) printf("bind err:%m\n"),exit(-1);
	printf("地址绑定成功!\n");
	
	//4.接收数据
	while(1)
	{
		bzero(buf,sizeof(buf));
		r=read(fd,buf,sizeof(buf));
		buf[r]=0;
		printf("%s\n",buf);
	}	
	
	//5.关闭
	close(fd);
	//6.删除socket文件
	unlink("my.sock");
	
}



        
   发送端            
        1.建立socket:socket
        2.连接到目标:connect(可选)                
        3.发送数据:write/send/sendto(不必要连接)

        4.关闭close

socketB.c

#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <linux/un.h>
#include <string.h>
#include <unistd.h>
main()
{
	int fd;
	int r;
	char buf[100];
	struct sockaddr_un addr={0};
	//1.建立socket
	fd=socket(AF_UNIX,SOCK_DGRAM,0);
	//fd=socket(AF_UNIX,SOCK_STREAM,0);
	//2.连接到指定的地址
	addr.sun_family=AF_UNIX;
	memcpy(addr.sun_path,"my.sock",
			strlen("my.sock"));
	r=connect(fd,(struct sockaddr*)&addr,
			sizeof(addr));
	//3.发送数据
	while(1)
	{
		write(fd,"Hello!MaomaoYu!",
			strlen("Hello!MaomaoYu!"));
		//read(fd,buf,100);
		//printf("%s\n",buf);
		sleep(1);	
	}
	//4.关闭
	close(fd);
}


基于ip

ipA.c

#include <sys/socket.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <netinet/in.h>
#include <arpa/inet.h>

main()
{
	int fd;
	int r;
	char buf[200];
	//1.建立socket
	//2
	fd=socket(AF_INET,SOCK_DGRAM,0);
	if(fd==-1) printf("socket err:%m\n"),exit(-1);
	printf("socket success!\n");
	//2.构造本地文件地址
	//3.
	struct sockaddr_in addr={0};
	addr.sin_family=AF_INET;
	addr.sin_port=htons(9999);
	addr.sin_addr.s_addr=inet_addr("192.168.1.116");
	//3.把socket绑定在地址上
	r=bind(fd,(struct sockaddr*)&addr,sizeof(addr));
	if(r==-1) printf("bind err:%m\n"),exit(-1);
	printf("bind success!\n");
	
	//4.接收数据
	while(1)
	{
		bzero(buf,sizeof(buf));
		r=read(fd,buf,sizeof(buf));
		buf[r]=0;
		printf("%s\n",buf);
	}	
	
	//5.关闭
	close(fd);
	//6.删除socket文件
	unlink("my.sock");
	
}

ipB.c

#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <string.h>
#include <unistd.h>
//1
#include <netinet/in.h>
#include <arpa/inet.h>
main()
{
	int fd;
	int r;
	//2
	struct sockaddr_in addr={0};
	//1.建立socket
	//3
	fd=socket(AF_INET,SOCK_DGRAM,0);
	//2.连接到指定的地址
	//4
	addr.sin_family=AF_INET;
	addr.sin_port=htons(9999);
	addr.sin_addr.s_addr
		=inet_addr("192.168.1.116");
	
	r=connect(fd,(struct sockaddr*)&addr,
			sizeof(addr));
	//3.发送数据
	write(fd,"Hello!Maomaochong!",
		strlen("Hello!Maomaochong!"));
	//4.关闭
	close(fd);
}


   2.C/S模型

        Server            Client
        建立socket:socket    建立socket:socket
        绑定地址:bind        建立连接:connect
        监听:listen            
        接收:accept                
        read/write        read/write
        close            close
        
        int listen(int fd,int num);
                0:监听成功
                -1:失败
        int accept(int fd,
                struct sockaddr*addr,//返回连接着的地址
                socklen_t* len)//接收返回地址的缓冲长度
        返回:    
                -1:接收失败
                >=0:对应客户的文件描述符号        

总结:
            共享内存
            共享队列
            socket文件通信
课堂练习:
            CS模型代码
            CS模型把socket文件替换成IP地址
课后作业:
            模仿课堂案例独立完成                        
                    1.共享内存
                    2.共享队列
                    3.socket对等模型
                    4.socket的CS模型
                    

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值