UDP协议下:单一socket复用 IPV4与IPV6地址
2014-04-03 17:04
513 查看
最近学习套接字编程,简单的总结了一下IPV4与IPV6地址复用的工作。
对于希望复用ipv4与ipv6地址的服务器来说,比较好的做法是将domain参数定义为AF_INET6。
getaddrinfo(char * host,char * server,struct addrinfo * hint,struct addrinfo ** result)
该函数将返回一个 struct addrinfo * 结构体指针链表,其表头地址储存于 struct addrinfo ** result 中。
下面简单介绍一下linux中struct addrinfo的实现
Struct addrinfo
{
int ai_flags;
int ai_family;
int ai_socktype;
int ai_protocol;
socklen_len ai_addrlen;
struct sockaddr * ai_addr;
char *ai_canonname;
struct addrinfo * ai_next;
}
该结构体各成员具体含义稍后介绍。
再来观察getaddrinfo()函数,其中参数 struct addrinfo * hint可以理解为一个用于过滤返回内容的参数。只有与hint属性匹配的addrinfo *才回被添加到返回的链表中。
回头来介绍一下struct addrinfo中各成员的含义,以及在调用getaddrinfo()函数时,hint中可以被赋值从而用于过滤的成员。
AI_PASSIVE 套接字将用于被动打开(或监听绑定),通常配置于server端。
AI_CANONNAME 告知getaddrinfo()函数返回主机的规范名字。
AI_ADDRCONFIG 按照所在主机的配置选择返回地址类型。也就是说返回除本机回馈接口以外的网络接口配置的IP地址版本一致的IP地址。
AF_INET6 指定返回IPV6地址信息;
AF_UNSPEC 未指定返回IP地址版本,也就是说符合hint过滤条件的所有地址信息都将返回。
SOCK_STREAM 简单理解为TCP
以上4个成员为getaddrinfo()函数的调用者可以在hint中配置的选项。
值得注意的是,在通用IPV4与IPV6地址的服务器中,调用getaddrinfo()函数时,通常将主机地址设置为NULL,并且hint->ai_flag = AI_PASSIVE,hint->ai_family = AF_INET6,这样将会得到本机的通配地址,并且套接字用于被动接受数据。从而支持IPV4与IPV6地址的通用。
下面给出一个调用getaddrinfo()函数返回地址信息的简单用例
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <netinet/in.h>
void printf_family(struct addrinfo * aip)
{
……
}
void printf_type (struct addrinfo *aip)
{
……
}
void printf_protocol(struct addrinfo * aip)
{
……
}
void printf_flags(struct addrinfo * aip)
{
……
}
int main(int argc , char * argv[])
{
struct addrinfo *ailist, * aip;
struct addrinfo hint;
const char * addr;
int err;
char abuf_v4[INET_ADDRSTRLEN]; // IPV4
char abuf_v6[INET6_ADDRSTRLEN];//IPV6
hint.ai_flags = 0 ;
hint.ai_family = AF_UNSPEC;
hint.ai_socktype = SOCK_DGRAM;
hint.ai_protocol = 0;
hint.ai_addrlen = 0;
hint.ai_canonname = NULL;
hint.ai_addr = NULL;
hint.ai_next =NULL;
if((err = getaddrinfo(argv[1],argv[2],&hint,&ailist)) != 0)
{
printf("get addrinfo err \n %s\n",gai_strerror(err));
return 0;
}
for(aip = ailist; aip != NULL ; aip = aip->ai_next )
{
printf_flags(aip);
printf_family(aip);
printf_type(aip);
printf_protocol(aip);
printf("\n");
printf("%s",aip->ai_canonname);
printf("\n");
int sfd = socket(aip->ai_family, aip->ai_socktype,
aip->ai_protocol);
if (sfd == -1)
continue;
}
exit (0);
}
将上面配置的socket与getaddrinfo()函数返回的地址信息绑定。
recvfrom(int sockfd,,char * buf, int bufsize,int flag,struct sockaddr *peer_addr, socklen_t * len);
该函数接受客户端发送到本机地址的数据,并通过peer_addr,返回客户端地址,len保存该地址信息有效长度。
对于同时兼容IPV4以及IPV6地址的服务器,需要处理不同版本的IP地址,因此需要引用一个通用的结构体用来保存两种不同长度的地址信息,所以有比较介绍一下struct sockaddr_storage结构
不同于struct sockaddr,新的struct sockaddr_storage足以容纳系统所支持的任何套接字地址结构,sockaddr_storage结构在<netinet/in.h>头文件中定义
struct sockaddr_storage
{
uint8_t ss_len; /* length of this struct (implementation dependent) */
sa_family_t ss_family; /* address family: AF_xxx value */
/*其他字段对用户来说是透明的 故没有列出*/
};
sockaddr_storage和sockaddr的主要差别:
sockaddr_storage通用套接字地址结构满足对齐要求
sockaddr_storage通用套接字地址结构足够大,能够容纳系统支持的任何套接字地址结构。
上面使用recvfrom函数,通过struct sockaddr_storage * peer_addr返回了客户端地址信息,从而可以使用函数:
Sendto (int sockfd, void * buf, int size, int flags,struct sockaddr * peer_addr, socklen_t peer_addr_len)
向客户端返回消息。
经过以上流程,就可以完成一个简单的兼容IPV4、IPV6地址的UDP服务器。
与服务器端一样,对于希望复用ipv4与ipv6地址的用户,比较好的做法是将domain参数定义为AF_INET6。
将上面配置的socket与getaddrinfo()函数返回的地址信息绑定。如果将套接字绑定到本机的一个特定IP(IPV4或IPV6地址),将无法实现IP地址的通用,为了解决这个问题,可将套接字绑定为本机的通配地址(0.0.0.0 or 0::0)。
或者,可以使用套接字选项中的SO_BINDTODEVICE选项,将套接字绑定至固定的网卡。
客户端使用recvfrom函数接收服务器传回的消息。
至此,我们完成了一个可通用IPV4与IPV6地址的服务器---客户端模型。
下面是简单的代码,基本借用了man getaddrinfo中的用例。通过man getaddrinfo 可以得到更加详细的说明。
服务器端:
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/socket.h>
#include <netdb.h>
#define BUF_SIZE 500
#ifndef HOST_NAME_MAX
#define HOST_NAME_MAX 256
#endif
void printf_family(struct addrinfo * aip)
{
printf(" family ");
switch (aip->ai_family)
{
case AF_INET:
printf("ip_v4");
break;
case AF_INET6:
printf("ip_v6");
break;
default:
printf("unknown");
}
}
void printf_type (struct addrinfo *aip)
{
printf(" type ");
switch (aip->ai_socktype)
{
case SOCK_STREAM:
printf("stream");
break;
case SOCK_DGRAM:
printf("datagram");
break;
case SOCK_SEQPACKET:
printf("seqpacket");
break;
case SOCK_RAW:
printf("raw");
break;
default:
printf("unknown (%d)",aip->ai_socktype);
}
}
void printf_protocol(struct addrinfo * aip)
{
printf(" protocol ");
switch (aip->ai_protocol)
{
case 0:
printf("default");
break;
case IPPROTO_TCP:
printf("TCP");
break;
case IPPROTO_UDP:
printf("UDP");
break;
case IPPROTO_RAW:
printf("RAW");
break;
default:
printf("unknown (%d)",aip->ai_protocol);
}
}
void printf_flags(struct addrinfo * aip)
{
printf(" flags ");
if(aip->ai_flags == 0)
printf(" 0 ");
else
{
if (aip->ai_flags & AI_PASSIVE)
{
printf("
baa7
passive");
return;
}
if (aip->ai_flags & AI_CANONNAME)
{
printf(" canon");
}
else
{
printf(" unknown");
}
}
}
//________________________________________//
int main(int argc, char *argv[])
{
struct addrinfo hints;
struct addrinfo *result, *rp;
int sfd, s;
struct sockaddr_storage peer_addr;
socklen_t peer_addr_len;
ssize_t nread;
char buf[BUF_SIZE];
if (argc != 2)
{
fprintf(stderr, "Usage: %s port\n", argv[0]);
exit(EXIT_FAILURE);
}
memset(&hints, 0, sizeof(struct addrinfo));
hints.ai_family = AF_INET6; /* Allow IPv4 or IPv6 */
hints.ai_socktype = SOCK_DGRAM; /* Datagram socket */
hints.ai_flags = AI_PASSIVE; /* For wildcard IP address */
hints.ai_protocol = 0; /* Any protocol */
hints.ai_canonname = NULL;
hints.ai_addr = NULL;
hints.ai_next = NULL;
s = getaddrinfo(NULL, argv[1], &hints, &result);
if (s != 0)
{
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(s));
exit(EXIT_FAILURE);
}
printf("get addr info end \n");
for (rp = result; rp != NULL; rp = rp->ai_next)
{
printf("init socket\n");
sfd = socket(rp->ai_family, rp->ai_socktype,rp->ai_protocol);
printf("socket end\n");
if (sfd == -1)
continue;
if (bind(sfd, rp->ai_addr, rp->ai_addrlen) == 0)
break;
else
printf("bind err \n");
close(sfd);
}
if (rp == NULL)
{ /* No address succeeded */
fprintf(stderr, "Could not bind\n");
exit(EXIT_FAILURE);
}
freeaddrinfo(result); /* No longer needed */
for (;;)
{
peer_addr_len = sizeof(struct sockaddr_storage);
nread = recvfrom(sfd, buf, BUF_SIZE, 0,(struct sockaddr *) &peer_addr, &peer_addr_len);
if (nread == -1)
continue;
char host[NI_MAXHOST], service[NI_MAXSERV];
s = getnameinfo((struct sockaddr *) &peer_addr,peer_addr_len, host, NI_MAXHOST,service, NI_MAXSERV, NI_NUMERICSERV);
if (s == 0)
{
printf("Received %ld bytes from %s:%s\n sockaddr_len %d\n",(long) nread, host, service,peer_addr_len);
printf("msg is : %s \n",buf);
}
else
fprintf(stderr, "getnameinfo: %s\n", gai_strerror(s));
if (sendto(sfd, buf, nread, 0,(struct sockaddr *) &peer_addr,peer_addr_len) != nread)
fprintf(stderr, "Error sending response\n");
}
}
客户端:
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <net/if.h>
#define BUF_SIZE 500
int sv_sock_bind_device(int s, char * dev)
{
struct ifreq ifr;
strncpy(ifr.ifr_name,dev,IFNAMSIZ);
if (setsockopt(s, SOL_SOCKET, SO_BINDTODEVICE, (char*)&ifr, sizeof(struct ifreq)) != 0)
{
return -1;
}
return 0;
}
void printf_family(struct addrinfo * aip)
{
if(aip == NULL)
{
printf("prt NULL\n");
}
else
printf(" family ");
switch (aip->ai_family)
{
case AF_INET:
printf("ip_v4\n");
break;
case AF_INET6:
printf("ip_v6\n");
break;
default:
printf("unknown\n");
}
}
int main(int argc, char *argv[])
{
struct addrinfo hints;
struct addrinfo *result, *rp;
struct addrinfo *result_local, *rp_local;
int sfd, s, j;
int sfd_bak;
size_t len;
ssize_t nread;
char buf[BUF_SIZE];
char buf2[BUF_SIZE];
struct sockaddr scr_addr;
struct sockaddr_storage peer_addr;
socklen_t peer_addr_len;
peer_addr_len = sizeof(struct sockaddr_storage);
memset(&scr_addr, 0, sizeof(scr_addr));
char ip_addr[INET6_ADDRSTRLEN];
if (argc < 3)
{
fprintf(stderr, "Usage: %s host port msg...\n", argv[0]);
exit(EXIT_FAILURE);
}
sfd = socket(AF_INET6,SOCK_DGRAM,0);
memset(&hints, 0, sizeof(struct addrinfo));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_DGRAM;
hints.ai_flags = AI_PASSIVE;
hints.ai_protocol = 0;
s = getaddrinfo(argv[1], argv[2], &hints, &result);
if (s != 0)
{
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(s));
exit(EXIT_FAILURE);
}
for (rp = result; rp != NULL; rp = rp->ai_next)
{
printf_family(rp);
break;
}
//if(sv_sock_bind_device(sfd, "eth0")==-1)
//printf("bind err\n");
int ret = sendto(sfd, argv[3],strlen(argv[3]) , 0,rp->ai_addr,28) ;
if(ret != strlen(argv[3]))
{
printf("ret=%d len=%d error:%d %s\n", ret, strlen(argv[3]), errno, strerror(errno));
exit(EXIT_FAILURE);
}
int socklen = sizeof(struct sockaddr);
nread = recvfrom(sfd, buf2, BUF_SIZE, 0,(struct sockaddr *)&peer_addr, &peer_addr_len);
printf("Received msg back %s\n",buf2);
exit(EXIT_SUCCESS);
}
UDP协议下:单一socket复用 IPV4与IPV6地址
1服务器端
通常情况下,对于一个简单的UDP服务器,其工作流程如下:1.1初始化套接字:
Socketfd = socket(int domain,int type,int protocol)对于希望复用ipv4与ipv6地址的服务器来说,比较好的做法是将domain参数定义为AF_INET6。
1.2获取服务器地址信息:
获取与socket关联的地址,复用IPV4以及IPV6的套接字,应该调用下面函数进行地址信息的获取:getaddrinfo(char * host,char * server,struct addrinfo * hint,struct addrinfo ** result)
该函数将返回一个 struct addrinfo * 结构体指针链表,其表头地址储存于 struct addrinfo ** result 中。
下面简单介绍一下linux中struct addrinfo的实现
Struct addrinfo
{
int ai_flags;
int ai_family;
int ai_socktype;
int ai_protocol;
socklen_len ai_addrlen;
struct sockaddr * ai_addr;
char *ai_canonname;
struct addrinfo * ai_next;
}
该结构体各成员具体含义稍后介绍。
再来观察getaddrinfo()函数,其中参数 struct addrinfo * hint可以理解为一个用于过滤返回内容的参数。只有与hint属性匹配的addrinfo *才回被添加到返回的链表中。
回头来介绍一下struct addrinfo中各成员的含义,以及在调用getaddrinfo()函数时,hint中可以被赋值从而用于过滤的成员。
1.2.1 ai_flag选项
可以是零个或多个在一起的AI_XXX值,具体如下:AI_PASSIVE 套接字将用于被动打开(或监听绑定),通常配置于server端。
AI_CANONNAME 告知getaddrinfo()函数返回主机的规范名字。
AI_ADDRCONFIG 按照所在主机的配置选择返回地址类型。也就是说返回除本机回馈接口以外的网络接口配置的IP地址版本一致的IP地址。
1.2.2 ai_family 选项
AF_INET 指定返回IPV4地址信息;AF_INET6 指定返回IPV6地址信息;
AF_UNSPEC 未指定返回IP地址版本,也就是说符合hint过滤条件的所有地址信息都将返回。
1.2.3 ai_socktype 选项
SOCK_DGRAM 简单理解为UDPSOCK_STREAM 简单理解为TCP
1.2.4 ai_protocol 选项
该选项通常可由ai_family以及ai_socktype共同确定。以上4个成员为getaddrinfo()函数的调用者可以在hint中配置的选项。
值得注意的是,在通用IPV4与IPV6地址的服务器中,调用getaddrinfo()函数时,通常将主机地址设置为NULL,并且hint->ai_flag = AI_PASSIVE,hint->ai_family = AF_INET6,这样将会得到本机的通配地址,并且套接字用于被动接受数据。从而支持IPV4与IPV6地址的通用。
下面给出一个调用getaddrinfo()函数返回地址信息的简单用例
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <netinet/in.h>
void printf_family(struct addrinfo * aip)
{
……
}
void printf_type (struct addrinfo *aip)
{
……
}
void printf_protocol(struct addrinfo * aip)
{
……
}
void printf_flags(struct addrinfo * aip)
{
……
}
int main(int argc , char * argv[])
{
struct addrinfo *ailist, * aip;
struct addrinfo hint;
const char * addr;
int err;
char abuf_v4[INET_ADDRSTRLEN]; // IPV4
char abuf_v6[INET6_ADDRSTRLEN];//IPV6
hint.ai_flags = 0 ;
hint.ai_family = AF_UNSPEC;
hint.ai_socktype = SOCK_DGRAM;
hint.ai_protocol = 0;
hint.ai_addrlen = 0;
hint.ai_canonname = NULL;
hint.ai_addr = NULL;
hint.ai_next =NULL;
if((err = getaddrinfo(argv[1],argv[2],&hint,&ailist)) != 0)
{
printf("get addrinfo err \n %s\n",gai_strerror(err));
return 0;
}
for(aip = ailist; aip != NULL ; aip = aip->ai_next )
{
printf_flags(aip);
printf_family(aip);
printf_type(aip);
printf_protocol(aip);
printf("\n");
printf("%s",aip->ai_canonname);
printf("\n");
int sfd = socket(aip->ai_family, aip->ai_socktype,
aip->ai_protocol);
if (sfd == -1)
continue;
}
exit (0);
}
1.3将地址与套接字绑定
bind(int sockfd, struct sockaddr * addr, socklen_t addr_len)将上面配置的socket与getaddrinfo()函数返回的地址信息绑定。
1.4 接收数据包并向客户端返回消息
作为UDP连接,调用函数:recvfrom(int sockfd,,char * buf, int bufsize,int flag,struct sockaddr *peer_addr, socklen_t * len);
该函数接受客户端发送到本机地址的数据,并通过peer_addr,返回客户端地址,len保存该地址信息有效长度。
对于同时兼容IPV4以及IPV6地址的服务器,需要处理不同版本的IP地址,因此需要引用一个通用的结构体用来保存两种不同长度的地址信息,所以有比较介绍一下struct sockaddr_storage结构
不同于struct sockaddr,新的struct sockaddr_storage足以容纳系统所支持的任何套接字地址结构,sockaddr_storage结构在<netinet/in.h>头文件中定义
struct sockaddr_storage
{
uint8_t ss_len; /* length of this struct (implementation dependent) */
sa_family_t ss_family; /* address family: AF_xxx value */
/*其他字段对用户来说是透明的 故没有列出*/
};
sockaddr_storage和sockaddr的主要差别:
sockaddr_storage通用套接字地址结构满足对齐要求
sockaddr_storage通用套接字地址结构足够大,能够容纳系统支持的任何套接字地址结构。
上面使用recvfrom函数,通过struct sockaddr_storage * peer_addr返回了客户端地址信息,从而可以使用函数:
Sendto (int sockfd, void * buf, int size, int flags,struct sockaddr * peer_addr, socklen_t peer_addr_len)
向客户端返回消息。
经过以上流程,就可以完成一个简单的兼容IPV4、IPV6地址的UDP服务器。
2客户端
2.1初始化套接字
Socketfd = socket(int domain,int type,int protocol)与服务器端一样,对于希望复用ipv4与ipv6地址的用户,比较好的做法是将domain参数定义为AF_INET6。
2.2 获取服务器地址信息
getaddrinfo(char * server_ip,char * server_port,struct addrinfo * hint,struct addrinfo ** result)2.3将地址与套接字绑定
bind(int sockfd, struct sockaddr * addr, socklen_t addr_len)将上面配置的socket与getaddrinfo()函数返回的地址信息绑定。如果将套接字绑定到本机的一个特定IP(IPV4或IPV6地址),将无法实现IP地址的通用,为了解决这个问题,可将套接字绑定为本机的通配地址(0.0.0.0 or 0::0)。
或者,可以使用套接字选项中的SO_BINDTODEVICE选项,将套接字绑定至固定的网卡。
2.4 向服务器发送数据包并接收返回消息
在获取了地址信息后,客户端发送数据与服务器回复消息的行为相同,同样调用sendto函数向对端地址发送数据。客户端使用recvfrom函数接收服务器传回的消息。
至此,我们完成了一个可通用IPV4与IPV6地址的服务器---客户端模型。
下面是简单的代码,基本借用了man getaddrinfo中的用例。通过man getaddrinfo 可以得到更加详细的说明。
服务器端:
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/socket.h>
#include <netdb.h>
#define BUF_SIZE 500
#ifndef HOST_NAME_MAX
#define HOST_NAME_MAX 256
#endif
void printf_family(struct addrinfo * aip)
{
printf(" family ");
switch (aip->ai_family)
{
case AF_INET:
printf("ip_v4");
break;
case AF_INET6:
printf("ip_v6");
break;
default:
printf("unknown");
}
}
void printf_type (struct addrinfo *aip)
{
printf(" type ");
switch (aip->ai_socktype)
{
case SOCK_STREAM:
printf("stream");
break;
case SOCK_DGRAM:
printf("datagram");
break;
case SOCK_SEQPACKET:
printf("seqpacket");
break;
case SOCK_RAW:
printf("raw");
break;
default:
printf("unknown (%d)",aip->ai_socktype);
}
}
void printf_protocol(struct addrinfo * aip)
{
printf(" protocol ");
switch (aip->ai_protocol)
{
case 0:
printf("default");
break;
case IPPROTO_TCP:
printf("TCP");
break;
case IPPROTO_UDP:
printf("UDP");
break;
case IPPROTO_RAW:
printf("RAW");
break;
default:
printf("unknown (%d)",aip->ai_protocol);
}
}
void printf_flags(struct addrinfo * aip)
{
printf(" flags ");
if(aip->ai_flags == 0)
printf(" 0 ");
else
{
if (aip->ai_flags & AI_PASSIVE)
{
printf("
baa7
passive");
return;
}
if (aip->ai_flags & AI_CANONNAME)
{
printf(" canon");
}
else
{
printf(" unknown");
}
}
}
//________________________________________//
int main(int argc, char *argv[])
{
struct addrinfo hints;
struct addrinfo *result, *rp;
int sfd, s;
struct sockaddr_storage peer_addr;
socklen_t peer_addr_len;
ssize_t nread;
char buf[BUF_SIZE];
if (argc != 2)
{
fprintf(stderr, "Usage: %s port\n", argv[0]);
exit(EXIT_FAILURE);
}
memset(&hints, 0, sizeof(struct addrinfo));
hints.ai_family = AF_INET6; /* Allow IPv4 or IPv6 */
hints.ai_socktype = SOCK_DGRAM; /* Datagram socket */
hints.ai_flags = AI_PASSIVE; /* For wildcard IP address */
hints.ai_protocol = 0; /* Any protocol */
hints.ai_canonname = NULL;
hints.ai_addr = NULL;
hints.ai_next = NULL;
s = getaddrinfo(NULL, argv[1], &hints, &result);
if (s != 0)
{
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(s));
exit(EXIT_FAILURE);
}
printf("get addr info end \n");
for (rp = result; rp != NULL; rp = rp->ai_next)
{
printf("init socket\n");
sfd = socket(rp->ai_family, rp->ai_socktype,rp->ai_protocol);
printf("socket end\n");
if (sfd == -1)
continue;
if (bind(sfd, rp->ai_addr, rp->ai_addrlen) == 0)
break;
else
printf("bind err \n");
close(sfd);
}
if (rp == NULL)
{ /* No address succeeded */
fprintf(stderr, "Could not bind\n");
exit(EXIT_FAILURE);
}
freeaddrinfo(result); /* No longer needed */
for (;;)
{
peer_addr_len = sizeof(struct sockaddr_storage);
nread = recvfrom(sfd, buf, BUF_SIZE, 0,(struct sockaddr *) &peer_addr, &peer_addr_len);
if (nread == -1)
continue;
char host[NI_MAXHOST], service[NI_MAXSERV];
s = getnameinfo((struct sockaddr *) &peer_addr,peer_addr_len, host, NI_MAXHOST,service, NI_MAXSERV, NI_NUMERICSERV);
if (s == 0)
{
printf("Received %ld bytes from %s:%s\n sockaddr_len %d\n",(long) nread, host, service,peer_addr_len);
printf("msg is : %s \n",buf);
}
else
fprintf(stderr, "getnameinfo: %s\n", gai_strerror(s));
if (sendto(sfd, buf, nread, 0,(struct sockaddr *) &peer_addr,peer_addr_len) != nread)
fprintf(stderr, "Error sending response\n");
}
}
客户端:
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <net/if.h>
#define BUF_SIZE 500
int sv_sock_bind_device(int s, char * dev)
{
struct ifreq ifr;
strncpy(ifr.ifr_name,dev,IFNAMSIZ);
if (setsockopt(s, SOL_SOCKET, SO_BINDTODEVICE, (char*)&ifr, sizeof(struct ifreq)) != 0)
{
return -1;
}
return 0;
}
void printf_family(struct addrinfo * aip)
{
if(aip == NULL)
{
printf("prt NULL\n");
}
else
printf(" family ");
switch (aip->ai_family)
{
case AF_INET:
printf("ip_v4\n");
break;
case AF_INET6:
printf("ip_v6\n");
break;
default:
printf("unknown\n");
}
}
int main(int argc, char *argv[])
{
struct addrinfo hints;
struct addrinfo *result, *rp;
struct addrinfo *result_local, *rp_local;
int sfd, s, j;
int sfd_bak;
size_t len;
ssize_t nread;
char buf[BUF_SIZE];
char buf2[BUF_SIZE];
struct sockaddr scr_addr;
struct sockaddr_storage peer_addr;
socklen_t peer_addr_len;
peer_addr_len = sizeof(struct sockaddr_storage);
memset(&scr_addr, 0, sizeof(scr_addr));
char ip_addr[INET6_ADDRSTRLEN];
if (argc < 3)
{
fprintf(stderr, "Usage: %s host port msg...\n", argv[0]);
exit(EXIT_FAILURE);
}
sfd = socket(AF_INET6,SOCK_DGRAM,0);
memset(&hints, 0, sizeof(struct addrinfo));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_DGRAM;
hints.ai_flags = AI_PASSIVE;
hints.ai_protocol = 0;
s = getaddrinfo(argv[1], argv[2], &hints, &result);
if (s != 0)
{
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(s));
exit(EXIT_FAILURE);
}
for (rp = result; rp != NULL; rp = rp->ai_next)
{
printf_family(rp);
break;
}
//if(sv_sock_bind_device(sfd, "eth0")==-1)
//printf("bind err\n");
int ret = sendto(sfd, argv[3],strlen(argv[3]) , 0,rp->ai_addr,28) ;
if(ret != strlen(argv[3]))
{
printf("ret=%d len=%d error:%d %s\n", ret, strlen(argv[3]), errno, strerror(errno));
exit(EXIT_FAILURE);
}
int socklen = sizeof(struct sockaddr);
nread = recvfrom(sfd, buf2, BUF_SIZE, 0,(struct sockaddr *)&peer_addr, &peer_addr_len);
printf("Received msg back %s\n",buf2);
exit(EXIT_SUCCESS);
}
相关文章推荐
- UDP协议发包的使用(DatagramSocket、DatagramPacket)
- 常见通信协议区别——tcp、udp、rpc、http、socket
- 通过socket和Udp协议简单实现一个群体聊天工具(控制台)
- Linux C raw socket 发送ipv4下的简单udp数据包
- 通过套接字(socket)和UDP协议实现网络通信
- 26 API-网络编程(网络概述,Socket通信机制,UDP协议发送和接收数据,TCP协议发送和接收数据)
- Linux下的socket协议UDP Client/Server程序
- 实现UDP协议,socket编程,调用到windowsAPI,实现客户端和服务器
- Android Socket网络编程 UDP协议
- 各协议(TCP/IP、UDP、HTTP、socket)内与外简述
- java中网络编程------UDP协议(实现步骤)DatagramSocket对象
- iOS- 移动端Socket UDP协议广播机制的实现
- 14.基于UDP协议的socket编程(1)
- 基于UDP协议之——socket编程
- Socket通信原理,TCP,UDP协议
- 关于网络传输协议的介绍(TCP、UDP、IP、Http、Socket)
- 常见通信协议区别——tcp、udp、rpc、http、socket
- iOS- 移动端Socket UDP协议广播机制的实现
- linux udp协议服务器,客户端socket使用
- 基于UDP协议的网络编程(使用DatagramSocket发送接收数据)