UDP通讯测试

时间:2024-03-17 22:03:10

参考资料:UNIX网络编程

实验平台:PC为client,RaspberryPi为server

基本类型和接口函数,参考man手册

#include <sys/socket.h>

struct sockaddr {
    sa_family_t     sa_family;      /* Address family */
    char            sa_data[];      /* Socket address */
};

#include <netinet/in.h>

struct sockaddr_in {
    sa_family_t     sin_family;     /* AF_INET */
    in_port_t       sin_port;       /* Port number */
    struct in_addr  sin_addr;       /* IPv4 address */
};

struct in_addr {
    in_addr_t s_addr;
};

typedef uint32_t in_addr_t;
typedef uint16_t in_port_t;


#include <sys/socket.h>
ssize_t sendto(int sockfd, const void buf[.len], size_t len, int flags,
                const struct sockaddr *dest_addr,
                socklen_t addrlen);

ssize_t recvfrom(int sockfd, void buf[restrict .len], size_t len, int flags,
                struct sockaddr *src_addr,
                socklen_t *addrlen);

    
#include <arpa/inet.h>
int inet_pton(int af, const char *restrict src, void *restrict dst);

const char *inet_ntop(int af, const void *restrict src, char dst[restrict .size], socklen_t size);




#include <arpa/inet.h>

int inet_pton(int af, const char *restrict src, void *restrict dst);

宏定义以及辅助类型信息

#define SERV_PORT 9877 /* TCP and UDP */
#define CHAR_ADDR_LEN   (16U)


#if fasle
siginfo_t {
    int      si_signo;     /* Signal number */
    int      si_errno;     /* An errno value */
    int      si_code;      /* Signal code */
    int      si_trapno;    /* Trap number that caused
                                hardware-generated signal
                                (unused on most architectures) */
    pid_t    si_pid;       /* Sending process ID */
    uid_t    si_uid;       /* Real user ID of sending process */
    int      si_status;    /* Exit value or signal */
    clock_t  si_utime;     /* User time consumed */
    clock_t  si_stime;     /* System time consumed */
    union sigval si_value; /* Signal value */
    int      si_int;       /* POSIX.1b signal */
    void    *si_ptr;       /* POSIX.1b signal */
    int      si_overrun;   /* Timer overrun count;
                                POSIX.1b timers */
    int      si_timerid;   /* Timer ID; POSIX.1b timers */
    void    *si_addr;      /* Memory location which caused fault */
    long     si_band;      /* Band event (was int in
                                glibc 2.3.2 and earlier) */
    int      si_fd;        /* File descriptor */
    short    si_addr_lsb;  /* Least significant bit of address
                                (since Linux 2.6.32) */
    void    *si_lower;     /* Lower bound when address violation
                                occurred (since Linux 3.19) */
    void    *si_upper;     /* Upper bound when address violation
                                occurred (since Linux 3.19) */
    int      si_pkey;      /* Protection key on PTE that caused
                                fault (since Linux 4.6) */
    void    *si_call_addr; /* Address of system call instruction
                                (since Linux 3.5) */
    int      si_syscall;   /* Number of attempted system call
                                (since Linux 3.5) */
    unsigned int si_arch;  /* Architecture of attempted system call
                                (since Linux 3.5) */
}

struct sigaction {
    void     (*sa_handler)(int);
    void     (*sa_sigaction)(int, siginfo_t *, void *);
    sigset_t   sa_mask;
    int        sa_flags;
    void     (*sa_restorer)(void);
};

typedef struct ucontext_t {
    struct ucontext_t *uc_link;
    sigset_t          uc_sigmask;
    stack_t           uc_stack;
    mcontext_t        uc_mcontext;
    ...
} ucontext_t;

#endif

服务器端测试代码(增加信号操作示例)

void handle_sig_action(int sig, siginfo_t *info, void *ctx)
{
    struct ucontext_t *p_context = (struct ucontext_t *)ctx;

    printf("%d----%d\n", info->si_pid, sigismember(&p_context->uc_sigmask, SIGINT));

    printf("Sig recv: %d\n", sig);
    exit(EXIT_SUCCESS);
}

void dg_echo(int sockfd, struct sockaddr *p_cli_addr, socklen_t addr_len)
{
    int  n;

    char  mesg[MAXLINE];
    char addr[CHAR_ADDR_LEN];

    struct sigaction act;
    act.sa_flags = SA_SIGINFO;
    act.sa_sigaction = handle_sig_action;

    sigemptyset(&act.sa_mask);
    if(-1 == sigaction(SIGINT, &act, NULL))
    {
        perror("sigaction() Error");
    }

    for ( ; ; )
    {
        n = recvfrom(sockfd, mesg, MAXLINE, 0, p_cli_addr, &addr_len);

        if(-1 == n)
        {
            perror("recvfrom() Error");
        }
        else
        {
            if(NULL == inet_ntop(AF_INET, &((struct sockaddr_in *)p_cli_addr)->sin_addr, addr, CHAR_ADDR_LEN))
            {
                perror("inet_ntop() Error");
            }
            else
            {
                printf("Received %d Bytes : %s From client %s\n", n, mesg, addr);
            }
        }

        n = sendto(sockfd, mesg, n, 0, p_cli_addr, addr_len);
        if(-1 == n)
        {
            perror("sendto() Error");
        }
        else
        {
            printf("Now send back %d Bytes!\n", n);
            fflush(stdout);
        }
    }
}

