通过UDP 广播可以将数据发送给同一网段下的所有指定端口号的进程。但是不能够在Internet上传输数据,只能在局域网中。
注意:只要是存在在与服务器同一网段内的主机,都会收到广播的数据,无论是否需要。
使用 setsockopt() 函数
int setsockopt(int sockfd, int level, int optname,
const void *optval, socklen_t optlen);
// 示例:
int flag = 1;
setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, &flag, sizeof(flag));
sockfd:套接字fd
level:SOL_SOCKET
optname:SO_BROADCAST (表示广播)
optval:&flag
optlen:flag的长度。
#include
#include
#include
#include
#include
#include
#define SRV_PORT 8000
#define BROADCAST_PORT 9000
#define BROADCAST_IP "192.168.124.255"
int main()
{
int sockfd;
sockfd = socket(AF_INET, SOCK_DGRAM, 0);
struct sockaddr_in srv_addr,clt_addr;
/*
srv_addr.sin_family = AF_INET;
srv_addr.sin_port = htons(SRV_PORT);
srv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
bind(sockfd,(struct sockaddr*)&srv_addr, sizeof(srv_addr));
*/
int opt = 1;
setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, (void*)&opt, sizeof(opt));
clt_addr.sin_family = AF_INET;
clt_addr.sin_port = htons(BROADCAST_PORT);
inet_pton(AF_INET, BROADCAST_IP, &clt_addr.sin_addr.s_addr);
char buf[BUFSIZ];
int i=0;
while(1)
{
sprintf(buf,"broadcast %dth message\n",i++);
sendto(sockfd, buf, strlen(buf), 0, (struct sockaddr*)&clt_addr, sizeof(clt_addr));
sleep(1);
}
return 0;
}
#include
#include
#include
#define BROADCAST_PORT 9000
int main()
{
int sockfd;
sockfd = socket(AF_INET, SOCK_DGRAM, 0);
struct sockaddr_in clt_addr;
clt_addr.sin_family = AF_INET;
clt_addr.sin_port = htons(BROADCAST_PORT);
clt_addr.sin_addr.s_addr = htonl(INADDR_ANY);
bind(sockfd,(struct sockaddr*)&clt_addr,sizeof(clt_addr));
char buf[BUFSIZ];
while(1)
{
int n = recvfrom(sockfd,buf,sizeof(buf),0,NULL,NULL);
write(STDOUT_FILENO, buf, n);
}
return 0;
}
既适用于 “局域网”, 也适用于 “广域网”。
组播组可以是永久的也可以是临时的。组播组地址中,有一部分由官方分配的,称为永久组播
组。永久组播组保持不变的是它的ip地址,组中的成员构成可以发生变化。永久组播组中成员的
数量都可以是任意的,甚至可以为零。那些没有保留下来供永久组播组使用的ip组播地址,可以
被临时组播组利用。
224.0.0.0~224.0.0.255 为预留的组播地址(永久组地址),地址224.0.0.0保留不做分配,其它地址供路由协议使用;
224.0.1.0~224.0.1.255 是公用组播地址,可以用于Internet;欲使用需申请。
224.0.2.0~238.255.255.255 为用户可用的组播地址(临时组地址),全网范围内有效;
239.0.0.0~239.255.255.255 为本地管理组播地址,仅在特定的本地范围内有效。 —— 测试学习使用 该组播地址。如:239.0.0.7
命令:ip address ---- ip ad
ip ad
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
2: ens33: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
link/ether 00:0c:29:1e:51:21 brd ff:ff:ff:ff:ff:ff
inet 192.168.124.128/24 brd 192.168.124.255 scope global dynamic ens33
valid_lft 1473sec preferred_lft 1473sec
inet6 fe80::20c:29ff:fe1e:5121/64 scope link
valid_lft forever preferred_lft forever
获取网卡对应编号
#include
unsigned int if_nametoindex(const char *ifname);
参数:网卡名称(借助 ip ad 命令查看)
返回值:网卡序号(编号)
使用 setsockopt() 函数
int setsockopt(int sockfd, int level, int optname,
const void *optval, socklen_t optlen);
// 例:
struct ip_mreqn group;
初始化 group
// 开发组播权限 --- server端
setsockopt(sockfd, IPPROTO_IP, IP_MULTICAST_IF, &group, sizeof(group));
// 加入到指定的组播组 --- client端
setsockopt(confd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &group, sizeof(group));
grep -r "struct ip_mreqn {" /usr/bin/include/ -n
,查找结构体的定义#include
struct ip_mreqn {
struct in_addr imr_multiaddr; /* 组播IP地址:如:“239.0.0.7” */
struct in_addr imr_address; /* 本机IP地址:192.168.0.105、0.0.0.0 */
int imr_ifindex; /* 网卡序号:命令获取、函数获取 */
};
// 举例:
定义组播地址:239.0.0.7
struct ip_mreqn group;
inet_pton(AF_INET, "239.0.0.7", &group.imr_multiaddr); // 设置组播IP地址
inet_pton(AF_INET, "0.0.0.0", &group.imr_address); // 设置IP地址 == INADDR_ANY
group.imr_ifindex = if_nametoindex("ens33"); // 设置网卡序号
创建套接字 sockfd = socket()
bind() 绑定服务器的ip地址和端口号,端口号是服务器端绑定的指定端口号,不可省略。
创建 struct ip_mreqn group ,并绑定组播的ip地址,设置主机ip地址,设置网卡序号
使用 setsockopt(confd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &group, sizeof(group)); 加入组播组
recvfrom() 接收客户端发送的数据,最后两个参数可传NULL,不关心发送端的地址结构
close(sockfd);
#include
#include
#include
#include
#include
#define MULTICAST_IP "239.0.0.7"
#define PORT 9001
#define SRV_PORT 8001
int main()
{
int sockfd;
sockfd = socket(AF_INET, SOCK_DGRAM, 0);
struct sockaddr_in clt_addr;
clt_addr.sin_family = AF_INET;
clt_addr.sin_port = htons(PORT);
inet_pton(AF_INET, MULTICAST_IP, &clt_addr.sin_addr.s_addr);
struct ip_mreqn group;
inet_pton(AF_INET, MULTICAST_IP, &group.imr_multiaddr);
inet_pton(AF_INET, "0.0.0.0", &group.imr_address);
group.imr_ifindex = if_nametoindex("ens33");
setsockopt(sockfd, IPPROTO_IP, IP_MULTICAST_IF, &group,sizeof(group));
int i=0;
char buf[BUFSIZ];
while(1)
{
sprintf(buf, "muticast %dth message\n", i++);
sendto(sockfd, buf, strlen(buf), 0, (struct sockaddr*)&clt_addr, sizeof(clt_addr));
sleep(1);
}
return 0;
}
#include
#include
#include
#include
#define PORT 9001
#define MULTICAST_IP "239.0.124.7"
int main()
{
int sockfd;
sockfd = socket(AF_INET, SOCK_DGRAM, 0);
struct sockaddr_in clt_addr;
clt_addr.sin_family = AF_INET;
clt_addr.sin_port = htons(PORT);
inet_pton(AF_INET, MULTICAST_IP, &clt_addr.sin_addr.s_addr);
bind(sockfd, (struct sockaddr*)&clt_addr, sizeof(clt_addr));
struct ip_mreqn group;
inet_pton(AF_INET, MULTICAST_IP, &group.imr_multiaddr);
inet_pton(AF_INET, "0.0.0.0", &group.imr_address);
group.imr_ifindex = if_nametoindex("ens33");
setsockopt(sockfd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &group, sizeof(group));
char buf[BUFSIZ];
while(1)
{
int n = recvfrom(sockfd, buf, sizeof(buf), 0, NULL, NULL);
write(STDOUT_FILENO, buf, n);
}
return 0;
}
本地套接字文件是个伪文件,不占用磁盘空间
int socket(int domain, int type, int protocol);
domain : AF_INET ----> AF_UNIX/AF_LOCAL
type : SOCK_STREAM 和 SOCK_DGRAM 均可
#include
struct sockaddr_in ------> struct sockaddr_un
struct sockaddr_un {
sa_family_t sun_family; /* AF_UNIX */
char sun_path[108]; /* Pathname */
};
sun_family = AF_UNIX;
strcpy(sun_path,"xxxx");
bind(lfd, (struct sockaddr*)&xxx_addr, 实际长度len);
实际长度len = offsetof(struct sockaddr_un, sun_path) + strlen(sun_path);
offsetof(类型,变量); #include <stddef.h> 注意:第二个参数不是具体的变量名,而是定义中的变量名
用于求类型中对应变量的偏移量,所以 offsetof(struct sockaddr_un, sun_path) 相当于AF_UNIX的大小,即为 2
注意:accept() 函数中 len的类型转换
#include
#include
#include
#include
#include
#include "wrap.h"
#define SRV_FILENAME "srv.socket"
int main()
{
int lfd, cfd, ret, len;
struct sockaddr_un srv_addr, clt_addr;
srv_addr.sun_family = AF_UNIX;
strcpy(srv_addr.sun_path, SRV_FILENAME);
len = offsetof(struct sockaddr_un, sun_path) + strlen(srv_addr.sun_path);
lfd = Socket(AF_UNIX, SOCK_STREAM, 0);
unlink(SRV_FILENAME);
Bind(lfd, (struct sockaddr*)&srv_addr, len);
Listen(lfd, 128);
cfd = Accept(lfd, (struct sockaddr*)&clt_addr, (socklen_t*)&len);
len -= offsetof(struct sockaddr_un, sun_path);
clt_addr.sun_path[len] = '\0';
printf("客户端本地套接字文件:%s\n",clt_addr.sun_path);
char buf[BUFSIZ];
while(1)
{
ret = Read(cfd, buf, sizeof(buf));
if(0 == ret)
{
Close(cfd);
printf("客户端退出\n");
break;
}
for(int i=0 ;i<ret ;++i)
{
buf[i] = toupper(buf[i]);
}
Write(cfd, buf, ret);
Write(STDOUT_FILENO, buf, ret);
}
Close(lfd);
return 0;
}
#include
#include
#include
#include
#include
#include "wrap.h"
#define SRV_FILENAME "srv.socket"
#define CLT_FILENAME "clt.socket"
int main()
{
int cfd, ret, len;
struct sockaddr_un srv_addr, clt_addr;
cfd = Socket(AF_UNIX, SOCK_STREAM, 0);
clt_addr.sun_family = AF_UNIX;
strcpy(clt_addr.sun_path, CLT_FILENAME);
len = offsetof(struct sockaddr_un, sun_path) + strlen(clt_addr.sun_path);
unlink(CLT_FILENAME);
Bind(cfd, (struct sockaddr*)&clt_addr, len);
srv_addr.sun_family = AF_UNIX;
strcpy(srv_addr.sun_path, SRV_FILENAME);
len = offsetof(struct sockaddr_un, sun_path) + strlen(srv_addr.sun_path);
Connect(cfd, (struct sockaddr*)&srv_addr, len);
printf("客户端与服务器连接成功\n");
char buf[BUFSIZ];
while(1)
{
Write(cfd, "hello\n", strlen("hello\n"));
int n = Read(cfd, buf, sizeof(buf));
Write(STDOUT_FILENO, buf, n);
sleep(1);
}
return 0;
}
网络套接字 | 本地套接字 | |
---|---|---|
server | lfd = socket(AF_INET, SOCK_STREAM, 0); | lfd = socket(AF_UNIX, SOCK_STREAM, 0); |
bzero() ---- struct sockaddr_in serv_addr; | bzero() ---- struct sockaddr_un serv_addr, clie_addr; | |
serv_addr.sin_family = AF_INET; serv_addr.sin_addr.s_addr = htonl(INADDR_ANY); serv_addr.sin_port = htons(8888); |
serv_addr.sun_family = AF_UNIX; strcpy(serv_addr.sun_path, “套接字文件名”) len = offsetof(sockaddr_un, sun_path) + strlen(serv_addr.sun_path); |
|
bind(lfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)); | unlink(“套接字文件名”); bind(lfd, (struct sockaddr *)&serv_addr, len); 创建新文件 |
|
Listen(lfd, 128); | Listen(lfd, 128); | |
cfd = Accept(lfd, ()&clie_addr, &len); | cfd = Accept(lfd, ()&clie_addr, &len); | |
client | lfd = socket(AF_INET, SOCK_STREAM, 0); | lfd = socket(AF_UNIX, SOCK_STREAM, 0); |
" 隐式绑定 获取 IP+port" | bzero() ---- struct sockaddr_un clie_addr; clie_addr.sun_family = AF_UNIX; strcpy(clie_addr.sun_path, “client套接字文件名”) len = offsetof(sockaddr_un, sun_path) + strlen(); unlink( “client套接字文件名”); bind(lfd, (struct sockaddr *)&clie_addr, len); |
|
bzero() ---- struct sockaddr_in serv_addr; | bzero() ---- struct sockaddr_un serv_addr; | |
serv_addr.sin_family = AF_INET; | serv_addr.sun_family = AF_UNIX; | |
inet_pton(AF_INT, “服务器IP”, &sin_addr.s_addr); serv_addr.sin_port = htons(“服务器端口”); |
strcpy(serv_addr.sun_path, “server套接字文件名”) len = offsetof(sockaddr_un, sun_path) + strlen(); |
|
connect(lfd, &serv_addr, sizeof()); | connect(lfd, &serv_addr, len); |