嵌入式软件开发 day25(套接字)

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_INETIPV4ip(7)
    AF_INET6IPV6ipv6(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);
}



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值