一.信号量(同步)
1.回顾:
一个进程控制另外一个进程.
逻辑变量+pause/sleep+信号
2.信号量(semaphore)信号灯
三个数据:红灯/绿灯/黄灯
60 90 10
信号量是共享内存整数数组.根据需要定义指定的数组长度
信号量就是根据数组中的值,决定阻塞还是解除阻塞
3.编程
3.1.创建或者得到信号量 semget
3.2.初始化信号量中指定下标的值 semctl
3.3.根据信号量阻塞或者解除阻塞 semop
3.4.删除信号量 semctl
案例:
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,//信号量ID struct sembuf *op,//对信号量的操作.操作可以是数组多个 size_t nums,//第二个参数的个数 );
返回:
-1:时失败
0:成功
int semctl(int semid, int nums,//对IPC_RMID无意义 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.
-:够减,则semop马上返回,不够减,则阻塞.
+:执行+操作
0:判定信号量>0,则阻塞,直到为0
控制进程的搭配方式:
+(解除阻塞) -(阻塞)
0(阻塞) -(解除阻塞)
#include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <sys/ipc.h> #include <sys/sem.h> //2.1.定义一个联合体 union semun { int val; struct semid_ds *buf; unsigned short *array; struct seminfo *__buf; }; main() { key_t key; int semid; //信号量ID union semun v;//2.2.定义初始化值 int r; struct sembuf op[1]; //1.创建信号量 key=ftok(".",99); if(key==-1) printf("ftok err:%m\n"),exit(-1); //semid=semget(key,1/*信号量数组个数*/, // IPC_CREAT|IPC_EXCL|0666); semid=semget(key,1,0);//得到信号量 if(semid==-1) printf("get err:%m\n"),exit(-1); printf("id:%d\n",semid); //2.初始化信号量 v.val=2; r=semctl(semid,0,SETVAL,v);//2.3设置信号量的值 if(r==-1) printf("初始化失败!\n"),exit(-1); //3.对信号量进行阻塞操作 //3.1.定义操作 op[0].sem_num=0;//信号量下标 op[0].sem_op=-1;//信号量操作单位与类型 op[0].sem_flg=0; while(1) { r=semop(semid,op,1); printf("解除阻塞!\n"); } //4.删除(可以不删除) }
#include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <sys/ipc.h> #include <sys/sem.h> //2.1.定义一个联合体 union semun { int val; struct semid_ds *buf; unsigned short *array; struct seminfo *__buf; }; main() { key_t key; int semid; //信号量ID union semun v;//2.2.定义初始化值 int r; struct sembuf op[2]; //1.创建信号量 key=ftok(".",99); if(key==-1) printf("ftok err:%m\n"),exit(-1); semid=semget(key,1,0);//得到信号量 if(semid==-1) printf("get err:%m\n"),exit(-1); printf("id:%d\n",semid); //3.对信号量进行阻塞操作 //3.1.定义操作 op[0].sem_num=0;//信号量下标 op[0].sem_op=1;//信号量操作单位与类型 op[0].sem_flg=0; op[1].sem_num=0;//信号量下标 op[1].sem_op=1;//信号量操作单位与类型 op[1].sem_flg=0; while(1) { r=semop(semid,op,2); sleep(1); } //4.删除(可以不删除) //semctl(semid,0,IPC_RMID); }
二.网络
1.基础(ip)
1.1.网络工具
ping
ping ip地址
ping -b ip广播地址
ifconfig -a
netstat -a
netstat -u
netstat -t
netstat -x
netstat -n
route
lsof
1.2.网络的基本概念
网络编程采用socket模型.
网络通信本质也是进程之间的IPC。
是不同主机之间。
识别主机:4字节整数:IP地址
识别进程:2字节整数:端口号
IP地址的表示方法: 内部表示:4字节整数
外部表示:数点字符串
结构体
1 2 3 4 分段表示,每个段使用.分割
"192.168.0.26"
ip地址的转换:
struct sockaddr_in { int sin_family; in_port_t sin_port; struct in_addr sin_addr; }
struct in_addr { in_addr_t s_addr; }
//总结:
IP地址的表示
字符串表示"192.168.0.26"
整数表示:in_addr_t;
字结构表示struct in_addr;
连接点:endpoint
struct sockaddr_in { in_port_t sin_port; struct in_addr sin_addr; };
1.3.IP地址的转换
inet_addr //把字符串转换为整数(网络字节序)
inet_aton //把字符串转换为struct in_addr;(网络字结序)
#inet_network//把字符串转换为整数(本地字节序)
inet_ntoa //把结构体转换为字符串
htons
htonl
ntohs
ntohl
#include <stdio.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> main() { /* in_addr_t nip=192<<24 | 168 <<16 | 0<<8 | 26; char *ip="192.168.0.26"; //把整数转换为字符串inet_ntoa struct in_addr sip; int myip; sip.s_addr=nip; printf("nip:%u\n",nip); printf("%s\n",inet_ntoa(sip)); myip=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]={192,168,0,26}; printf("%d\n",*(int*)ip); */ char *ip="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=inet_lnaof(addr); host=inet_netof(addr); tmp.s_addr=net; printf("%s\n",inet_ntoa(tmp)); tmp.s_addr=host; printf("%s\n",inet_ntoa(tmp)); }
1.4.IP地址的意义
IP地址的位表达不同意义:
IP地址组建网络:网络标识/主机标识
网络 主机
A类 7 24 网络少 主机
B类 14 16
C类 21 8
D类 组播
E类 没有使用
1.5.计算机系统中的网络配置
/etc/hosts文件 配置IP,域名,主机名
gethostbyname
gethostbyaddr
/etc/protocols文件 配置系统支持的协议
/etc/services文件 配置服务
get***by***;
gethostbyname
getprotobyname
#include <stdio.h> #include <netdb.h> main() { struct hostent *ent; /*打开主机配置数据库文件*/ sethostent(1); while(1) { ent=gethostent(); if(ent==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 <stdio.h> #include <netdb.h> main() { struct hostent *ent; ent=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]); }
#include <stdio.h> #include <netdb.h> #include <sys/utsname.h> main() { struct protoent *ent; struct utsname name; ent=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); }
2.TCP/UDP编程
对等模型:AF_INET SOCK_DGRAM 0:UDP
C/S 模型:AF_INET SOCK_STREAM 0:TCP
2.0.网络编程
ISO的7层模型:
物理层
数据链路层 数据链路层(数据物理怎么传输)
网络层 IP层 (数据的传输方式)
传输层 传输层 (数据传输的结果)
会话层 应用层 (数据传递的含义)
表示层
应用层
2.1.UDP编程的数据特点
UDP采用对等模型SOCK_DGRAM
socket socket:socket
绑定IP地址bind 连接目标(可选) conncect
read/recv/recvfrom 发送数据 write/send/sendto
关闭close
案例:
A: B
接收用户的数据 发送数据
打印数据与发送者IP 接收数据并打印
返发一个信息
#include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <string.h> #include <sys/socket.h> #include <netinet/in.h> 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=socket(AF_INET,SOCK_DGRAM,17); if(fd==-1) printf("socket:%m\n"),exit(-1); printf("建立socket成功!\n"); ad.sin_family=AF_INET; ad.sin_port=htons(11111); inet_aton("192.168.180.92",&ad.sin_addr); r=bind(fd,(struct sockaddr*)&ad,sizeof(ad)); if(r==-1) printf("bind err:%m\n"),exit(-1); printf("绑定成功!\n"); while(1) { len=sizeof(ad_snd); r=recvfrom(fd,buf,sizeof(buf)-1,0, (struct sockaddr*)&ad_snd,&len); if(r>0){ buf[r]=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==0) { printf("关闭!\n"); break; } if(r==-1) { printf("网络故障!\n"); break; } } close(fd); }
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <netdb.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> main() { int fd; struct sockaddr_in ad; char buf[101]; int r; fd=socket(AF_INET,SOCK_DGRAM,0); if(fd==-1) printf("socket err:%m\n"),exit(-1); ad.sin_family=AF_INET; ad.sin_port=htons(11111); ad.sin_addr.s_addr=inet_addr("192.168.180.92"); //connect(fd,(struct sockaddr*)&ad,sizeof(ad)); while(1) { r=read(0,buf,sizeof(buf)-1); if(r<=0) break; buf[r]=0; r=sendto(fd,buf,r,0, (struct sockaddr*)&ad,sizeof(ad)); bzero(buf,sizeof(buf)); r=recv(fd,buf,sizeof(buf),0); buf[r]=0; printf("来自接收方的数据:%s\n",buf); //r=send(fd,buf,r,0); if(r==-1) break; } close(fd); }
总结:
1.问题:
connect + send == sendto
2.问题:
recvfrom的作用不是专门从指定IP接收
而是从任意IP接收数据,返回发送数据者的IP
3.问题:
为什么要bind,bind主要目的告诉网络发送数据的目标.
是否一定绑定才能发送数据?
否:只要知道你的IP与PORT,就能发送数据.
4.问题:
为什么发送者没有绑定IP与端口,他也有端口?
底层网络驱动,帮我们自动生成IP与端口.
5.缺陷:
接收方不区分发送者的.
send函数
sendto函数
int sendto( int fd,//socket描述符号 const void *buf,//发送的数据缓冲 size_t size,//发送的数据长度 int flags,//发送方式MSG_NOWAIT MSG_OOB const struct sockaddr *addr,//发送的目标的IP与端口 socklen_t len//sockaddr_in的长度 );
返回:
-1:发送失败
>=0:发送的数据长度
recv函数
recvfrom函数
int recvfrom( int fd, void *buf, size_t size, int flags, struct sockaddr*addr,//返回发送者IP与端口 socklen_t *len);//输入返回IP的缓冲大小,返回实际IP的大小
2.2.TCP编程的数据特点
2.3.TCP服务器的编程
3.TCP的服务器编程模型
4.IP协议与处理(SOCK_RAW,SOCK_PACKET)
5.pcap编程
6.HTTP协议与网页搜索
作业:
1.重新编写UDP网络通信
2.使用gethostbyname的得到bbs.tarena.com.cn
www.sina.com