一.基于文件的通信
1.普通文件(io/mmap)
2.有名管道文件
3.匿名管道
4.Socket
二.基于内存的通信
0.一组内核内存的工具
ipcs
ipcs -m
ipcs -q
ipcs -s
ipcrm -q 编号ID
1.普通的父子进程之间的匿名内存共享映射
2.内核共享内存
编程模型
2.1.创建共享内存,得到一个ID shmget
2.2.把ID映射成虚拟地址(把内核中的缓存连接到用户进程) shmat
2.3.使用虚拟地址访问内核共享内存 使用任何内存函数与运算符号
2.4.卸载虚拟地址 shmdt
2.5.删除共享内存 shctl(修改/获取共享内存的属性)
共享内存的属性
案例:
A.创建共享内存,并且修改内存数据。
1.创建共享内存
int shmget(key_t key,
//
为什么需要key
int size, // 共享内存大小
int flags // 共享内存的属性与权限
)
为什么要key_t:
int size, // 共享内存大小
int flags // 共享内存的属性与权限
)
约定创建与访问的是同一个共享内存。
第三个参数:
方式|权限
方式:创建 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.删除
void *startaddr, // 0:系统指定首地址
int flags) // 挂载方式,建议0,可以使用IPC_RDONLY
int shmctl(
int id,
//
被操作的共享内存ID
int how, // 操作方式:一共三种操作
struct shmid_ds*ds) // 共享内存属性
how:
int how, // 操作方式:一共三种操作
struct shmid_ds*ds) // 共享内存属性
IPC_STAT
IPC_SET
IPC_RMID
#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++;
}
}
#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.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或者IPC_NOWAIT,建议为0
);
返回:
int id, // 消息队列ID
const void *msg, // 要发送消息
size_t len, // 消息的长度
int flags // 发送消息的方式0或者IPC_NOWAIT,建议为0
);
-1:失败
0:成功
第二个参数的消息有固定的格式
4字节:表示消息的类型
若干字节:消息内容。
第三个参数:
消息的大小,不包含类型的4个字节
#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 /* 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;
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);
}
#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文件的通信方式,比较重要,原因:网络采用这种通信模型。
两种模型:
对等模型
C/S模型
1.对等模型:
1.建立socket:socket
int socket(
int domain, // 地址族的类型AF_UNIX AF_INET
int type, // 支持的数据格式:流SOCK_STREAM/报文SOCK_DGRAM
int protocol); // 支持的协议,建议为0
返回值:
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
struct sockaddr;
struct sockaddr_un;un=unix
struct sockaddr_in;in=internet
int bind(
int fd,
//
socket描述符号
struct sockaddr*addr, // 绑定地址
socklen_t size); // 地址长度
3.接收数据
struct sockaddr*addr, // 绑定地址
socklen_t size); // 地址长度
read/recv/recvfrom
4.关闭socket
#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");
}
2.连接到目标:connect(可选)
3.发送数据:write/send/sendto
4.关闭close
#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);
// 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!"));
sleep(1);
}
// 4.关闭
close(fd);
}
#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");
}
#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 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) // 接收返回地址的缓冲长度
返回:
struct sockaddr*addr, // 返回连接着的地址
socklen_t* len) // 接收返回地址的缓冲长度
-1:接收失败
>=0:对应客户的文件描述符号
#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);
}
#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);
}
总结:
共享内存
共享队列
socket文件通信
课堂练习:
CS模型代码
CS模型把socket文件替换成IP地址
课后作业:
模仿课堂案例独立完成
1.共享内存
2.共享队列
3.socket对等模型
4.socket的CS模型