C++学习:第六章Linux高级编程 - (八)共享内存、消息队列、Socket、Socket编程模型

一、 基于文件的通信

  1. 普通文件(io/mmap)
  2. 有名管道文件
  3. 匿名管道
  4. Socket

 

二、 基于内存的通信

  1. 一组内核内存的工具

ipcs //查看以下三个

ipcs -m //查看共享内存

ipcs -q //查看共享队列

ipcs -s //查看共享信号量

ipcrm -q 编号ID //删除。。。

  2. 普通的父子进程之间的匿名内存共享影射

  3. 内核共享内存

编程模型:

3.1 创建共享内存,得到一个ID  shmget

3.2 把ID影射成虚拟地址(挂载)  shmat

3.3 使用虚拟地址访问内核共享内存,使用任何内存函数与运算符号

str** mem** += ++ [] ->

3.4 卸载虚拟地址 shmdt

3.5 删除共享内存 shctl(修改/获取共享内存的属性)

共享内存的属性:

运行 ipcs -m 

key 共享内存的ID 共享内存的创建者 共享内存的参数(读写执行) 共享内存的大小 有几个内存挂在该共享内存上 共享内存的状态

案例:

A. 创建共享内存,并且修改内存数据。

  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 //删除

 文件A,负责写数据

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

}

文件B,负责读数据

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

 

  4. 内核共享队列(有序)

编程模型:

4.1 创建共享队列/得到队列msgget

4.2 使用队列(发送消息msgsnd/接收消息msgrcv)

4.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个字节

 

文件A,负责写数据

#include <unistd.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

//2构造消息

struct msgbuf
{
    long type;
    char data[32];//任意大小
};

main()
{
    key_t key;
    int msgid;
    int i;
    struct msgbuf msg;//2构造消息

    //1创建消息队列
    key=ftok(".",200);
    if(key==-1) printf("ftok err:%m\n"),exit(-1);

    msgid=msgget(key,IPC_CREAT|IPC_EXCL|0666);
    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;//这个值必须大于0
        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);
    }

    //4删除队列
    //msgctl(msgid,IPC_RMID,0);
}

 

文件B,负责读数据

#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)
    {
        bzero(&msg,sizeof(msg));
        msg.type=2;
        msgrcv(msgid,&msg,sizeof(msg.data),2,0);
        printf("%s\n",msg.data);
    }
}

 

三、 基于socket文件的IPC

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

两种模型:

对等模型

C/S模型(主客模型)

  1. 对等模型:

接收数据模型

1.1 建立socket:socket

int socket(

int domain, //地址族的类型AF_UNIX AF_INET

int type, //支持的数据格式:流SOCK_STREAM/报文SOCK_DGRAM

int protocol); //支持的协议,建议为0

返回值:

成功返回文件描述符号。

失败返回-1;

1.2 绑定在地址上(文件目录地址)URL(Universe Resource Location)

协议://路径/文件名

 

file:///usr/bin/ls

http://192.168.0.72/index.php

 

struct sockaddr;

struct sockaddr_un; un=unix

struct sockaddr_in; in=internet

 

int bind(int fd, //socket描述符号

struct sockaddr*addr, //绑定地址

socklen_t size); //地址长度

1.3 接收数据

read/recv/recvfrom

1.4 关闭socket

 

发送数据模型

  1. 建立socket:socket
  2. 连接到目标:connect(可选)
  3. 发送数据:write/send/sendto
  4. 关闭close

 

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地址

 

ipA

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

//1.
#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成功!\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.180.92");

    //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");

}

 

ipB

#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.180.92");
    r=connect(fd,(struct sockaddr*)&addr,
    sizeof(addr));

    //3.发送数据
    write(fd,"Hello!Maomaochong!",strlen("Hello!Maomaochong!"));

    //4.关闭
    close(fd);dd

}

 

server

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


main()
{
    int sfd;
    int cfd;
    struct sockaddr_un addr;
    int r;
    char buf[100];

    //1.建立socket
    sfd=socket(AF_UNIX,SOCK_STREAM,0);
    if(sfd==-1) printf("socket err:%m\n"),exit(-1);
    printf("建立socket成功!\n");

    //2.绑定地址
    bzero(&addr,sizeof(addr));
    addr.sun_family=AF_UNIX;
    memcpy(addr.sun_path,"cs.sock",strlen("cs.sock")+1);
    r=bind(sfd,(struct sockaddr*)&addr,sizeof(addr));
    if(r==-1) printf("bind err:%m\n"),exit(-1);
    printf("bind成功!\n");


    //3.监听
    r=listen(sfd,10);
    if(r==-1) printf("listen err:%m\n"),exit(-1);
    printf("listen成功!\n");


    //4.接收客户
    cfd=accept(sfd,0,0);
    if(cfd==-1) printf("accept err:%m\n"),exit(-1);
    printf("建立连接者的状态成功!\n");

    //5.接收这个客户的数据
    while(1)
    {
        r=read(cfd,buf,sizeof(buf));
        if(r==0)
        {
            printf("连接者退出");
            break;
        }

        if(r==-1)
        {
            printf("scoket故障!\n");
            break;
        }

        buf[r]=0;
        printf("::%s\n",buf);
        write(cfd,"Hi",2);
    }

    //6.关闭客户
    close(cfd);

    //7.关闭整个socket
    close(sfd);

}

 

socketA

#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.构造本地文件地址
    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");
}

 

socketB

#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,"cs.sock",strlen("cs.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);
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值