APUE
命令:netstat(查看网络状态)
-anu :查看udp(报式套接字)
-ant:查看tcp(流式套接字)
网络IPC:套接字
查看网络设备命令:ip ad sh -> 查看设备索引号 ,ifconfig
IPC_PRIVATE:匿名ipc
/proc/
一、套接字描述符
1.1 socket()
描述:创建一个端口去通信(用协议族domain 中的某个协议protocol 来完成type类型的传输)
手册:man socket
头文件:
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
函数原型:
int socket(int domain, int type, int protocol);
-
domain – 协议族
名字 目的 手册 AF_UNIX,AF_LOCAL 本地通讯 unix(7) AF_INET IPV4 ip(7) AF_INET6 IPV6 ipv6(7) AF_NETLINK 内核和用户通信 netlink(7) AF_PACKET 底层传输(网卡驱动) packet(7) -
type – 实现方式
种类 功能 SOCK_STREAM 流式(有序,可靠:收到的内容顺序一定是对的,双工,基于链接:一对一,字节流式传输) SOCK_DGRAM 报式(通常用来传结构体,无连接的) SOCK_SEQPACKET 有序分组(有序,可靠,双工,基于链接) SOCK_RAW 原始套接字 SOCK_RDM 可靠数据:能接到内容,顺序不确定 SOCK_PACKET 已废弃 -
protocol–协议(0:domain协议族中默认支持type的协议)
返回值:
成功:一个文件(套接字)描述符
失败:-1,errno
二、字节序
2.1 处理字节序和网络字节序转换函数
只要是走网络的非单字节数据就要注意字节序,主机序->网络序
手册:man htonl
头文件:
#include <arpa/inet.h>
函数原型:
uint32_t htonl(uint32_t hostlong); //以网络字节表示的32位整数
uint16_t htons(uint16_t hostshort); //以网络字节表示的16位整数
uint32_t ntohl(uint32_t netlong); //以主机字节表示的32位整数
uint16_t ntohs(uint16_t netshort); //以主机字节表示的16位整数
- h – 主机
- n – 网络
- l – 长整型
- s – 短整型
三、将套接字与地址关联
0.0.0.0:能够匹配任何的IP地址,表示在当前这个时刻,我们自己的IP地址是多少,0.0.0.0 就会替换成我们当前的IP地址
每个包最少含有一个ip和一个端口
端口有65536个
推荐使用1024以上的端口
udp包推荐报头长度 -
udp包报头长度:512
3.1 bind()
描述:将套接字与地址关联
手册:man bind
头文件:
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
函数原型:
int bind(int sockfd, const struct sockaddr *addr,
socklen_t addrlen);
-
sockfd – 获取 SOCKET 返回的文件描述符
-
addr – 协议地址,依赖于当前用到的协议族中的地址信息,AF_INET 协议族中的 协议地址类型为 struct sockaddr_in
//sockaddr 依赖于当前用到的协议族中的地址信息 struct sockaddr { sa_family_t sa_family; char sa_data[14]; }
不同的协议族绑定自己这端的地址,所用的结构体是不一样的。所以是不存在 struct sockaddr 类型的。我们的处理方式是:我们用的是哪一个协议族,就把该协议族地址作为addr ,然后再把地址长度写到addrlen
For AF_INET,see ip(7); for AF_INET6, see ipv6(7); for AF_UNIX, see unix(7); forAF_APPLETALK, see ddp(7); for AF_PACKET, see packet(7); for AF_X25, seex25(7); and for AF_NETLINK, see netlink(7).
例:
AF_INET,see ip(7)
struct sockaddr_in { sa_family_t sin_family; /* 协议族 address family: AF_INET */ in_port_t sin_port; /* 需要的端口 port in network byte order */ //IP地址 并非点分式,而是大整数internet address ,用的时候需要格式转换:inet_pton() struct in_addr sin_addr; /* internet address */ }; /* Internet address. */ struct in_addr { uint32_t s_addr; /* address in network byte order */ };
-
addrlen – 协议地址空间大小
返回值:
成功:0
失败:-1,errno
3.2 inet_pton() & inet_ntop()
描述: IP地址格式转换:点分式 --> 大整数 & 大整数格式 --> 点分式格式
手册:man inet_pton
& man inet_ntop
头文件:
#include <arpa/inet.h>
函数原型:
int inet_pton(int af, const char *src, void *dst);
const char *inet_ntop(int af, const void *src,
char *dst, socklen_t size);
-
af – 协议族,只能是 AF_INET or AF_INET6,即IPV4或者IPV6
-
src – IP地址
-
dst – 转换之后的存储空间
-
size – dst指向的空间大小
返回值:
成功:inet_ntop返回dst首地址,inet_pton返回1
失败:inet_ntop返回NULL,inet_pton返回-1,errno = EAFNOSUPPORT
四、报式套接字传输
跨主机通信注意:
主动端:先发包的一方
1 SOCKET:socket()
2 给SOCKET取得地址:bind() (可省)
3 发/收消息:recvfrom()
4 关闭:close()被动端:先收包的一方(先运行起来)
1 SOCKET:socket()
2 给SOCKET取得地址:bind() (不可省)
3 收/发消息:sendto()
4 关闭:close()为什么 主动端的 bind()可以省略呢?
bind() 是给SOCKET取得地址,即绑定本地地址。这个操作是和本机的约定。
发送端如果不和本机约定地址,即省略bind()操作。当前SOCKET建立成功后。系统会为我们分配一个可用的空闲的地址给我们用,在进程结束之前 该端口一直给我们用。
4.1 recvfrom()
recvfrom() 用于报式套接字通信,接收的每一个消息来源可能不一致,所以除了需要指定目标SOCKET,和存储信息的位置即大小,以及有无特殊要求之外。还需要记录对方是谁,即发送端身份。
描述: 从 SOCKET上接收信息
手册:man recvfrom
头文件:
#include <sys/types.h>
#include <sys/socket.h>
函数原型:
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,
struct sockaddr *src_addr, socklen_t *addrlen);
-
sockfd – 目标套接字文件描述符
-
buf – 接收信息存储地址
-
len – 接收信息存储空间大小
-
flags – 特殊要求
-
src_addr – 用于保存发送端的地址,即保存对面端的地址,接收到消息的时候保存
-
addrlen – 发送端地址空间大小
返回值:
成功:接收到的字节数
失败:-1,errno
4.2 sendto()
用于报式套接字发送数据
描述:发送数据到socket
手册:man sendto
头文件:
#include <sys/types.h>
#include <sys/socket.h>
函数原型:
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,
const struct sockaddr *dest_addr, socklen_t addrlen);
sockfd – 目标套接字文件描述符
buf – 发送数据信息存储地址
len – 发送数据信息存储空间大小
flags – 特殊要求
dest_addr – 接收端的地址,即对端地址
addrlen – 接收端地址空间大小
返回值:
成功:发送的字节数
失败:-1,errno
4.3 代码演示
功能:报式套接字传输数据
proto.h
#ifndef PROTO_H__
#define PROTO_H__
#include <stdint.h>
#define RCVER_PORT "2989"
#define NAMESIZE 13
struct msg_st
{
uint8_t name[NAMESIZE];
uint32_t math;
uint32_t chinese;
}__attribute__((packed));
#endif
rcver.c 接收端
#include <stdio.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include "proto.h"
#define IPSTRSIZE 128
int main()
{
int sd;
struct sockaddr_in laddr,raddr;
struct msg_st rbuf;
socklen_t raddr_len;
char ipstr[IPSTRSIZE];
sd = socket(AF_INET,SOCK_DGRAM,0/*IPPROTO_UDP*/);
if(sd < 0)
{
perror("socket()");
exit(1);
}
laddr.sin_family = AF_INET;
laddr.sin_port = htons(atoi(RCVER_PORT));
inet_pton(AF_INET,"0.0.0.0",&laddr.sin_addr);
if(bind(sd,(void *)&laddr,sizeof(laddr)) < 0)
{
perror("bind()");
exit(1);
}
raddr_len = sizeof(raddr);
while(1)
{
if(recvfrom(sd,&rbuf,sizeof(rbuf),0,(void *)&raddr,&raddr_len) < 0)
{
perror("recvfrom()");
exit(1);
}
inet_ntop(AF_INET,&raddr.sin_addr,ipstr,IPSTRSIZE);
printf("-----MESSAGE FROM:%s:%d-------\n",ipstr,ntohs(raddr.sin_port));
printf("NAME:%s\n",rbuf.name);
printf("MATH:%d\n",ntohl(rbuf.math));
printf("CHINESE:%d\n",ntohl(rbuf.chinese));
}
close(sd);
exit(0);
}
snder.c发送端
#include <stdio.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <string.h>
#include "proto.h"
#define IPSTRSIZE 128
int main(int argc,char *argv[])
{
int sd;
struct sockaddr_in raddr;
struct msg_st sbuf;
if(argc < 2)
{
fprintf(stderr,"Usage...\n");
exit(1);
}
sd = socket(AF_INET,SOCK_DGRAM,0/*IPPROTO_UDP*/);
if(sd < 0)
{
perror("socket()");
exit(1);
}
//bind();
memset(&sbuf,'\0' ,sizeof(sbuf));
strcpy(sbuf.name,"Alan");
sbuf.math = htonl(rand()%100);
sbuf.chinese = htonl(rand()%100);
raddr.sin_family = AF_INET;
raddr.sin_port = htons(atoi(RCVER_PORT));
inet_pton(AF_INET,argv[1],&raddr.sin_addr);
if(sendto(sd,&sbuf,sizeof(sbuf),0,(void *)&raddr,sizeof(raddr)) < 0)
{
perror("sendto()");
exit(1);
}
puts("OK");
close(sd);
exit(0);
}
五、动态报式套接字传输
注意,只要涉及到网络传输内容,传输的内容从来不会用到指针,因为如果是跨主机传输,两个设备上的某个地址中的数据是不同的,传递指针会真的传一个地址字符串过去,而不是地址里的内容
代码演示:
proto.h
#ifndef PROTO_H__
#define PROTO_H__
#include <stdint.h>
#define RCVER_PORT "2989"
#define MSGMAX (512-8)
#define NAMEMAX (MSGMAX-8)
struct msg_st
{
uint32_t math;
uint32_t chinese;
uint8_t name[1];
}__attribute__((packed));
#endif
rcver.c
#include <stdio.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include "proto.h"
#define IPSTRSIZE 128
int main()
{
int sd;
struct sockaddr_in laddr,raddr;
struct msg_st *rbuf;
socklen_t raddr_len;
char ipstr[IPSTRSIZE];
rbuf = malloc(MSGMAX);
/*if error*/
sd = socket(AF_INET,SOCK_DGRAM,0/*IPPROTO_UDP*/);
if(sd < 0)
{
perror("socket()");
exit(1);
}
laddr.sin_family = AF_INET;
laddr.sin_port = htons(atoi(RCVER_PORT));
inet_pton(AF_INET,"0.0.0.0",&laddr.sin_addr);
if(bind(sd,(void *)&laddr,sizeof(laddr)) < 0)
{
perror("bind()");
exit(1);
}
raddr_len = sizeof(raddr);
while(1)
{
if(recvfrom(sd,rbuf,MSGMAX,0,(void *)&raddr,&raddr_len) < 0)
{
perror("recvfrom()");
exit(1);
}
inet_ntop(AF_INET,&raddr.sin_addr,ipstr,IPSTRSIZE);
printf("-----MESSAGE FROM:%s:%d-------\n",ipstr,ntohs(raddr.sin_port));
printf("NAME:%s\n",rbuf->name);
printf("MATH:%d\n",ntohl(rbuf->math));
printf("CHINESE:%d\n",ntohl(rbuf->chinese));
}
close(sd);
free(rbuf);
exit(0);
}
snder.c
#include <stdio.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <string.h>
#include "proto.h"
#define IPSTRSIZE 128
int main(int argc,char *argv[])
{
int sd,size;
struct sockaddr_in raddr;
struct msg_st *sbuf;
if(argc < 3)
{
fprintf(stderr,"Usage...\n");
exit(1);
}
if(strlen(argv[2]) > NAMEMAX-1)
{
fprintf(stderr,"Name is too long.\n");
exit(1);
}
size = sizeof(struct msg_st) + strlen(argv[2]);
sbuf = malloc(size);
/*if error*/
sd = socket(AF_INET,SOCK_DGRAM,0/*IPPROTO_UDP*/);
if(sd < 0)
{
perror("socket()");
exit(1);
}
//bind();
strcpy(sbuf->name,argv[2]);
sbuf->math = htonl(rand()%100);
sbuf->chinese = htonl(rand()%100);
raddr.sin_family = AF_INET;
raddr.sin_port = htons(atoi(RCVER_PORT));
inet_pton(AF_INET,argv[1],&raddr.sin_addr);
if(sendto(sd,sbuf,size,0,(void *)&raddr,sizeof(raddr)) < 0)
{
perror("sendto()");
exit(1);
}
puts("OK");
close(sd);
free(sbuf);
exit(0);
}
六、流式套接字传输
五元信息组:cookie
netstat
6.1 listen()
描述: 监听
手册:man listen
头文件:
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
函数原型:
int listen(int sockfd, int backlog);
-
sockfd – 套接字文件描述符
-
backlog – 能够建立全连接总的数量,(原半连接池,已废弃)
返回值:
成功:0
失败:-1,errno
6.2 connect()
描述:通过socket建立连接
手册:man connect
头文件:
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
函数原型:
int connect(int sockfd, const struct sockaddr *addr,
socklen_t addrlen);
- sockfd – 套接字文件描述符
- addr:对端地址信息(根据fd内的协议族查询对应的)
- addrlen:对端地址长度
返回值:
成功:当前建立链接的套接字文件描述符
失败:-1 , errno
6.3 accept()
描述:通过socket接受链接(对方发起链接,我接受链接并且记录对方位置)
手册:man accept
头文件:
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
函数原型:
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
-
sockfd – 套接字文件描述符
-
addr – 对端地址信息
-
addrlen – 对端地址长度
返回值:
成功:当前建立链接的套接字文件描述符
失败:-1 , errno
6.4 recv()
recv()应用于流式套接字,只需要指定目标SOCKET,和存储信息的位置即大小,以及有无特殊要求即可。因为是提前建立好链接的,一对一,点对点的连接方式,所以不用记录 对端是谁。
描述: 从 SOCKET上接收信息
手册:man recv
头文件:
#include <sys/types.h>
#include <sys/socket.h>
函数原型:
ssize_t recv(int sockfd, void *buf, size_t len, int flags);
-
sockfd – 目标套接字文件描述符
-
buf – 接收信息存储地址
-
len – 接收信息存储空间大小
-
flags – 特殊要求
返回值:
成功:接收到的字节数
失败:-1,errno
6.5 send()
描述:用于流式套接字发送数据
手册:man send
头文件:
#include <sys/types.h>
#include <sys/socket.h>
函数原型:
ssize_t send(int sockfd, const void *buf, size_t len, int flags);
-
sockfd – 套接字文件描述符
-
buf – 待发送的数据空间
-
len – 发送数据信息存储空间大小
-
flags – 特殊要求(0:跟着默认走)
返回值:
成功:发送的字节数
失败:-1,errno
七、多点通信
广播:全网广播,子网广播(广播地址:255.255.255.255)
组播:也称多播,多播组(类似群组)(组播地址[d类地址,224开头]:224.2.2.2)
man 7 tcp ip socket
socket options:存放着广播多播的开关
7.1 setsockopt() & getsockopt()
描述:设置Socket选项 & 获取Socket选项
手册:man setsockopt
头文件:
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
函数原型:
int getsockopt(int sockfd, int level, int optname,
void *optval, socklen_t *optlen);
int setsockopt(int sockfd, int level, int optname,
const void *optval, socklen_t optlen);
-
sockfd – 目标套接字文件描述符
-
level – 哪一层上的哪一属性(Socket options描绘中的)
层名:SOL_SOCKET
属性:
SO_BROADCAST等等:允许将数据发送至广播地址,仅限于报式套接字
SO_BINDTODEVICE:支持在多网卡情况下,指定走某个网卡。
-
optname – 开关(Socket options描绘中的)
-
optval – 要传参数的起始位置(依据optname的描述初始化)
-
optlen – 传参的长度
返回值:
成功:0
失败:-1,errno
7.2 if_nametoindex() & if_indextoname()
描述:设备名转设备索引号
头文件:
#include <net/if.h>
函数原型:
unsigned int if_nametoindex(const char *ifname);
char *if_indextoname(unsigned int ifindex, char *ifname);
- ifname – 设备名
- ifindex – 索引号
返回值:
成功:if_nametoindex()返回索引号,if_indextoname()返回设备名
失败:if_nametoindex()返回0设置errno,if_indextoname()返回NULL设置errno
7.3 代码演示
7.3.1 功能: 全网广播
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
...
proto.h
#ifndef PROTO_H__
#define PROTO_H__
#include <stdint.h>
#define RCVER_PORT "2989"
#define NAMESIZE 13
struct msg_st
{
uint8_t name[NAMESIZE];
uint32_t math;
uint32_t chinese;
}__attribute__((packed));
#endif
rcver.c 接收端打开全网广播属性
#include <stdio.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include "proto.h"
#define IPSTRSIZE 128
int main()
{
int sd;
struct sockaddr_in laddr,raddr;
struct msg_st rbuf;
socklen_t raddr_len;
char ipstr[IPSTRSIZE];
sd = socket(AF_INET,SOCK_DGRAM,0/*IPPROTO_UDP*/);
if(sd < 0)
{
perror("socket()");
exit(1);
}
laddr.sin_family = AF_INET;
laddr.sin_port = htons(atoi(RCVER_PORT));
inet_pton(AF_INET,"0.0.0.0",&laddr.sin_addr);
if(bind(sd,(void *)&laddr,sizeof(laddr)) < 0)
{
perror("bind()");
exit(1);
}
raddr_len = sizeof(raddr);
while(1)
{
if(recvfrom(sd,&rbuf,sizeof(rbuf),0,(void *)&raddr,&raddr_len) < 0)
{
perror("recvfrom()");
exit(1);
}
inet_ntop(AF_INET,&raddr.sin_addr,ipstr,IPSTRSIZE);
printf("-----MESSAGE FROM:%s:%d-------\n",ipstr,ntohs(raddr.sin_port));
printf("NAME:%s\n",rbuf.name);
printf("MATH:%d\n",ntohl(rbuf.math));
printf("CHINESE:%d\n",ntohl(rbuf.chinese));
}
close(sd);
exit(0);
}
snder.c 发送端推荐也打开全网广播属性
#include <stdio.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <string.h>
#include "proto.h"
#define IPSTRSIZE 128
int main(int argc,char *argv[])
{
int sd;
struct sockaddr_in raddr;
struct msg_st sbuf;
sd = socket(AF_INET,SOCK_DGRAM,0/*IPPROTO_UDP*/);
if(sd < 0)
{
perror("socket()");
exit(1);
}
//bind();
int val = 1;
if(setsockopt(sd,SOL_SOCKET,SO_BROADCAST,&val,sizeof(val)) < 0)
{
perror("setsockopt()");
exit(1);
}
memset(&sbuf,'\0' ,sizeof(sbuf));
strcpy(sbuf.name,"Alan");
sbuf.math = htonl(rand()%100);
sbuf.chinese = htonl(rand()%100);
raddr.sin_family = AF_INET;
raddr.sin_port = htons(atoi(RCVER_PORT));
inet_pton(AF_INET,"255.255.255.255",&raddr.sin_addr);
if(sendto(sd,&sbuf,sizeof(sbuf),0,(void *)&raddr,sizeof(raddr)) < 0)
{
perror("sendto()");
exit(1);
}
puts("OK");
close(sd);
exit(0);
}
7.3.2 功能:组播
IP_ADD_MEMBERSHIP (since Linux 1.2) 加入多播组
Join a multicast group. Argument is an ip_mreqn structure.
struct ip_mreqn {
/* 多播组地址 大整数 IP地址 IP multicast group address */
struct in_addr imr_multiaddr;
/* 当前自己IP地址 IP address of local interface */
struct in_addr imr_address;
/*网络索引号*/
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__
#include <stdint.h>
#define MGROUP "224.2.2.2"
#define RCVER_PORT "2989"
#define NAMESIZE 13
struct msg_st
{
uint8_t name[NAMESIZE];
uint32_t math;
uint32_t chinese;
}__attribute__((packed));
#endif
snder.c 发送端加入组
#include <stdio.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <string.h>
#include <net/if.h>
#include "proto.h"
#define IPSTRSIZE 128
int main(int argc,char *argv[])
{
int sd;
struct sockaddr_in raddr;
struct msg_st sbuf;
sd = socket(AF_INET,SOCK_DGRAM,0/*IPPROTO_UDP*/);
if(sd < 0)
{
perror("socket()");
exit(1);
}
//bind();
struct ip_mreqn mreq;
inet_pton(AF_INET,MGROUP, &mreq.imr_multiaddr);
inet_pton(AF_INET,"0.0.0.0", &mreq.imr_address);
mreq.imr_ifindex = if_nametoindex("ens33");
if(setsockopt(sd,IPPROTO_IP,IP_ADD_MEMBERSHIP,&mreq,sizeof(mreq)) < 0)
{
perror("setsockopt()");
exit(1);
}
memset(&sbuf,'\0' ,sizeof(sbuf));
strcpy(sbuf.name,"Alan");
sbuf.math = htonl(rand()%100);
sbuf.chinese = htonl(rand()%100);
raddr.sin_family = AF_INET;
raddr.sin_port = htons(atoi(RCVER_PORT));
inet_pton(AF_INET,MGROUP,&raddr.sin_addr);
if(sendto(sd,&sbuf,sizeof(sbuf),0,(void *)&raddr,sizeof(raddr)) < 0)
{
perror("sendto()");
exit(1);
}
puts("OK");
close(sd);
exit(0);
}
rcver.c 接收端加入组
#include <stdio.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <net/if.h>
#include "proto.h"
#define IPSTRSIZE 128
int main()
{
int sd;
struct sockaddr_in laddr,raddr;
struct msg_st rbuf;
socklen_t raddr_len;
char ipstr[IPSTRSIZE];
sd = socket(AF_INET,SOCK_DGRAM,0/*IPPROTO_UDP*/);
if(sd < 0)
{
perror("socket()");
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);
mreq.imr_ifindex = if_nametoindex("ens33");
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(RCVER_PORT));
inet_pton(AF_INET,"0.0.0.0",&laddr.sin_addr);
if(bind(sd,(void *)&laddr,sizeof(laddr)) < 0)
{
perror("bind()");
exit(1);
}
raddr_len = sizeof(raddr);
while(1)
{
if(recvfrom(sd,&rbuf,sizeof(rbuf),0,(void *)&raddr,&raddr_len) < 0)
{
perror("recvfrom()");
exit(1);
}
inet_ntop(AF_INET,&raddr.sin_addr,ipstr,IPSTRSIZE);
printf("-----MESSAGE FROM:%s:%d-------\n",ipstr,ntohs(raddr.sin_port));
printf("NAME:%s\n",rbuf.name);
printf("MATH:%d\n",ntohl(rbuf.math));
printf("CHINESE:%d\n",ntohl(rbuf.chinese));
}
close(sd);
exit(0);
}