C/C++:UDP IPv4 IPv6 客户端 & 服务端 简例

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 消息服务端收不到。

猜你喜欢

转载自blog.csdn.net/test1280/article/details/81515271
今日推荐