void server_main(void)
{
    int sockfd;

    struct sockaddr_in  serv_addr, cli_addr;

    /* IPPROTO_IP = 0 */      /* Dummy protocol for TCP.  */
    sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);

    if(sockfd < 0)
    {
        perror("socket() fail");
    }
    else
    {
        memset(&serv_addr, 0, sizeof(serv_addr));

        serv_addr.sin_family = AF_INET;

        /* /usr/include/netinet/in.h */
        /* Address to accept any incoming messages.  */
        /* #define INADDR_ANY   ((in_addr_t) 0x00000000) */
        serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
        //inet_pton(AF_INET, "192.168.39.39", &servaddr.sin_addr);
        serv_addr.sin_port = htons(SERV_PORT);

        if(0 != bind(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)))
        {
            perror("bind() Error");
        }
        else
        {
            dg_echo(sockfd, (struct sockaddr *)&cli_addr, sizeof(cli_addr));
        }
    }
}

客户端测试代码(增加信号操作示例)

void dg_cli(FILE *fp, int sockfd, const struct sockaddr *p_serv_addr, socklen_t addr_len)
{
    int n;
    socklen_t len;

    struct sockaddr_in reply_addr;
    char sendline[MAXLINE];
    char recv_buf[MAXLINE + 1];
    char recv_addr[CHAR_ADDR_LEN];

    while (fgets(sendline, MAXLINE, fp) != NULL)
    {
        n = sendto(sockfd, sendline, strlen(sendline), 0, p_serv_addr, addr_len);
        if(-1 == n)
        {
            perror("sendto() Error");
        }
        else
        {
            printf("send %d bytes successfully!\n", n);
            fflush(stdout);
        }

        n = recvfrom(sockfd, recv_buf, MAXLINE, 0, (struct sockaddr *)&reply_addr, &len);

        if(-1 == n)
        {
            perror("recvfrom() Error");
        }
        else
        {
            printf("Received %d Bytessuccess!\n", n);
            fflush(stdout);

            if(NULL == inet_ntop(AF_INET, &reply_addr.sin_addr, recv_addr, CHAR_ADDR_LEN))
            {
                perror("inet_ntop()");
            }

            if((len != addr_len) || (memcmp(p_serv_addr, (struct sockaddr *)&reply_addr, len) != 0))
            {
                printf("Replied from %s ignored!\n", recv_addr);
                continue;
            }
            else
            {
                printf("Replied from %s with:\n", recv_addr);
            }

            recv_buf[n] = '\0';   /* null terminate */
            fputs(recv_buf, stdout);
        }
    }
}


/*________________________________________________________________________________________________*/
/*                                                                                                */
/*________________________________________________________________________________________________*/
static void sock_ntop(const struct sockaddr_in *p_addr, char *char_pres, int len)
{
    char port_str[8];

    if(p_addr->sin_family == AF_INET)
    {
        if(inet_ntop(AF_INET, &(p_addr->sin_addr), char_pres, len) == NULL)
        {
            perror("inet_ntop()");
        }
        else
        {
            if(ntohs(p_addr->sin_port) != 0)
            {
                snprintf(port_str, sizeof(port_str), ":%u", ntohs(p_addr->sin_port));
                strcat(char_pres, port_str);
            }
        }
    }
    else
    {
        printf("sa_family not match!\n");
    }
}

/*________________________________________________________________________________________________*/
/*                                                                                                */
/*________________________________________________________________________________________________*/
/*-------------------------------------------------------------
#include <signal.h>
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);

issta@SuperHard:~$ kill -l
 1) SIGHUP	 2) SIGINT	 3) SIGQUIT	 4) SIGILL	 5) SIGTRAP
 6) SIGABRT	 7) SIGBUS	 8) SIGFPE	 9) SIGKILL	10) SIGUSR1
11) SIGSEGV	12) SIGUSR2	13) SIGPIPE	14) SIGALRM	15) SIGTERM
16) SIGSTKFLT	17) SIGCHLD	18) SIGCONT	19) SIGSTOP	20) SIGTSTP
21) SIGTTIN	22) SIGTTOU	23) SIGURG	24) SIGXCPU	25) SIGXFSZ
26) SIGVTALRM	27) SIGPROF	28) SIGWINCH	29) SIGIO	30) SIGPWR
31) SIGSYS	34) SIGRTMIN	35) SIGRTMIN+1	36) SIGRTMIN+2	37) SIGRTMIN+3
38) SIGRTMIN+4	39) SIGRTMIN+5	40) SIGRTMIN+6	41) SIGRTMIN+7	42) SIGRTMIN+8
43) SIGRTMIN+9	44) SIGRTMIN+10	45) SIGRTMIN+11	46) SIGRTMIN+12	47) SIGRTMIN+13
48) SIGRTMIN+14	49) SIGRTMIN+15	50) SIGRTMAX-14	51) SIGRTMAX-13	52) SIGRTMAX-12
53) SIGRTMAX-11	54) SIGRTMAX-10	55) SIGRTMAX-9	56) SIGRTMAX-8	57) SIGRTMAX-7
58) SIGRTMAX-6	59) SIGRTMAX-5	60) SIGRTMAX-4	61) SIGRTMAX-3	62) SIGRTMAX-2
63) SIGRTMAX-1	64) SIGRTMAX	

--------------------------------------------------------------*/

