每次只有两个实体相互通信,发送端和接收端都是唯一确定的。
广播地址: 主机号最大的地址;
以192.168.1.0 (255.255.255.0) 网段为例,最大的主机地址192.168.1.255代表该网段的广播地址
(同一局域网内的主机都会接收到,如果其他主机没有加入广播站,就会将消息丢弃)
setsockopt 设置套接字的属性
头文件:
#include<sys.socket.h>
#include<sys/types.h>
#include<sys/time.h>
原型: int setsockopt(int sockfd,int level,int optname,\
void *optval,socklen_t optlen)
功能: 获得/设置套接字属性
参数:
sockfd:套接字描述符
level:协议层
optname:选项名
optval:选项值
optlen:选项值大小
返回值: 成功 0 失败-1
1)广播的发送端流程 ---》类似于UDP的客户端
a。IP (192.168.50.255/255.255.255.255)
b。端口号
1. 创建用户数据报套接字;
sockfd = socket(AF_INET,SOCK_DGRAM,0);
2.setsockopt可以设置套接字属性,先设定该套接字允许发送广播
int optval = 1;
// SOL_SOCKET 传输层 SO_BROADCAST 允许发送广播
setsockopt(sockfd,SOL_SOCKET,SO_BROADCAST,&optval,sizeo(optval));
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(atoi(argv[2]));
addr.sin_addr.s_addr = inet_addr(argv[1]);
4. 发送数据包
sendto(sockfd,buf,sizeof(buf),0,\
(struct sockaddr*)&addr,sizeof(addr));
发送者需要借助 setsockopt 开通广播权限:
./client:
/*客户端创建代码 */
#include
#include /* See NOTES */
#include
#include
#include /* superset of previous */
#include
#include
#include
#include
#include
#include
#include
#include
int main(int argc, char const *argv[])
{
if(argc<3)
{
printf("plase input ");
}
//1.创建套接字,用于链接
int sockfd;
sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if (sockfd < 0)
{
perror("socket err");
return -1;
}
printf("sockfd:%d\n", sockfd);
//2.判断是否允许广播
int broad;
socklen_t lenth = sizeof(broad);//确定broad长度
getsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, &broad, &lenth); //获取sockfd属性
printf("%d\n",broad);
//设置允许广播
broad=1;
setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, &broad, lenth);
printf("设置网络成功\n");
//2.填充结构体
struct sockaddr_in saddr;
saddr.sin_family = AF_INET;
saddr.sin_port = htons(atoi(argv[1]));
saddr.sin_addr.s_addr = inet_addr("192.168.50.255");
socklen_t len = sizeof(saddr); //结构体大小
char buf[128] = {0};
int ret;
while (1)
{
//发送信息
printf("client:");
fgets(buf, sizeof(buf), stdin); //从终端获取内容存放到数组中
if (strncmp(buf, "quit", 4) == 0) //输入quit退出客户端
{
break;
}
if (buf[strlen(buf)] == '\0')
{
buf[strlen(buf) - 1] = '\0';
}
sendto(sockfd, buf, sizeof(buf), 0, (struct sockaddr *)&saddr, len);
}
close(sockfd);
return 0;
}
2)广播的接收端流程 -----》类似于UDP服务器
基本无需改动
sockfd = socket(AF_INET,SOCK_DGRAM,0);
2. 绑定IP地址(广播IP或0.0.0.0)和端口
广播的接收者需要加入 广播站
struct sockaddr_in saddr,caddr;
saddr.sin_family = AF_INET;
saddr.sin_port = htons(atoi(argv[2]));
saddr.sin_addr.s_addr = inet_addr("0.0.0.0");
//广播地址或0.0.0.0
//0.0.0.0 是一个特殊的IP地址,用于表示服务器端将监听所有可用的网络接口
// 而不仅仅是IP地址,广播地址也会监听。
socklen_t len = sizeof(caddr);
bind(sockfd,(struct sockaddr *)&saddr,sizeof(saddr));
3. 等待接收数据
recvfrom(sockfd,buf,sizeof(buf),0,\
(struct sockaddr *)&caddr,&len);
/*服务器创建代码 */
#include
#include /* See NOTES */
#include
#include
#include /* superset of previous */
#include
#include
#include
#include
int main(int argc, char const *argv[])
{
if (argc < 2)
{
printf("plase input \n");
return -1;
}
//1.创建套接字,用于链接
int sockfd;
sockfd = socket(AF_INET,SOCK_DGRAM, 0);
if (sockfd < 0)
{
perror("socket err");
return -1;
}
printf("sockfd:%d\n", sockfd);
//2.绑定 ip+port 填充结构体
struct sockaddr_in saddr;
saddr.sin_family = AF_INET;
saddr.sin_port = htons(atoi(argv[1]));
saddr.sin_addr.s_addr = inet_addr("192.168.50.255");
socklen_t len = sizeof(saddr); //结构体大小
//bind绑定ip和端口
if (bind(sockfd, (struct sockaddr *)&saddr, len) < 0)
{
perror("bind err");
return -1;
}
printf("bind success\n");
char buf[128] = {0};
while (1)
{
//接收信息
if (recvfrom(sockfd, buf, sizeof(buf), 0, (struct sockaddr *)&saddr, &len) < 0)
{
perror("recvfrom err");
return -1;
}
printf("client ip:%s ,port:%d buf:%s\n", inet_ntoa(saddr.sin_addr), ntohs(saddr.sin_port),buf);
// //发送信息
// printf("server:");
// fgets(buf, sizeof(buf), stdin); //从终端获取内容存放到数组中
// if (strncmp(buf, "quit", 4) == 0) //输入quit退出客户端
// {
// break;
// }
// if (buf[strlen(buf)] == '\0')
// {
// buf[strlen(buf) - 1] = '\0';
// }
// sendto(sockfd, buf, sizeof(buf), 0, (struct sockaddr *)&saddr, len);
}
close(sockfd);
return 0;
}
广播方式发给所有的主机,过多的广播会大量的占用网络带宽,造成广播风暴,影响正常的通信;
224.10.10.10(相当于组名)
。单播方式只能发给一个接收方
。 组播是一个人发送,加入到多播组的主机接收数据
。 多播方式既可以发给多个主机,又能避免像广播一样造成过多的负载:
IP的二级划分中 D类IP:
第一字节的前四位固定为 1110
D类IP : 224.0.0.1 - 239.255.255.255
224.10.10.10
1)组播的发送端流程 ---》类似于UDP的客户端
1. 创建用户数据报套接字;
sockfd = socket(AF_INET,SOCK_DGRAM,0);
2. 指定接收方地址指定为组播地址 224.0.0.10 (224.0.0.1 - 239.255.255.255都可以) 并指定接收端端口信息
//2. 填充结构体: 组播ip 和 端口
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(atoi(argv[2]));
addr.sin_addr.s_addr = inet_addr(argv[1]); //组播的IP
3. 发送数据包
sendto(sockfd,buf,sizeof(buf),0,(struct sockaddr *)&addr,sizeof(addr));
/*客户端创建代码 */
#include
#include /* See NOTES */
#include
#include
#include /* superset of previous */
#include
#include
#include
#include
#include
#include
#include
#include
int main(int argc, char const *argv[])
{
if(argc<3)
{
printf("plase input ");
}
//1.创建套接字,用于链接
int sockfd;
sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if (sockfd < 0)
{
perror("socket err");
return -1;
}
printf("sockfd:%d\n", sockfd);
// //2.判断是否允许广播
// int broad;
// socklen_t lenth = sizeof(broad);//确定broad长度
// getsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, &broad, &lenth); //获取sockfd属性
// printf("%d\n",broad);
// //设置允许广播
// broad=1;
// setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, &broad, lenth);
// printf("设置网络成功\n");
//2.填充结构体
struct sockaddr_in saddr;
saddr.sin_family = AF_INET;
saddr.sin_port = htons(atoi(argv[2]));
saddr.sin_addr.s_addr = inet_addr(argv[1]);//组播ip
socklen_t len = sizeof(saddr); //结构体大小
char buf[128] = {0};
int ret;
while (1)
{
//发送信息
printf("client:");
fgets(buf, sizeof(buf), stdin); //从终端获取内容存放到数组中
if (strncmp(buf, "quit", 4) == 0) //输入quit退出客户端
{
break;
}
if (buf[strlen(buf)] == '\0')
{
buf[strlen(buf) - 1] = '\0';
}
sendto(sockfd, buf, sizeof(buf), 0, (struct sockaddr *)&saddr, len);
}
close(sockfd);
return 0;
}
1)组播的接收端流程 ---》类似于UDP的服务器
1)组播的接收端流程 ---》类似于UDP的服务器
struct ip_mreq
{
struct in_addr imr_multiaddr; /* 指定多播组IP */
struct in_addr imr_interface; /* 本地网卡地址,通常指定为 INADDR_ANY--0.0.0.0*/};
}
struct ip_mreq mreq;
//bzero(&mreq, sizeof(mreq));
mreq.imr_multiaddr.s_addr = inet_addr("224.10.10.1");
mreq.imr_interface.s_addr = INADDR_ANY;
setsockopt(sockfd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq));
1、创建用户数据报套接字;
sockfd = socket(AF_INET,SOCK_DGRAM,0);
2. 加入多播组 : 只有接收者加入多播组 (广播是发送者需要设置广播属性,组播是接收者加入多播组)
//2.将主机假如多播组
struct ip_mreq mreq; //组播的结构体变量
mreq.imr_multiaddr.s_addr = inet_addr(argv[1]); //组IP
mreq.imr_interface.s_addr = inet_addr("0.0.0.0"); //本地IP加入到上面的多播组
// 将本地IP 加入到多播组 setsockopt(sockfd,IPPROTO_IP,IP_ADD_MEMBERSHIP,&mreq,sizeof(mreq));
3. 绑定组播IP地址(绑定组播IP或0.0.0.0)和端口
//3. 填充结构体: 绑定组播ip和端口
struct sockaddr_in saddr,caddr;
saddr.sin_family = AF_INET;
saddr.sin_port = htons(atoi(argv[2]));
saddr.sin_addr.s_addr = inet_addr("0.0.0.0"); //组IP
4. 接收数据包
recvfrom(sockfd,buf,sizeof(buf),0,(struct sockaddr*)&caddr,&len);
/*服务器创建代码 */
#include
#include /* See NOTES */
#include
#include
#include /* superset of previous */
#include
#include
#include
#include
int main(int argc, char const *argv[])
{
if (argc < 3)
{
printf("plase input \n");
return -1;
}
//1.创建套接字,用于链接
int sockfd;
sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if (sockfd < 0)
{
perror("socket err");
return -1;
}
printf("sockfd:%d\n", sockfd);
//设置接收组播
struct ip_mreq group;
group.imr_multiaddr.s_addr = inet_addr(argv[1]); //组IP
group.imr_interface.s_addr = inet_addr("0.0.0.0"); //本地IP加入到上面的多播组(自己的ip)
setsockopt(sockfd,IPPROTO_IP,IP_ADD_MEMBERSHIP,&group,sizeof(group));
printf("加组成功\n");
//2.绑定 ip+port 填充结构体
struct sockaddr_in saddr;
saddr.sin_family = AF_INET;
saddr.sin_port = htons(atoi(argv[2]));
saddr.sin_addr.s_addr = inet_addr(argv[1]);
socklen_t len = sizeof(saddr); //结构体大小
//bind绑定ip和端口
if (bind(sockfd, (struct sockaddr *)&saddr, len) < 0)
{
perror("bind err");
return -1;
}
printf("bind success\n");
char buf[128] = {0};
while (1)
{
//接收信息
if (recvfrom(sockfd, buf, sizeof(buf), 0, (struct sockaddr *)&saddr, &len) < 0)
{
perror("recvfrom err");
return -1;
}
printf("client ip:%s ,port:%d buf:%s\n", inet_ntoa(saddr.sin_addr), ntohs(saddr.sin_port), buf);
}
close(sockfd);
return 0;
}
· unix网络编程最开始有的编程都是一台主机内进程和进程之间的编程。(本地通信)
· socket同样可以用于本地间进程通信, 创建套接字时使用本地协议AF_LOCAL或AF_UNIX
· 分为流式套接字和数据报套接字
· 和其他进程间通信相比使用方便、效率更高,常用于前后台进程通信。
unix域套接字编程,实现本间进程的通信,依赖的是s类型的文件;(套接字文件)
TCP,UDP 都是依赖IP 端口号进行通信,实现两个主机通信
本地通信不需要ip和端口,所以无法进行两个主机通信
核心代码:
#include
#include
struct sockaddr_un {
sa_family_t sun_family; /* 本地协议 AF_UNIX */
char sun_path[UNIX_PATH_MAX]; /* 本地路径 s类型的套接字文件 */
};
unix socket = socket(AF_UNIX, type, 0); //type为流式或数据包套接字
struct sockaddr_un myaddr;
myaddr.sun_family = AF_UNIX; //填充UNIX域套接字
strcpy(saddr.sun_path,"./myunix"); // 创建套接字的路径(将套接字创建到哪? 服务器与客户端一致)