getsockopt, setsockopt 获取设置Socket选项
NAME
getsockopt, setsockopt - get and set options on sockets
SYNOPSIS
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
int getsockopt(int sockfd, int level, int optname,
void *optval, socklen_t *optlen);
/*对某一个指定的Socket的某个层面上的众多属性中的某一个属性进行设置,不同属性参数可能类型不同,所以需要指明参数类型和大小
如:对指定Socket sd 的SOL_SOCKET层面 的 SO_BROADCAST属性进行设置,即打开该属性,表示允许将数据发送至广播地址,即打开广播属性。
int val = 1;
setsockopt(sd, SOL_SOCKET, SO_BROADCAST,&val, sizeof(val));
*/
int setsockopt(int sockfd, int level, int optname,
const void *optval, socklen_t optlen);
RETURN VALUE
On success, zero is returned for the standard options. On error, -1 is returned, and errno is set appropriately.
广播篇:
man 7 socket
Socket options
The socket options listed below can be set by using setsockopt(2) and read with getsockopt(2) with the socket level set to SOL_SOCKET for all sockets. Unless otherwise noted, optval is a pointer to an int.
除非另有说明,否则optval是一个指向int型的指针
层名:SOL_SOCKET
属性:SO_BROADCAST等等:允许将数据发送至广播地址,仅限于报式套接字
...
SO_BINDTODEVICE:支持在多网卡情况下,指定走某个网卡。
SO_BROADCAST
Set or get the broadcast flag. When enabled, datagram sockets are allowed to send packets to a broadcast address. This option has no effect on stream-oriented sockets.
属性为flag
...
实验1 :全网广播
注意点:想要针对广播 进行数据发送,接收 就必须要打开SO_BROADCAST属性,对于从广播接收数据,如果不打开广播属性,有可能收的到,也有可能收不到,如果想一定收到,就必须打开SO_BROADCAST属性
这里 客户端 和 服务端 全部都打开了 SO_BROADCAST 广播属性
proto.h
#ifndef PROTO_H_
#define PROTO_H_
#define RCVPORT "1989"
#define NAMEMAX 512-8-8 // math+chinese 8 + UDP报头 8
#define IPSTRSIZE 40
struct msg_st
{
uint32_t math;
uint32_t chinese;
uint8_t name[1];
}__attribute__((pack));
#endif
rcver.c 打开全网广播属性
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <string.h>
#include <unistd.h>
#include "proto.h"
int main()
{
int sd;
struct sockaddr_in laddr,raddr;
struct msg_st *rbufp;
socklen_t raddr_len;
char ipstr[IPSTRSIZE];
int size;
//根据服务端 即将接受数据大小 申请空间 用于存储接收数据
size = sizeof(struct msg_st) + NAMEMAX - 1;// -uint8_t name[1];
rbufp = malloc(size);
if(rbufp == NULL)
{
perror("malloc()");
exit(1);
}
//创建Socket, 即确定传输协议族,传输协议,传输方式
sd = socket(AF_INET,SOCK_DGRAM,0);//0默认是IPPROTO_UDP
if(sd < 0)
{
perror("soccket()");
exit(1);
}
//对指定Socket sd 的SOL_SOCKET层面 的 SO_BROADCAST属性进行设置,即打开该属性,表示允许将数据发送至广播地址,即打开广播属性。
int val = 1;
if(setsockopt(sd, SOL_SOCKET, SO_BROADCAST,&val, sizeof(val)) < 0)
{
perror("setsockopt()");
exit(1);
}
//设置 本端,即服务端 地址信息, struct sockaddr_in结构体信息:协议族类型,端口,IP
laddr.sin_family = AF_INET;
laddr.sin_port = htons(atoi(RCVPORT));
inet_pton(AF_INET,"0.0.0.0",&laddr.sin_addr);//0.0.0.0:any address
//绑定 Socket 到 本端地址
if(bind(sd,(void *)&laddr,sizeof(laddr)) < 0)
{
perror("bind");
exit(1);
}
/* !!!! */
raddr_len = sizeof(raddr);
while(1)
{
//接受数据,数据存储于rbufp,将对端地址信息(struct sockaddr_in)存储于raddr
recvfrom(sd,rbufp,size,0,(void *)&raddr,&raddr_len);
inet_ntop(AF_INET,&raddr.sin_addr,ipstr,IPSTRSIZE);
printf("---MESSAGE FROM %s:%d---\n",ipstr,ntohs(raddr.sin_port));
printf("NAME = %s\n",rbufp->name);
printf("MATH = %d\n",ntohl(rbufp->math));
printf("CHINESE = %d\n",ntohl(rbufp->chinese));
}
close(sd);
exit(0);
}
snder.c 打开全网广播属性
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <string.h>
#include <unistd.h>
#include "proto.h"
int main(int argc,char *argv[])
{
int sd;
struct sockaddr_in raddr;
struct msg_st *sbufp;
int size;
if(argc < 3)
{
fprintf(stderr,"Usage ...\n");
exit(1);
}
//需要发送的数据包大小,即柔型数组大小
size = sizeof(struct msg_st) + strlen(argv[2]);
//根据需要发送数据包大小 申请空间
sbufp = malloc(size);
if(sbufp == NULL)
{
perror("malloc()");
exit(1);
}
//创建Socket, 即确定传输协议族,传输协议,传输方式
sd = socket(AF_INET,SOCK_DGRAM,0);//IPPROTO_UDP
if(sd < 0)
{
perror("soccket()");
exit(1);
}
//对指定Socket sd 的SOL_SOCKET层面 的 SO_BROADCAST属性进行设置,即打开该属性,表示允许将数据发送至广播地址,即打开广播属性。
int val = 1;
if(setsockopt(sd, SOL_SOCKET, SO_BROADCAST,&val, sizeof(val)) < 0)
{
perror("setsockopt()");
exit(1);
}
//bind() 可省略绑定到本端地址,系统会自动分配
// 拷贝命令行中的 名字到 数据包对应位置
strcpy(sbufp->name,argv[2]);
sbufp->math = htonl(rand()%100);
sbufp->chinese = htonl(rand()%100);
//设置 服务端地址信息, struct sockaddr_in结构体信息:协议族类型,端口,IP
raddr.sin_family = AF_INET;
raddr.sin_port = htons(atoi(RCVPORT));
//地址为广播地址,255.255.255.255 广播地址
inet_pton(AF_INET,"255.255.255.255",&raddr.sin_addr);
//向 服务端 发送数据
if(sendto(sd,sbufp,size,0,(void *)&raddr,sizeof(raddr)) < 0)
{
perror("sendto()");
exit(1);
}
puts("OK");
close(sd);
exit(0);
}
组播篇:
man 7 ip
Socket options
IP supports some protocol-specific socket options that can be set with setsockopt(2) and read with getsockopt(2). The socket option level for IP is IPPROTO_IP. A boolean integer flag is zero when it is false, otherwise true.
层名:IPPROTO_IP
属性:
IP_ADD_MEMBERSHIP (since Linux 1.2) 加入多播组
Join a multicast group. Argument is an ip_mreqn structure.
struct ip_mreqn {
/* 多播组地址 大整数 IP multicast group address */
struct in_addr imr_multiaddr;
/* 当前自己IP地址 IP address of local interface */
struct in_addr imr_address;
/*网络索引号(读取命令: ip ad sh) interface index
if_nametoindex():名字转换成索引号
unsigned int if_nametoindex(const char *ifname);
*/
int imr_ifindex;
};
/* 大整数 Internet address. */
struct in_addr {
uint32_t s_addr; /* address in network byte order */
};
离开多播组:
IP_ADD_SOURCE_MEMBERSHIP (since Linux 2.4.22 / 2.5.68)
IP_MULTICAST_IF (since Linux 1.2) 创建多播组
proto.h
#ifndef PROTO_H_
#define PROTO_H_
#define MGROUP "224.2.2.2" //多播地址
#define RCVPORT "1989"
#define NAMEMAX 512-8-8
#define IPSTRSIZE 40
struct msg_st
{
uint32_t math;
uint32_t chinese;
uint8_t name[1];
}__attribute__((pack));
#endif
snder.c 客户端
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <string.h>
#include <unistd.h>
#include "proto.h"
int main(int argc,char *argv[])
{
int sd;
struct sockaddr_in raddr;
struct msg_st *sbufp;
int size;
if(strlen(argv[2]) > NAMEMAX)
{
fprintf(stderr,"Name is too long!\n");
exit(1);
}
size = sizeof(struct msg_st) + strlen(argv[2]);
sbufp = malloc(size);
if(sbufp == NULL)
{
perror("malloc()");
exit(1);
}
sd = socket(AF_INET,SOCK_DGRAM,0);//IPPROTO_UDP
if(sd < 0)
{
perror("soccket()");
exit(1);
}
//设置 创建多播组需要的信息
struct ip_mreqn mreq;
inet_pton(AF_INET,MGROUP,&mreq.imr_multiaddr);//设置多播地址
inet_pton(AF_INET,"0.0.0.0",&mreq.imr_address);//设置当前自己IP地址
mreq.imr_ifindex = if_nametoindex("eth0");//设置网络设备索引号 指定所用网卡名
//对指定Socket sd 的IPPROTO_IP层面 的IP_MULTICAST_IF属性进行设置,表示创建一个多播组。mreq参数为创建多播组需要的信息。
if(setsockopt(sd, IPPROTO_IP, IP_MULTICAST_IF,&mreq, sizeof(mreq)) < 0)
{
perror("setsockopt()");
exit(1);
}
//bind()
strcpy(sbufp->name,argv[2]);
sbufp->math = htonl(rand()%100);
sbufp->chinese = htonl(rand()%100);
raddr.sin_family = AF_INET;
raddr.sin_port = htons(atoi(RCVPORT));
inet_pton(AF_INET,MGROUP,&raddr.sin_addr);//对端地址为多播组地址
if(sendto(sd,sbufp,size,0,(void *)&raddr,sizeof(raddr)) < 0)
{
perror("sendto()");
exit(1);
}
puts("OK");
close(sd);
exit(0);
}
rcver.c
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <string.h>
#include <unistd.h>
#include "proto.h"
int main()
{
int sd;
struct sockaddr_in laddr,raddr;
struct msg_st *rbufp;
socklen_t raddr_len;
char ipstr[IPSTRSIZE];
int size;
size = sizeof(struct msg_st) + NAMEMAX - 1;// -uint8_t name[1];
rbufp = malloc(size);
if(rbufp == NULL)
{
perror("malloc()");
exit(1);
}
sd = socket(AF_INET,SOCK_DGRAM,0);//IPPROTO_UDP
if(sd < 0)
{
perror("soccket()");
exit(1);
}
//设置 加入多播组需要的信息
struct ip_mreqn mreq;
inet_pton(AF_INET,MGROUP,&mreq.imr_multiaddr);//设置多播地址
inet_pton(AF_INET,"0.0.0.0",&mreq.imr_address);//设置当前自己IP地址
mreq.imr_ifindex = if_nametoindex("eth0");//设置网络索引号
//对指定Socket sd 的IPPROTO_IP层面 的IP_ADD_MEMBERSHIP属性进行设置,表示加入一个多播组,mreq参数为加入多播组需要的信息。
if(setsockopt(sd, IPPROTO_IP, IP_ADD_MEMBERSHIP,&mreq, sizeof(mreq)) < 0)
{
perror("setsockopt()");
exit(1);
}
laddr.sin_family = AF_INET;
laddr.sin_port = htons(atoi(RCVPORT));
inet_pton(AF_INET,"0.0.0.0",&laddr.sin_addr);//0.0.0.0:any address
if(bind(sd,(void *)&laddr,sizeof(laddr)) < 0)
{
perror("bind");
exit(1);
}
/* !!!! */
raddr_len = sizeof(raddr);
while(1)
{
recvfrom(sd,rbufp,size,0,(void *)&raddr,&raddr_len);
inet_ntop(AF_INET,&raddr.sin_addr,ipstr,IPSTRSIZE);
printf("---MESSAGE FROM %s:%d---\n",ipstr,ntohs(raddr.sin_port));
printf("NAME = %s\n",rbufp->name);
printf("MATH = %d\n",ntohl(rbufp->math));
printf("CHINESE = %d\n",ntohl(rbufp->chinese));
}
close(sd);
exit(0);
}