热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

C++学习:第六章Linux高级编程(九)信号量同步、socket网络编程基础、TCP、UDP

一、信号量(同步)1.回顾:一个进程控制另外一个进程.逻辑变量pausesleep信号2.信号量(semaphore)信号灯三个数据:红灯绿灯黄灯
一、信号量(同步)

 

  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 //2.1.定义一个联合体
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 //2.1.定义一个联合体
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 MAX_SEMAPHORE 10
#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 main()
{/*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 main()
{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 main()
{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 main()
{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服务器的编程

  1. TCP的服务器编程模型
  2. IP协议与处理&#xff08;SOCK_RAW,SOCK_PACKET&#xff09;
  3. pcap编程
  4. HTTP协议与网页搜索

推荐阅读
author-avatar
QueenieYam任嘉明
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有