static void exit_client(int sign_num)
{
    printf("Force Exit By Received %d!\n", sign_num);
}


void cli_conn(FILE *fp, int sockfd, const struct sockaddr *p_serv_addr, socklen_t addr_len)
{
    int n;
    struct sockaddr_in cli_addr;

    char sendline[MAXLINE];
    char recv_buf[MAXLINE + 1];
    char addr_inf[24];

    /* If the connection or binding succeeds, zero is returned.
        On error, -1 is returned, and errno is set to indicate the error. */
    if(-1 == connect(sockfd, p_serv_addr, addr_len))
    {
        perror("connect() error");
    }
    else
    {
        /* The addrlen argument of getsockname() should be initialized to indicate the amount of space */
        socklen_t len =sizeof(cli_addr);
        
        if(-1 == getsockname(sockfd, (struct sockaddr *)&cli_addr, &len))
        {
            perror("getsockname() error");
        }
        else
        {
            printf("%u\n", cli_addr.sin_family);
            printf("%u\n", ntohs(cli_addr.sin_port));
        }

        sock_ntop(&cli_addr, addr_inf, 24);
        printf("Local Address %s\n", addr_inf);

        struct sigaction action;
        sigemptyset(&action.sa_mask);
        action.sa_flags = 0;
        action.sa_handler = exit_client;

        if(-1 == sigaction(SIGINT, &action, NULL))  /* signal(SIGINT, exit_client)  */
        {
            perror("sigaction() Error");
        }

        while (fgets(sendline, MAXLINE, fp) != NULL)
        {
            n = write(sockfd, sendline, strlen(sendline));

            if(n == -1)
            {
                perror("write() error");
            }
            else
            {
                printf("Send %d Bytes successfully!\n", n);
                fflush(stdout);

                n = read(sockfd, recv_buf, MAXLINE);
                if(n == -1)
                {
                    perror("read() error");
                }
                else
                {
                    printf("Receive %d Bytes success!\n", n);
                    fflush(stdout);

                    recv_buf[n] = '\0';   /* null terminate */
                    fputs(recv_buf, stdout);
                }
            }
        }
    }
}

/*________________________________________________________________________________________________*/
/*                                                                                                */
/*________________________________________________________________________________________________*/
void client_main(const char *src)
{
    int rult;
    int  sockfd;
    struct sockaddr_in serv_addr;

    memset(&serv_addr, 0, sizeof(serv_addr));

    serv_addr.sin_family = AF_INET;
    serv_addr.sin_port = htons(SERV_PORT);

    /* printf("serv_addr.sin_port = %u\n", serv_addr.sin_port);  */
    /* printf("%u ---<--- %u\n", ntohs(serv_addr.sin_port), SERV_PORT); */

    rult = inet_pton(AF_INET, src, &serv_addr.sin_addr);

    if(rult == 0)
    {
        fprintf(stderr, "src does not contain a character string representing a valid network address\n");
    }
    else if(rult == -1)
    {
        perror("inet_pton() error");
    }
    else
    {
        sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);

        if(sockfd < 0)
            perror("socket");
        else
        {
            /* printf("socket len: %ld\n", sizeof(serv_addr)); */
            printf("sin_port = %u  (%u)\n", serv_addr.sin_port, ntohs(serv_addr.sin_port));

            /* dg_cli(stdin, sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)); */
            cli_conn(stdin, sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr));
        }
    }
}

调用测试用例

int main(int argc, char *argv[])
{
    if(argc < 2)
    {
        fprintf(stderr, "Useg: %s <Parameter>\n", argv[0]);
        exit(EXIT_FAILURE);
    }
    else
    {
        printf("User Input Parameter: %s\n", argv[1]);
    }

    int opt;

    while ((opt = getopt(argc, argv, "udp")) != -1)
    {
        switch(opt)
        {
            case 'u':
                //client_main("192.168.39.39");
                client_main("192.168.252.1");
                //client_main("127.0.0.1");
                //client_main("192.168.39.33");
                break;
            case 'd':
                break;
            case 'p':
                break;
            default :
                break;
        }
    }

    exit(EXIT_SUCCESS);
}

测试结果

客户端:

服务器:

补充知识:

程序员不应操作sockaddr结构,sockaddr是给操作系统用的
程序员应使用sockaddr_in来表示地址,sockaddr_in区分了地址和端口,使用更方便。