1. 回顾:
一个进程控制另外一个进程.
逻辑变量+pause/sleep+信号
2. 信号量(semaphore)信号灯
三个数据:红灯/绿灯/黄灯
60 90 10
信号量是共享内存整数数组,根据需要定义指定的数组长度
信号量就是根据数组中的值,决定阻塞还是解除阻塞
3. 编程
3.1 创建或者得到信号量 semget
3.2 初始化信号量中指定下标的值 semctl
3.3 根据信号量阻塞或者解除阻塞 semop
3.4 删除信号量
案例:
A: B:
创建信号量 得到信号量
初始化信号量
根据信号量阻塞 解除阻塞
删除信号量
semget函数说明
int semget(key_t key,int nums, //信号量数组个数int flags); //信号量的创建标记//创建IPC_CREAT|IPC_EXCL|0666//打开0返回: -1:失败>=0:成功返回信号量的ID
int semop(int semid, //信号量IDstruct sembuf *op, //对信号量的操作,操作可以是数组多个size_t nums, //第二个参数的个数);返回:-1:时失败0:成功
int semctl(int semid, //对IPC_RMID无意义int nums, //信号量数组下标,第一个变量为0,以此类推 int cmd, //SETVAL IPC_RMID...); //对IPC_RMID无意义
struct sembuf
{int sem_num;//下标int sem_op;int sem_flg;//建议为0.
}
sem_op:
前提条件信号量是unsigned short int;
不能<0.
-&#xff1a;够减&#xff0c;则semop马上返回&#xff0c;不够减&#xff0c;则阻塞.
&#43;&#xff1a;执行&#43;操作
0&#xff1a;判定信号量>0&#xff0c;则阻塞&#xff0c;直到为0
控制进程的搭配方式&#xff1a;
&#43;(解除阻塞) -(阻塞)
0(阻塞) -(解除阻塞)
/*arg for semctl systemcalls.*/
union semun{
int val;/*value for SETVAL*/
struct semid_ds *buf;/*buffer for IPC_STAT&IPC_SET*/
ushort *array;/*array for GETALL&SETALL*/
struct seminfo *__buf;/*buffer for IPC_INFO*/
https://blog.csdn.net/xiajun07061225/article/details/8475738
文件A
#include
#include
#include
#include
#include
union semun {int val;struct semid_ds *buf;unsigned short *array;struct seminfo *__buf;
};main()
{key_t key;int semid; //信号量IDunion semun v;//2.2.定义初始化值int r;struct sembuf op[1];//1.创建信号量key&#61;ftok(".",99);if(key&#61;&#61;-1) printf("ftok err:%m\n"),exit(-1);//semid&#61;semget(key,1/*信号量数组个数*/,// IPC_CREAT|IPC_EXCL|0666);semid&#61;semget(key,1,0);//得到信号量if(semid&#61;&#61;-1) printf("get err:%m\n"),exit(-1);printf("id:%d\n",semid);//2.初始化信号量v.val&#61;2;r&#61;semctl(semid,0,SETVAL,v);//2.3设置信号量的值if(r&#61;&#61;-1) printf("初始化失败!\n"),exit(-1);//3.对信号量进行阻塞操作//3.1.定义操作op[0].sem_num&#61;0;//信号量下标op[0].sem_op&#61;-1;//信号量操作单位与类型op[0].sem_flg&#61;0;//操作标记while(1){r&#61;semop(semid,op,1);printf("解除阻塞&#xff01;\n");}//4.删除(可以不删除)
}
文件B
#include
#include
#include
#include
#include
union semun {int val;struct semid_ds *buf;unsigned short *array;struct seminfo *__buf;
};main()
{key_t key;int semid; //信号量IDunion semun v;//2.2.定义初始化值int r;struct sembuf op[2];//1.创建信号量key&#61;ftok(".",99);if(key&#61;&#61;-1) printf("ftok err:%m\n"),exit(-1);semid&#61;semget(key,1,0);//得到信号量if(semid&#61;&#61;-1) printf("get err:%m\n"),exit(-1);printf("id:%d\n",semid);//3.对信号量进行阻塞操作//3.1.定义操作op[0].sem_num&#61;0;//信号量下标op[0].sem_op&#61;1;//信号量操作单位与类型op[0].sem_flg&#61;0;op[1].sem_num&#61;0;//信号量下标op[1].sem_op&#61;1;//信号量操作单位与类型op[1].sem_flg&#61;0;while(1){r&#61;semop(semid,op,2);sleep(1);}//4.删除(可以不删除)//semctl(semid,0,IPC_RMID);}
自己找的信号量代码
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define FILE_NAME "test2.c"union semun{int val ;struct semid_ds *buf ;unsigned short *array ;struct seminfo *_buf ;
}arg;struct semid_ds sembuf;int main()
{key_t key ;int semid ,ret,i;unsigned short buf[MAX_SEMAPHORE] ;struct sembuf sb[MAX_SEMAPHORE] ;pid_t pid ;pid &#61; fork() ;if(pid <0){/* Create process Error! */fprintf(stderr,"Create Process Error!:%s\n",strerror(errno));exit(1) ;}if(pid > 0){/* in parent process !*/key &#61; ftok(FILE_NAME,&#39;a&#39;) ;if(key &#61;&#61; -1){/* in parent process*/fprintf(stderr,"Error in ftok:%s!\n",strerror(errno));exit(1) ;}semid &#61; semget(key,MAX_SEMAPHORE,IPC_CREAT|0666); //创建信号量集合if(semid &#61;&#61; -1){fprintf(stderr,"Error in semget:%s\n",strerror(errno));exit(1) ;}printf("Semaphore have been initialed successfully in parent process,ID is :%d\n",semid);sleep(2) ;printf("parent wake up....\n");/* 父进程在子进程得到semaphore的时候请求semaphore&#xff0c;此时父进程将阻塞直至子进程释放掉semaphore*//* 此时父进程的阻塞是因为semaphore 1 不能申请&#xff0c;因而导致的进程阻塞*/for(i&#61;0;i
二、网络
1. 基础&#xff08;ip&#xff09;
1.1 网络工具
ping
ping ip //地址
ping -b ip //广播地址
ifconfig -a //查看所有网络口信息
netstat -a //显示所有正在使用的网络状态
netstat -u //支持udp协议
netstat -t //支持tcp协议
netstat -x //支持本地unix协议
netstat -n //数字方式显示
route
lsof
1.2 网络的基本概念
网络编程采用socket模型.
网络通信本质也是进程之间的IPC&#xff0c;是不同主机之间。
识别主机&#xff1a;4字节整数&#xff1a;IP地址
识别进程&#xff1a;2字节整数&#xff1a;端口号
IP地址的表示方法&#xff1a; 内部表示&#xff1a;4字节整数
外部表示&#xff1a;数点字符串
结构体
1 2 3 4 分段表示&#xff0c;每个段使用.分割
"192.168.0.26"
ip地址的转换&#xff1a;
sockaddr是在头文件 /usr/include/bits/socket.h 中定义的
struct sockaddr
{sa_family_t sin_family; //地址族char sa_data[14]; //14字节&#xff0c;包含套接字中的目标地址和端口信息
};
sockaddr_in是在头文件 /usr/include/netinet/in.h 中定义的
struct sockaddr_in
{int sin_family; //地址族in_port_t sin_port; //16位TCP/UDP端口号struct in_addr sin_addr; //32位ip地址char sin_zero[8]; //留用
}
struct in_addr
{in_addr_t s_addr; //32位IPv4地址
}
//总结&#xff1a;
IP地址的表示
字符串表示"192.168.0.26"
整数表示&#xff1a;in_addr_t;
字结构表示struct in_addr;
连接点&#xff1a;endpoint
1.3 IP地址的转换
inet_addr //把字符串转换为整数&#xff08;网络字节序&#xff09;
inet_aton //把字符串转换为struct in_addr;(网络字结序)
inet_network //把字符串转换为整数&#xff08;本地字节序&#xff09;
inet_ntoa //把结构体转换为字符串
htons //Host to Network Short
htonl //Host to Network Long
ntohs //Network to Host Short
ntohl //Host to Network Long
补充&#xff1a;关于字节序
网络字节顺序NBO&#xff08;Network Byte Order&#xff09;&#xff1a;
按从高到低的顺序存储&#xff0c;在网络上使用统一的网络字节顺序&#xff0c;可以避免兼容性问题。
主机字节顺序&#xff08;HBO&#xff0c;Host Byte Order&#xff09;&#xff1a;
不同的机器HBO不相同&#xff0c;与CPU设计有关&#xff0c;数据的顺序是由cpu决定的,而与操作系统无关。
如 Intelx86结构下,short型数0x1234表示为34 12, int型数0x12345678表示为78 56 34 12如IBM power PC结构下,short型数0x1234表示为12 34, int型数0x12345678表示为12 34 56 78
由于这个原因不同体系结构的机器之间无法通信,所以要转换成一种约定的数序,也就是网络字节顺序,其实就是如同powerpc那样的顺序 。在PC开发中有ntohl和htonl函数可以用来进行网络字节和主机字节的转换。
主机的字节序不同&#xff0c;注意下列代码
#include
#include
#include
#include
{/*in_addr_t nip&#61;192<<24 | 168 <<16 | 0<<8 | 26;char *ip&#61;"192.168.0.26";//把整数转换为字符串inet_ntoastruct in_addr sip;int myip;sip.s_addr&#61;nip;printf("nip:%u\n",nip);printf("%s\n",inet_ntoa(sip));myip&#61;inet_addr(ip);printf("%u\n",myip);printf("%hhu.%hhu.%hhu.%hhu\n", myip>>24 & 255,myip>>16 & 255,myip>>8 & 255,myip>>0 & 255);*//*char ip[4]&#61;{192,168,0,26};printf("%d\n",*(int*)ip);*/char *ip&#61;"10.45.8.1";struct in_addr addr;in_addr_t net;in_addr_t host;struct in_addr tmp;inet_aton(ip,&addr);net&#61;inet_lnaof(addr);host&#61;inet_netof(addr);tmp.s_addr&#61;net;//网络表示printf("%s\n",inet_ntoa(tmp));tmp.s_addr&#61;host;//主机表示printf("%s\n",inet_ntoa(tmp));}
1.4 IP地址的意义
IP地址的位表达不同意义&#xff1a;
IP地址组建网络&#xff1a;网络标识/主机标识
网络 主机
A类 前7位表示 后24位表示 网络少主机多
B类 14 16
C类 2 1 8
D类 组播
E类 没有使用
1.5 计算机系统中的网络配置
/etc/hosts 文件 配置IP&#xff0c;域名&#xff0c;主机名
gethostbyname
gethostbyaddr
/etc/protocols 文件 配置系统支持的协议
/etc/services 文件 配置服务
get***by***;
gethostbyname
getprotobyname
#include
#include
{struct hostent *ent;/*打开主机配置数据库文件*/sethostent(1);//1一直打开&#xff0c;0单次打开while(1){ent&#61;gethostent();if(ent&#61;&#61;0) break;printf("主机名:%s\t",ent->h_name);printf("IP地址:%hhu.%hhu.%hhu.%hhu\t",ent->h_addr[0],ent->h_addr[1],ent->h_addr[2],ent->h_addr[3]);printf("别名:%s\n",ent->h_aliases[0]);}endhostent();
}
#include
#include
{struct hostent *ent;ent&#61;gethostbyname("bbs.tarena.com.cn");//printf("%s\n",ent->h_aliases[0]);printf("%hhu.%hhu.%hhu.%hhu\n",ent->h_addr_list[0][0],ent->h_addr_list[0][1],ent->h_addr_list[0][2],ent->h_addr_list[0][3]);
}
编译时 gcc protocol.c -omain -D_GNU_SOURCE 最后一个是宏&#xff0c;编译时需要加上
#include
#include
#include
{struct protoent *ent;struct utsname name;ent&#61;getprotobyname("tcp");printf("%d\n",ent->p_proto);uname(&name);printf("%s\n",name.machine);printf("%s\n",name.nodename);printf("%s\n",name.sysname);printf("%s\n",name.domainname);
}
三、UDP编程模型
对等模型 AF_INET SOCK_DGRAM 0:UDP
C/S 模型 AF_INET SOCK_STREAM 0:TCP
2.1 网络编程
ISO的7层模型&#xff1a;
物理层
数据链路层 数据链路层 (数据物理怎么传输)
网络层 IP层 (数据的传输方式)
传输层 传输层 (数据传输的结果)
会话层 应用层 (数据传递的含义)
表示层
应用层
2.2 UDP编程的数据特点
UDP采用对等模型SOCK_DGRAM
socket socket:socket
绑定IP地址bind 连接目标(可选) conncect
read/recv/recvfrom 发送数据 write/send/sendto
关闭close
案例&#xff1a;
A B
接收用户的数据 发送数据
打印数据与发送者IP 接收数据并打印
返发一个信息
udpA&#xff1a;
#include
#include
#include
#include
#include
#include
main()
{int fd;//socket描述符号struct sockaddr_in ad;//本机的IP地址char buf[100];//接收数据缓冲struct sockaddr_in ad_snd;//发送者IP地址socklen_t len;//发送者IP的长度int r;fd&#61;socket(AF_INET,SOCK_DGRAM,17);//udp协议17 可以自己查看 protocols 中if(fd&#61;&#61;-1) printf("socket:%m\n"),exit(-1);printf("建立socket成功!\n");ad.sin_family&#61;AF_INET;ad.sin_port&#61;htons(11111);inet_aton("192.168.180.92",&ad.sin_addr);r&#61;bind(fd,(struct sockaddr*)&ad,sizeof(ad));if(r&#61;&#61;-1) printf("bind err:%m\n"),exit(-1);printf("绑定成功!\n");while(1){len&#61;sizeof(ad_snd);r&#61;recvfrom(fd,buf,sizeof(buf)-1,0,(struct sockaddr*)&ad_snd,&len);if(r>0){buf[r]&#61;0;printf("发送者IP:%s,端口:%hu,数据:%s\n",inet_ntoa(ad_snd.sin_addr),ntohs(ad_snd.sin_port),buf);sendto(fd,"古怪!",strlen("古怪!"),0,(struct sockaddr*)&ad_snd,sizeof(ad_snd));}if(r&#61;&#61;0){printf("关闭!\n");break;}if(r&#61;&#61;-1){printf("网络故障!\n");break;}}close(fd);
}
udpB
#include
#include
#include
#include
#include
#include
#include
#include
main()
{int fd;struct sockaddr_in ad;char buf[101];int r;fd&#61;socket(AF_INET,SOCK_DGRAM,0);if(fd&#61;&#61;-1) printf("socket err:%m\n"),exit(-1);ad.sin_family&#61;AF_INET;ad.sin_port&#61;htons(11111);ad.sin_addr.s_addr&#61;inet_addr("192.168.180.92");//connect(fd,(struct sockaddr*)&ad,sizeof(ad));while(1){r&#61;read(0,buf,sizeof(buf)-1);//从屏幕读取一行&#xff0c;0&#xff1a;数据流输入&#xff0c;1&#xff1a;数据流输出&#xff0c;2&#xff1a;数据流错误if(r<&#61;0) break;buf[r]&#61;0;r&#61;sendto(fd,buf,r,0,(struct sockaddr*)&ad,sizeof(ad));bzero(buf,sizeof(buf));r&#61;recv(fd,buf,sizeof(buf),0);//这个0 后面会专门讲buf[r]&#61;0;printf("来自接收方的数据:%s\n",buf);//r&#61;send(fd,buf,r,0);if(r&#61;&#61;-1) break;}close(fd);
}
总结&#xff1a;
1. 问题&#xff1a;
connect &#43; send &#43; close &#61;&#61; sendto
sendto 是连接&#43;发送&#43;关闭&#xff0c;每次都是这个循环
connect是一直连接
2. 问题&#xff1a;
recvfrom的作用不是专门从指定IP接收
而是从任意IP接收数据&#xff0c;返回发送数据者的IP
3. 问题&#xff1a;
为什么要bind&#xff0c;bind主要目的告诉网络发送数据的目标.
是否一定绑定才能发送数据?
否:只要知道你的IP与PORT&#xff0c;就能发送数据.
4. 问题&#xff1a;
为什么发送者没有绑定IP与端口&#xff0c;他也有端口?
底层网络驱动&#xff0c;帮我们自动生成IP与端口.
5. 缺陷&#xff1a;
接收方不区分发送者的&#xff0c;这是UDP的一大缺陷的&#xff0c;在TCP中改善了这个问题
send函数
sendto函数
int sendto(int fd, //socket描述符号const void *buf, //发送的数据缓冲size_t size, //发送的数据长度int flags, //发送方式MSG_NOWAIT MSG_OOBconst struct sockaddr *addr, //发送的目标的IP与端口socklen_t len //sockaddr_in的长度
);返回&#xff1a;-1&#xff1a;发送失败>&#61;0&#xff1a;发送的数据长度
recv函数
recvfrom函数
int recvfrom(int fd,void *buf,size_t size,int flags,struct sockaddr*addr, //返回发送者IP与端口socklen_t *len); //输入返回IP的缓冲大小,返回实际IP的大小
2.3 TCP编程的数据特点
2.4 TCP服务器的编程