C/C++:UDP IPv4 IPv6 客户端 & 服务端 简例
客户端:
/***********************************************************
*
* Filename : client.c
* Last Revision : Revision: 1.0
* Last Date : Date: 2018/08/08
* Author : Jiang
* Description : udp sender
*
***********************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <errno.h>
/*--------------------------------------------------------------
* 函数名称:work6
* 功能描述:创建IPV6套接字并发送UDP消息
* 参数说明:
* 返 回 值:
* 备 注:失败退出进程
*/
void work6(const char *host, int port)
{
struct sockaddr_in6 server6_addr;
memset(&server6_addr, 0, sizeof(server6_addr));
server6_addr.sin6_family = AF_INET6;
server6_addr.sin6_port = htons(port);
if (inet_pton(AF_INET6, host, &server6_addr.sin6_addr) <= 0) {
fprintf(stderr, "inet_pton error: %d(%s)", errno, strerror(errno));
exit(1);
}
int fd = socket(AF_INET6, SOCK_DGRAM, 0);
if (fd < 0) {
fprintf(stderr, "create socket failed: %s\n", strerror(errno));
exit(1);
}
fprintf(stdout, "create IPV6 socket OK\n");
const char *udpMsg = "Hi, test1280! [from ipv6]";
sendto(fd, udpMsg, strlen(udpMsg), 0, (struct sockaddr*)&server6_addr, sizeof(server6_addr));
}
/*--------------------------------------------------------------
* 函数名称:work
* 功能描述:创建IPV4套接字并发送UDP消息
* 参数说明:
* 返 回 值:
* 备 注:失败退出进程
*/
void work(const char *host, int port)
{
struct sockaddr_in server_addr;
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(port);
server_addr.sin_addr.s_addr = inet_addr(host);
int fd = socket(AF_INET, SOCK_DGRAM, 0);
if (fd < 0) {
fprintf(stderr, "create socket failed: %s\n", strerror(errno));
exit(1);
}
fprintf(stdout, "create IPV4 socket OK\n");
const char *udpMsg = "Hi, test1280! [from ipv4]";
sendto(fd, udpMsg, strlen(udpMsg), 0, (struct sockaddr*)&server_addr, sizeof(server_addr));
}
int main(const char *host, int port)
{
#ifdef _UDP_IPV6
work6("::1", 4020);
#else
work("127.0.0.1", 4020);
#endif
return 0;
}
服务端:
/***********************************************************
*
* Filename : server.c
* Last Revision : Revision: 1.0
* Last Date : Date: 2018/08/08
* Author : Jiang
* Description : udp receiver
*
***********************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <errno.h>
/*--------------------------------------------------------------
* 函数名称:createSocket
* 功能描述:创建数据报套接字(server)
* 参数说明:
* 返 回 值:数据报套接字文件描述符
* 备 注:失败退出进程
*/
int createSocket()
{
struct addrinfo hints;
memset(&hints, 0, sizeof(hints));
hints.ai_flags = AI_PASSIVE;
hints.ai_socktype = SOCK_DGRAM;
#ifdef _UDP_IPV6
hints.ai_family = AF_INET6;
#else
hints.ai_family = AF_INET;
#endif
struct addrinfo *res;
int e = getaddrinfo(NULL, "4020", &hints, &res);
if (e != 0) {
fprintf(stderr, "server: ERROR: getaddrinfo failed\n");
exit(1);
}
int fd = -1;
struct addrinfo *cur = res;
while (cur != NULL) {
fd = socket(cur->ai_family, cur->ai_socktype, cur->ai_protocol);
if (fd >= 0) {
#ifdef _UDP_IPV6
fprintf(stdout, "server: INFO : create IPV6 socket OK\n");
#else
fprintf(stdout, "server: INFO : create IPV4 socket OK\n");
#endif
break;
}
cur = cur->ai_next;
}
// 创建套接字失败
if (fd < 0) {
freeaddrinfo(res);
fprintf(stderr, "server: ERROR: create udp socket failed\n");
exit(1);
}
if (bind(fd, (struct sockaddr *)cur->ai_addr, cur->ai_addrlen) < 0) {
freeaddrinfo(res);
fprintf(stderr, "server: ERROR: udp socket bind failed\n");
exit(1);
}
freeaddrinfo(res);
fprintf(stdout, "server: INFO : ini udp server OK\n");
return fd;
}
/*--------------------------------------------------------------
* 函数名称:iniUdpSvr
* 功能描述:初始化数据报服务
* 参数说明:
* 返 回 值:数据报套接字文件描述符
* 备 注:失败退出进程
*/
int iniUdpSvr()
{
return createSocket();
}
/*--------------------------------------------------------------
* 函数名称:dealUdp
* 功能描述:处理数据报消息
* 参数说明:
* 返 回 值:
* 备 注:错误退出进程
*/
void dealUdp(int fd)
{
struct timeval tv;
tv.tv_sec = 0;
tv.tv_usec = 20000;
fd_set fds;
FD_ZERO(&fds);
FD_SET(fd, &fds);
int readyNum = select(fd+1, &fds, NULL, NULL, &tv);
if (readyNum < 0) { // 错误、信号中断
if (errno == EINTR) {
fprintf(stdout, "server: WARN : select RET EINTR\n");
return;
}
else {
fprintf(stderr, "server: ERROR: select RET %d (%s)\n", errno, strerror(errno));
exit(1);
}
}
else if (readyNum == 0) { // 超时
return;
}
else // 活动文件描述符
;
static char udpBuf[1024*1024];
memset(udpBuf, 0, sizeof(udpBuf));
int rbytes = rbytes = read(fd, udpBuf, sizeof(udpBuf));
if (rbytes <= 0) {
fprintf(stderr, "server: ERROR: read failed: RET %d (%s)\n", errno, strerror(errno));
exit(1);
}
fprintf(stdout, "server: INFO : recv msg: %s\n", udpBuf);
}
/*--------------------------------------------------------------
* 函数名称:work
* 功能描述:工作函数(主线程主循环)
* 参数说明:
* 返 回 值:
* 备 注:错误退出进程
*/
void work()
{
int fd = iniUdpSvr();
while (1) {
dealUdp(fd);
}
}
int main()
{
work();
return 0;
}
测试:
1)IPv4服务端套接字,IPv4客户端套接字:
编译服务端:
$ gcc -o server server.c
编译客户端:
$ gcc -o client client.c
运行服务端:
$ ./server
server: INFO : create IPV4 socket OK
server: INFO : ini udp server OK
server: INFO : recv msg: Hi, test1280! [from ipv4]
^C
运行客户端:
$ ./client
create IPV4 socket OK
2)IPv6服务端套接字,IPv4客户端套接字:
编译服务端:
$ gcc -o server server.c -D_UDP_IPV6
编译客户端:
$ gcc -o client client.c
运行服务端:
$ ./server
server: INFO : create IPV6 socket OK
server: INFO : ini udp server OK
server: INFO : recv msg: Hi, test1280! [from ipv4]
^C
运行客户端:
$ ./client
create IPV4 socket OK
3)IPv6服务端套接字,IPv6客户端套接字:
编译服务端:
$ gcc -o server server.c -D_UDP_IPV6
编译客户端:
$ gcc -o client client.c -D_UDP_IPV6
运行服务端:
$ ./server
server: INFO : create IPV6 socket OK
server: INFO : ini udp server OK
server: INFO : recv msg: Hi, test1280! [from ipv6]
^C
运行客户端:
$ ./client
create IPV6 socket OK
4)IPv4服务端套接字,IPv6客户端套接字:
编译服务端:
$ gcc -o server server.c
编译客户端:
$ gcc -o client client.c -D_UDP_IPV6
运行服务端:
$ ./server
server: INFO : create IPV4 socket OK
server: INFO : ini udp server OK
^C
注意:服务端并未收到任何UDP消息!
运行客户端:
$ ./client
create IPV6 socket OK
Tips:
服务端套接字为 IPV6 时,客户端套接字可以是 IPV4,也可以是 IPV6,与服务端通信。
服务端套接字为 IPV4 时,客户端套接字仅可为 IPV4,不可以是 IPV6。若为 IPV6,则发送 UDP 消息服务端收不到。